2014年7月3日 星期四

實驗記錄 - Halcon 影像處理之彩色影像


前言

在 .NET 中常用的 Halcon 讀取影像的方法,一般會採用下列的方式
var filepath = @"D:\test.png";
var img = new HImage(filepath);
如果影像不是儲存於檔案系統中,而是存在於 byte[] array 中,可以採用下列的方式
// 假設 source 儲存的是一個 w = 10, h = 20 大小的灰階影像
byte[] source = new byte[200];
GCHandle pinSourceArray = GCHandle.Alloc(souce, GCHandleType.Pinned);
IntPtr sourcePtr = pinSourceArray.AddrOfPinnedObject();
var img = new HImage("byte", 10, 20, sourcePtr);
pinSourceArray.Free();     
好的,那彩色影像又該如何呢?
Halcon 提供了以下方法建立彩色影像
  • HOperatorSet.GenImage3(RGBImage, type, imageWidth, imageHeight, RedPointer, GreenPointer, BluePointer)
  • HOperatorSet.GenImageInterleaved(RGBImage, sourcePointer, colorFormat, originalWidth, originalHeight, alignment, type, imageWidth, imageHeight, startRow, startColumn, bitPerChannel, bitshift)
如果是採用 GenImage3 方法,就必須要有 R, G, B 三色的指標。
實驗的資料來源,是先由 Bitmap 讀取 24 bit 的 8 * 8 彩色影像, 取得 source.
byte[] source = new byte[8*8*3];
資料的儲存方式為
Color 24 bit 的儲存方式為
     * source[0] = blue
     * source[1] = green
     * source[2] = red

Color 32 bit 的儲存方式為
     * source[0] = blue
     * source[1] = green
     * source[2] = red
     * source[3] = alpha
所以,要作 RGB 的資料分離,再取得各別的指標

RGB 分離實驗 1,陣列給值

有了來源 byte[] source, 要分離出 RGB 3 個 byte[] 有什麼難, 程式碼如下
int imgW = 8; //影像寬
int imgH = 8; //影像高
int size = imgW *imgH;

byte[] redSource = new byte[size];
byte[] greenSource = new byte[size];
byte[] greenSource = new byte[size];

//迴圈給值
for(int index = 0, pos = 0 ; index < source.Length;)
{
    redSource[pos] = source[index + 2];
    greenSource[pos] = source[index + 1];
    blueSource[pos] = source[index + 0];

    index+=3;
    pos++;
}
很直覺,也沒什麼問題,概念實作完畢後,當然要把資料量弄大一點,所以,新的測試影像為 24bit 的 4000 * 3000 彩色影像。
結果,這個分離程式效率實在是太慢了,平均約花費了 300 ms。所以只能改寫了。

RGB 分離實驗 2,利用指標給值

不多說,馬上用指標改寫
unsafe
{
    fixed (byte* sourcePtr = source, redPtr = redSource, greenPtr = greenSource, bluePtr = blueSource)
    {
        byte* pSource = sourcePtr;
        byte* pTRed = redPtr;
        byte* pTGreen = greenPtr;
        byte* pTBlue = bluePtr;
        for (int index = 0; index < size; index++)
        {
            *pTRed = pSource[2];
            *pTGreen = pSource[1];
            *pTBlue = pSource[0];

            pTRed++;
            pTGreen++;
            pTBlue++;
            pSource += 3;
        }
    }
}
效率提高了,平均約花費了 100 ms 左右,可是還是慢。
最後,使用 HOperatorSet.GenImageInterleaved 方法,平均約花費 50ms
GCHandle pinSourceArray = GCHandle.Alloc(souce, GCHandleType.Pinned);
IntPtr sourcePtr = pinSourceArray.AddrOfPinnedObject();     

HObject imgRGB;
HOperatorSet.GenEmptyObj(out imgRGB);
IntPtr sourcePtr = pinnedArray.AddrOfPinnedObject();

string imgType = "bgr";  //影像資料的儲存方式
HOperatorSet.GenImageInterleaved(out imgRGB, sourcePtr, imgType, width, height
                                , 0, "byte", width, height, 0, 0, -1, 0);

sourcePtr.Free();

結論

實驗 1 速度為 300 ms, 實驗 2 採用指標,速度為 100 ms, Halcon 的 GenImageInterLeaved 方法為 50 ms.
它是怎麼辦到的,是否用了平行處理的技術,還是其它? 不可知。 下次將考慮使用 Parallel 的方法試試看。

Note

  • 有關 GenImageInterleaved 方法的詳細資訊,參考 [1]
  • 影像處理的速度可以有多快,參考 [2]
  • .NET 並行處理, 參考 [3], [4]
  • 指標操作,參考 [5] 的系列文章
其它有關於 GCHandle 一樣搜尋 MSDN 就可以找到相關的說明

參考資料 ( References )

沒有留言:

張貼留言