2019年6月30日 星期日

在Visual Studio 2019上安裝Qt

在撰寫Qt程式時不一定需要使用Qt Creator,Visual Studio也是可以的,本章節將示範如何在Visual Studio上安裝Qt。

首先去Qt的官方網站: https://www.qt.io/
按下中間大大的綠色按鈕(Start free Qt trial)

我們選擇右邊的Open Source的部分。

按下最下方的Go open source按鈕。

然後就會下載一個Qt的安裝檔。

開啟之後會進入安裝界面

按Next之後,會開始初始化安裝。

按skip

按Next

選擇要安裝的目的資料夾之後,按Next 

疑,好像沒有MSVC 2019的版本,沒關係,2017的版本也可以安裝在Visual Studio 2019上。
如果電腦是64位元的,可以選擇(MSVC 2017 64-bit)版本。
按Next

詳細閱讀之後點選接受並按下Next

按Next

按下Install,開始安裝 

等它安裝完,需要一點時間(我記得我大概等了十分鐘吧)

安裝完後按Finish,上面的Launch Qt Creater可以勾消掉

然後我們啟動Visual Studio 2019,按選單的延伸模組/管理延伸模組

在右上角的搜尋框搜尋Qt,然後對(Qt Visual Studio Tools)按下Download

等它下載完

接著關掉Visual Studio,它會跑出這個東西,按下Modify

再稍等一會兒

修改完成

重新開啟Visual Studio,對選單的延伸模組按下去,選擇/Qt VS Tools/Qt Options

按下右側的Add,Version name填想要的名稱,路徑選擇剛剛安裝時的資料夾/版本號/msvc2017_64,按下OK

選擇剛剛字定的名稱,再次按下OK
註:因為我之前就安裝過了,所以圖片中有兩個選項。

接著重開Visual Studio,到建立新專案的介面,選擇Qt GUI Application,按下一步

設定完成之後,按下建立

按Next

勾選需要的套件,按Next

設定完成之後,按Finish

接著會出現Qt相關的程式碼,在這裡就可以開始寫Qt程式了,在開始寫之前先測試看看有沒有問題,我們按下綠色箭頭。

執行程式之後如果跑出Qt視窗,恭喜你,安裝成功了。

疑難排解:
若出現64位元Release不能編譯的問題請參見:

[解決方法]Visual Studio不能執行Qt 64位元Release的問題


若開啟Visual Studio出現「未正確載入套件 'Vsix'」的錯誤請參見:

[解決方法] Visual Studio的「未正確載入套件 'Vsix'」錯誤


若出現「內嵌變數至少需要 '/std:c++17'」或「無法由initializer list轉換為qbytearrayview」的錯誤請參見:

[解決]執行QT 出現 「內嵌變數至少需要 '/std:c++17'」的錯誤


=====分隔線=====
如果覺得這篇文有幫助到你們的話,請留言或幫忙按個廣告吧。

您的支持是我寫文的最大動力。

如果依然失敗了,也請留言讓我知道,看看哪個環節出了問題,我有看到就會回。


希望這篇文有幫助到各位ξ( ✿>◡❛)

2019年6月23日 星期日

C#呼叫C++進行影像處理

當使用C#進行影像處理的時候,如果遇上了需要大量運算的部分,C#可能不太夠力(C#的指標運算受到了限制而且在運算過程中做了很多邊界檢查),這時可以考慮呼叫C++的函式處理這些影像。

在開始之前可以先參考這篇文章:
https://corettainformation.blogspot.com/2019/06/cc.html



我們以C# Window Forms App作為範例。




我們拉一個Panel(裝PictureBox用的容器)進表單中,然後讓它填滿整個表單。

然後拉一個PictureBox(圖片),讓它填滿表單,樣式設定為Zoom(這樣就可以等比例縮放影像)

接著拉一個MenuStrip(選單)進表單中,設定兩組選項("載入影像"和"處理")


最後拉一個openFileDialog(開啟檔案用的對話方塊)進表單中。


這樣子需要的元件已經都佈置好了。


然後在表單的程式碼中新增一個Bitmap(點陣圖)


回到表單設計頁面,將"載入影像"點兩下,再次進入程式碼編輯器中。

鍵入以下程式碼


private void 載入影像ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "圖片檔 (*.png;*.jpg;*.bmp;*.gif;*.tif)|*.png;*.jpg;*.bmp;*.gif;*.tif";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                bitmap = new Bitmap(openFileDialog1.FileName);//將選擇的影像載入至bitmap中
                pictureBox1.Image = bitmap;//將bitmap顯示在pictureBox中
            }
        }

解說:
openFileDialog1.Filter可限制檔案的類型,我們選png、jpg、bmp、gif、tif這五種。
使用openFileDialog1.ShowDialog()可開啟瀏覽檔案的對話方塊,當DialogResult.OK(開啟成功)時會將檔案路徑(openFileDialog1.FileName)傳給Bitmap產生新的點陣物件,我們將這個點陣影像指派給bitmap,最後設定PictureBox的影像為這個點陣圖。



接著測試程式是否能正常執行,執行程式後,按下載入影像/選擇要瀏覽的圖片/按下開啟舊檔。



如果能正常顯示影像,代表到目前為只是成功的,可以繼續做下去。


回到表單設計頁面,點兩下"處理",進入程式碼編輯器中。


接著鍵入以下程式碼。
註:必須在標頭引入 using System.Drawing.Imaging;

