如何將HObject(HImage)轉換成Bitmap?




某些時候我們可能會有將HImage轉成Bitmap的需求
例如可能想透過Picturebox察看結果但不想使用HalconWindow元件
下面為轉換的方式,三通道彩圖或是灰階圖都適用
原理大致上是利用指標(Pointer)取出像素色彩數值,但彩圖在HImage與Bitmap中RGB的排列方式並不相同
在Bitmap中,32bit位元像素的排列方式為BGRA BGRA BGRA
但在HImage則是 BBBB GGGG RRRR
所幸的是,我們能夠透過函式GetImagePointer3取得每個色彩的指標起始位置
這使的轉換會變得相當容易
灰階圖則是相對單純許多
程式碼會註解解說每一行的功能

/// <summary>
/// 將Halcon Image轉成Bitmap,不適用多通道影像(Channel>3)
/// </summary>
/// <param name="ho_Image">Halcon Image</param>
/// <returns></returns>
public Bitmap HoImageToBitmap(HObject ho_Image)
{
    if (ho_Image == null)
        throw new ArgumentNullException("ho_Image不能為null");

        Bitmap bmp = null;
        BitmapData bitmapData = new BitmapData();
        Rectangle rect;
        bool isColor = false;

        HTuple hv_channels;

        HTuple hv_type;
        HTuple hv_Width;
        HTuple hv_Height;
        HTuple hv_point = null;         //灰階用指標
        HTuple hv_pointerRed = null;    //彩圖用指標R
        HTuple hv_pointerGreen = null;  //彩圖用指標G
        HTuple hv_pointerBlue = null;   //彩圖用指標B

        //確認影像頻道 1:灰階 3:彩圖 other:不允許轉換
        HOperatorSet.CountChannels(ho_Image, out hv_channels);

        if ((int)hv_channels == 1)
            isColor = false;
        else if ((int)hv_channels == 3)
            isColor = true;
        else
            Throw new System.ArgumentException("影像為多通道圖(Channel>3)");

        var pixelFormat = 
        (isColor) ? PixelFormat.Format32bppRgb : PixelFormat.Format8bppIndexed;
        //取得色彩起始位置
        if (isColor)
           HOperatorSet.GetImagePointer3(ho_Image, out hv_pointerRed, out hv_pointerGreen,
                 out hv_pointerBlue, out hv_type, out hv_Width, out hv_Height);
        else
            HOperatorSet.GetImagePointer1(ho_Image, out hv_point, out hv_type, 
                out hv_Width, out hv_Height);
        //前處理部分,建立空的Bitmap與記憶體鎖定位置
        bmp = new Bitmap((int)hv_Width, (int)hv_Height, pixelFormat);

        rect = new Rectangle(0, 0, hv_Width, hv_Height);

        bitmapData = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat);

        unsafe
        {
            if (isColor)
            {   
                //取得HImage色彩與Bitmap的起始位置
                byte* p = (byte*)(void*)bitmapData.Scan0;
                byte* hv_R = (byte*)hv_pointerRed.I;
                byte* hv_G = (byte*)hv_pointerGreen.I;
                byte* hv_B = (byte*)hv_pointerBlue.I;

                for (int j = 0; j < (int)hv_Height; j++)
                {
                    for (int i = 0; i < (int)hv_Width; i++)
                    {
                            //依序填入Bitmap之後移動指標
                            p[0] = (byte)hv_B[0]; ++p; ++hv_B;
                            p[0] = (byte)hv_G[0]; ++p; ++hv_G;
                            p[0] = (byte)hv_R[0]; ++p; ++hv_R;
                            p[0] = (byte)255; ++p;
                    }
                }
            }
            else
            {

                //灰階用調色盤前置
                ColorPalette grayPalette;
                using (Bitmap tempBmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
                    grayPalette = tempBmp.Palette;

                for (int i = 0; i < 256; i++)
                    grayPalette.Entries[i] = Color.FromArgb(i, i, i);


                byte* p = (byte*)(void*)bitmapData.Scan0;
                byte* hv_p = (byte*)hv_point.I;

                for (int j = 0; j < (int)hv_Height; j++)
                {
                    for (int i = 0; i < (int)hv_Width; i++)
                    {
                            p[0] = (byte)hv_p[0];
                            ++p; ++hv_p;
                    }
                }
                //更改調色盤
                bmp.Palette = grayPalette;
            }
            bmp.UnlockBits(bitmapData);
        }
        ho_Image.Dispose();
        return bmp;
}
參考資料



留言

張貼留言