2021年7月25日 星期日

Git 版本管控(二)-Git的環境建置

延續上次的Git 版本管控(一)-淺談版本管控,這次則是要說明如何建置Git的環境,廢話不多說直接往下看~

步驟1:搜尋Git官網,並點選Download for windows,版本可能會有些差異,但使用上不會差太多


Git有提供安裝版本免安裝版本,預設會幫你下載安裝版本,如果你不喜歡則可改下載其他版本,我自己是喜歡免安裝版本


再來就是下載GUI,我自己偏向使用Git Extensions(一樣有免安裝版本),後續也是用此作教學說明

步驟2:下載KDiff3(下載連結),而KDiff3主要可幫助我們做程式碼比對,使用上很方便


步驟3:確認下載與安裝Git、Git Extensions、KDiff3完成後,就能開始操作GUI,而使用前要先將Git與KDiff3路徑設定完才能使用!!

初次開啟Git Extensions會先出現須設定的CheckList,如下圖所示,要針對橘色部分做修復


先修復第一個項目(Git路徑設定),如下圖所示,設定完可從左方的Git Extensions回到剛剛的CheckList,並可發現第一與最後一個項目都轉成綠色


再來就是修復二、三、四項目(設定KDiff3與個人資訊),這三項剛好都在同一個頁面就能完成,最主要是KDiff3路徑一定要設定,個人資訊其實沒設定也能使用


當問題都修復完成,就會看到全綠色的狀態,如下圖所示


最後點選ok,就會進入Git Extensions主頁面,代表可以正式使用Git 做版本管控瞜!






2021年6月12日 星期六

Line 跨系統聊天紀錄轉移

今年四月的時候,因手上安卓手機已經用了快五年,感覺已經不能再撐下去了,因此萌生換手機的念頭,而自己對手機的要求也不高,用起來不要卡卡就可以,然後身邊朋友總說蘋果手機用久都不太卡頓,所以這次我決定要來跳槽,剛好親戚有不要的二手iPhone 7 plus 128G,因而我就大膽的買下去!!

當然,拿到新手機很開心,但我馬上遇到一個問題,就是遇到跨系統時,Line的聊天紀錄似乎沒這麼好處理,所以我詢問一些有跳槽經驗的朋友,同時也上網查一些資料,得到的結果大概就兩種,第一種是官方說法,正常無法轉移聊天紀錄,所以蠻多人直接放棄轉移,若有聊天紀錄需要,則會從電腦版的Line去查找;第二種就是訪間有些付費軟體可以協助轉移,但過程也小複雜....後來我想想,其實也沒有非得保留舊聊天紀錄,所以決定放棄轉移了,直接另起爐灶。

但,當我將Line從安卓轉到蘋果時,我依照正常流程輸入手機號碼,且事前沒有備份到iCloud雲蝶(廢話),所以我選擇不復原聊天紀錄,常理來說進入到主畫面時,應該要是全空的狀態,但.....神奇的事情發生了,我點開我的蘋果 Line,竟發現有聊 ! 天 ! 紀 ! 錄 !    突然覺得大腦充滿困惑,我到目前也完全不知道怎做到的,可能是老天賞賜給我的奇蹟吧!

事後想想,唯一的可能性,起先因為app剩Line還沒完全轉移,所以有一段時間我是安桌與蘋果手機同時使用,因為比較常使用Line,所以我幾乎都用安卓,然後分享網路給蘋果,聽起有點搞笑,哈哈哈,但這可能是個關鍵!!猜想會不會因為我在安卓分享網路給蘋果狀態下,再進行Line的轉移動作,間接也將聊天內容轉到蘋果呢?但過了一段時間後,原先以為全都轉移的聊天紀錄,其實只將二月到四月這範圍的聊天紀錄轉移過來,去年甚至更久以前的就都沒有轉過來,但至少有總比沒有好!!

最後,這個方法我並沒有再回去測試一遍,因為怕到時就真的全空了!但對於某些人如果跟我一樣,原先想放棄舊聊天紀錄,若這個方式可以幫你/妳保留近兩個月的聊天內容,其實我覺得還不錯,不會出現空白聊天視窗,然後不知怎麼接的感覺,整體來說我覺得是可以嘗試看看,萬一沒有成功,其實心裡也不會太難過,但如果成功了,我覺得心裡應該會蠻爽的!!!!!

僅供參考瞜~

2021年5月24日 星期一

心情札記-2021.05.24

今天窩了一整天的宿舍,內心被程式與高溫弄得有點心煩,趁著傍晚天氣較涼到附近公園溜搭,走著走著,竟傳來久違的夏天氣息,那就是在樹上高歌的蟬聲,如此熱鬧般的蟬聲,對比人行道空冷的寂靜,顯得格外諷刺,看來這個夏天依舊不寧靜,願這疫情能早點結束,讓生活回到從前般的開心!!