Bitmap MyNewBmp = bitmap;
Rectangle MyRec = new Rectangle(0, 0, MyNewBmp.Width, MyNewBmp.Height);
BitmapData MyBmpData = MyNewBmp.LockBits(MyRec, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
          //empty
}
MyNewBmp.UnlockBits(MyBmpData);
pictureBox1.Image = MyNewBmp;

解說:
Rectangle可以是一種矩形範圍,此範例為(0,0)的座標到(總寬度,總高度)的矩形範圍(意即選取了整張影像)
BitmapData 是像素資料。
LockBits可將點陣影像鎖定在系統記憶體內,可提高處理效能(但是最後務必使用UnlockBits來解鎖)。
MyNewBmp.LockBits的參數中第一個放入MyRec(剛剛的矩形範圍),第二個是數字3(ImageLockMode是列舉,其中的ReadWrite是列舉值3,代表這個BitmapData可以讀取也可以寫入),第三個參數是PixelFormat的列舉值,Format32bppArgb代表32位元的影像格式(包含Alpha、R、G、B的色彩通道)。

unsafe是C#要使用非安全性代碼時必須使用的關鍵字,此部分先空著,等一下會在這個括號裡加東西。
UnlockBits是用來解鎖LockBits鎖住的記憶體,這是必要的東做,不做的話有可能會出現難以預期的錯誤。
最後將MyMyNewBmp顯示在pictureBox上

註:如果無法使用unsafe的話,請對專案按右鍵/屬性,點選建置/勾選允許不安全的程式碼。



接著我們開始製作C++函式來給C#調用。

對方案按下右鍵/加入/新增專案

嘗試尋找一個C++的CLR類別庫專案。
註:如果找不到的話請見下一張圖。

如果沒有找到的話就必須先安裝CLI的套件。


新增完成後,進入C++的程式碼編輯器中。


接著在類別中新增以下函式:

public:
void inline colorTo255(unsigned char* ptr, int width, int height, int channel)
{
unsigned char** fp = new unsigned char* [height];
int Stride = width * channel, x = 0, y = 0;
for (int j = 0; j < height; j++)
fp[j] = ptr + (Stride * j);
for (y = 0; y < height; y++)
{
for (x = 0; x < Stride; x += channel)
{
fp[y][x] = 255 - fp[y][x];
fp[y][x + 1] = 255 - fp[y][x + 1];
fp[y][x + 2] = 255 - fp[y][x + 2];
}
}
delete[] fp;
}

解說:
我們會在稍後引入點陣圖第0個像素的指標(ptr),其他參數包括width(影像寬度)、height(影像高度)、channel(通道數,有ARGB四種,所以此範例中的值應為四)
Stride是指掃描寬度(影像的每一列有多少位元組),x、y為等一下會用到的座標。
在 for (int j = 0; j < height; j++)fp[j] = ptr + (Stride * j); 中,我們將fp設定為影像中每一列開頭的指標位置的指標。
在外層迴圈,為逐列掃描,一個影像有幾列高度(height)就有多高。
在內層迴圈,為逐行掃描,每次增加4個位元組。
fp[y][x] = 255 - fp[y][x]; 將影像中的(x,y)座標的B值反轉。
fp[y][x + 1] = 255 - fp[y][x + 1];將影像中的(x,y)座標的G值反轉。
fp[y][x + 2] = 255 - fp[y][x + 2];將影像中的(x,y)座標的R值反轉。
註:在這裡,色彩空間的排列方式為BGRA(意即fp[y][x + 3]會是Alpha值)
結束後,使用 delete[] fp; 刪除不再需要用到的fp


 然後我們對這個C++專案按右鍵/建置。

接著對C#專案按右鍵/加入/參考。


選擇剛剛建立的C++專案,按下確定。

加入完參考後就可以讓C#呼叫剛剛建立的類別了,我們回到C#的程式碼編輯器中,在表頭加入剛剛的名稱空間,並新增C++物件。

using ClassLibrary2;
註:意思就是引入剛剛在C++程式碼中出現的「namespace ClassLibrary2」

接著回到處理影像的那部分(unsafe那裡),輸入以下程式碼:

private void 處理ToolStripMenuItem_Click(object sender, EventArgs e)
{
      Bitmap MyNewBmp = bitmap;
      Rectangle MyRec = new Rectangle(0, 0, MyNewBmp.Width, MyNewBmp.Height);
      BitmapData MyBmpData = MyNewBmp.LockBits(MyRec, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
       unsafe
       {
            C.colorTo255((byte*)MyBmpData.Scan0, MyNewBmp.Width, MyNewBmp.Height, 4);
        }
        MyNewBmp.UnlockBits(MyBmpData);
        pictureBox1.Image = MyNewBmp;
}

解說:
colorTo255是剛剛建立的C++函式,MyBmpData.Scan0是MyBmpData的第0個像素(我們將它轉換成byte*),MyNewBmp.Width是影像寬度,MyNewBmp.Height是影像高度 ,4是指通道數(包含ARGB四個通道)。


最後可以來測試程式是否正確了,我們按照剛剛的步驟執行程式,試著載入圖片。



然後按下選單中的處理。


如果看到影像的色彩被反轉了,恭喜你,成功了。

=====分隔線=====
如果覺得這篇文有幫助到你們的話,請留言或幫忙按個廣告吧。

您的支持是我寫文的最大動力。

如果依然失敗了,也請留言讓我知道,看看哪個環節出了問題,我有看到就會回。


希望這篇文有幫助到各位ξ( ✿>◡❛)