2021年3月30日 星期二

Git 版本管控(一)-淺談版本管控

最近,部門說要導入這個,然後由我做教學?!?!,既然都這樣安排了,就來整理自己目前對Git的了解,預計分享的內容如下:
  1. 淺談版本管控
  2. Git的環境建置
  3. Git本機操作
  4. Git遠端操作
淺談版本管控(Version Control)
首先,為什麼要使用版本管控(Version Control)呢?說說自己以前在研究所時期,剛開始寫C/C++跟OpenCV時,往往就是修改了一下程式或寫一些功能之後,為了避免後續程式碼可能被改到不能用,就會加上時間做區隔,類似做備份使用,但久而久之備份的內容越來越多,有時想還原回正常版本卻忘記不知哪邊被改動,因為距離上次修改太久了,所以只能細部查找差異性,間接花了很多時間在做非功能性開發,這個狀況持續到我工作接觸到Git才有所改變,此時發現原來寫軟體除了邏輯要夠用之外,其實蠻多小工具也要學會,因為正式的專案其架構都蠻大的,所以工作上會切分成寫人機介面(前端)演算法(後端)而某方寫完功能後一定會做測試,後續並做整合測試等等,因此若用改時間或註解紀錄程式碼修改差異,其效率或除錯速度都不好,所以才會出現版本管控(Version Control)這個概念。

更進一步的說,寫程式除了功能寫完之外,一般還會有整合、測試、進版等等,所以不是單單寫完程式就沒事!!畢竟如果寫出一堆Bug給別人就尷尬了,或是自己未來要除錯也不容易,總結來說版本管控(Version Control)就是用來記錄程式碼每次更改的差異,並能協助我們紀錄特定版本,若出現某版本因修改後而不穩定時,還能還原先前穩定版本,同時能找到是誰偷放這個臭蟲,哈哈哈哈,然後發現原來是自己搞的鬼!

版本管控系統(Version Control Systems)
    版本管控常用的工具,可分為集中式(Centralized Version Control Systems)與分散式(Distributed Version Control Systems),集中式顧名思義就是說將其程式碼存放在遠端的伺服器(Server),因此每次的提交(Commit)都必須要連到這個伺服器(Server)才行,本機端是無法做提交(Commit)動作,同時企業使用時可能要付費,若伺服器(Server)不小心發生什麼毀損,程式碼可能就會不翼而飛,其架構如下:



    相反的分散式則可以在本機端進行提交(Commit),同時也能夠上傳(push)到伺服器(Server),以我來看其實分散式的伺服器(Server)也可以視為是一種本機,只是相對於我們它是遠端而已,重點是它是免費的,免錢的最好啦!!其架構如下所示


後續會以分散式管控的Git做教學,預計會說明如何搭配Gui+Git做使用~~

2021年3月20日 星期六

C# 使用C++ DLL檔(回傳值)

延續上篇(C# 使用C++ DLL檔)之後,一般使用DLL時,除了將參數提供給DLL演算法做使用,也需將演算法結果從DLL回傳回去,所以這次就來說明如何回傳結果給C#吧。

目前我了解到如果要從DLL回傳結果給C#,都會使用指標做回傳,而我先前測試經驗,C++撰寫上比較不會遇到問題,但在接收端C#有遇過接不到數值、接錯值、或是跑久還造成程式當掉,重點是DLL除錯還不怎麼容易,像是瞎子摸象的感覺,如果有人知道怎樣較快除DLL的Bug,再歡迎留言告訴我,感謝!!!經過一番的測試之後,我目前可正常回傳的資訊分別為字串與float陣列資訊。

Extern.cpp

#ifdef SYSALGORITHM_EXPORTS
#define SYSALGORITHM_API __declspec(dllexport)
#include "SysAlgorithm.h"

extern "C" SYSALGORITHM_API int add(int a, int b)
{
	Calculator Calculator;
	return Calculator.add(a, b);
}

extern "C" SYSALGORITHM_API int subtract(int a, int b)
{
	Calculator Calculator;
	return Calculator.subtract(a, b);
}

extern "C" SYSALGORITHM_API void getDLLValue(float * DLLvalue)
{
	Calculator Calculator;
	return Calculator.getDLLValue(DLLvalue);
}

extern "C" SYSALGORITHM_API void getDLLInfo(char * DLLInfo)
{
	Calculator Calculator;
	return Calculator.getDLLInfo(DLLInfo);
}

#endif

SysAlgorithm.h

#pragma once
#ifndef SYSALGORITHM_H
#define SYSALGORITHM_H

#include <fstream>
#include <string>

class Calculator
{
public:
	Calculator();
	~Calculator();

	int add(int a, int b);
	int subtract(int a, int b);
	void getDLLValue(float * DLLvalue);
	void getDLLInfo(char * DLLInfo);
};

#endif // !SYSALGORITHM_H 

SysAlgorithm.cpp

#include "SysAlgorithm.h"

Calculator::Calculator()
{

}

Calculator::~Calculator()
{

}

int Calculator::add(int a, int b)
{
	return(a + b);
}

int Calculator::subtract(int a, int b)
{
	return(a - b);
}

void Calculator::getDLLValue(float * DLLvalue)
{
	DLLvalue[0] = 0;
	DLLvalue[1] = 1.2;
	DLLvalue[2] = -1.2;
}

void Calculator::getDLLInfo(char * DLLInfo)
{
	char Info[20] = "return DLLInfo";
	strcpy_s(DLLInfo, strlen(Info) + 1, Info);
} 

C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace SysUI
{
    public partial class Form1 : Form
    {
        [DllImport("SysAlgorithm.dll", EntryPoint = "add", CallingConvention = CallingConvention.Cdecl)]
        private static extern int add(int a, int b);

        [DllImport("SysAlgorithm.dll", EntryPoint = "subtract", CallingConvention = CallingConvention.Cdecl)]
        private static extern int subtract(int a, int b);

        [DllImport("SysAlgorithm.dll", EntryPoint = "getDLLValue", CallingConvention = CallingConvention.Cdecl)]
        private static extern void getDLLValue([MarshalAs(UnmanagedType.LPArray)]float[] value);

        [DllImport("SysAlgorithm.dll", EntryPoint = "getDLLInfo", CallingConvention = CallingConvention.Cdecl)]
        private static extern void getDLLInfo(StringBuilder DLLInfo);

        private StringBuilder DLLInfo = new StringBuilder(20);
        private float[] DLLvalue = new float[3];

        public Form1()
        {
            InitializeComponent();
            label1.Text = add(3, 5).ToString();
            label2.Text = subtract(3, 5).ToString();
            getDLLValue(DLLvalue);
            getDLLInfo(DLLInfo);
            label6.Text = DLLvalue[0].ToString() + "," + DLLvalue[1].ToString("F2") + "," + DLLvalue[2].ToString("F2");
            label8.Text = DLLInfo.ToString();
        }
    }
}
 
為了可以正確取得從DLL演算法回傳參數,C#的DllImport這邊很重要!!記得要注意


結果圖:


2021年2月20日 星期六

OpenCV Trackbar

一般寫影像處理時,不同的環境條件所需設定的參數都不一樣,為了加快自己調整參數的速度,決定使用opencv 的tracker bar做即時調整,而opencv的 tracker bar是callback函式,所以當拉把被移動位置時,會主動跳到callback函式做動作,同時也將當前bar的數值給函式使用,如此一來,就可以即時顯示不同結果,實際使用非常方便,不然從程式端去調整很麻煩,如果大家有更好的做法,也歡迎分享一下~~

Trackbar主要有五個參數要設定,分別為Trackbar的標籤名稱、在哪個視窗顯示Trackbar、Trackbar最大值(最小值預設固定為0)、Trackbar當前數值、callback函式(引數內容固定)

若想要使用兩個Trackbar,只要在第一個參數(標籤名稱)提供另一個名稱就可以,如下所示
	createTrackbar("Param1", "Window", &Param1, SliderMax, on_trackbar);
	createTrackbar("Param2", "Window", &Param2, SliderMax, on_trackbar);


程式碼如下:
#include <cstdio>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

const int SliderMax = 100;
int Param1 = 50;
int Param2 = 0;

Mat GrayImg, OutputImg;

static void on_trackbar(int, void*)
{
	double Alpha = (float)Param1 / 50;
	double Beta = Param2;
	GrayImg.convertTo(OutputImg, -1, Alpha, Beta);
	imshow("Window", OutputImg);
}

int main(){

	Mat SrcImg = imread("dog.jpg", CV_LOAD_IMAGE_COLOR);
	namedWindow("Window", WINDOW_AUTOSIZE);
	
	cvtColor(SrcImg, GrayImg, CV_BGR2GRAY);

	createTrackbar("Param1", "Window", &Param1, SliderMax, on_trackbar);
	createTrackbar("Param2", "Window", &Param2, SliderMax, on_trackbar);

	waitKey(0);
	return 0;
}
結果圖:


或是



備註(參考資料):
1.https://docs.opencv.org/3.4/da/d6a/tutorial_trackbar.html