zoukankan      html  css  js  c++  java
  • 使用GDI+位图数据扫描线处理图像的小技巧 from http://blog.csdn.net/maozefa/article/details/4533770

     

    使用GDI+位图数据扫描线处理图像的小技巧

    分类: C/C++ 2071人阅读 评论(4) 收藏 举报

        在GDI+图像处理中,我们经常利用BitmapData结构对图像数据扫描线进行操作,在我的大部分BOLG文章中,都使用了这个方法。GDI+位图通过其LockBits方法和UnlockBits方法,分别用来锁定(获取)和解锁(释放)BitmapData数据,我们一般都在这2个方法之间操作图像数据扫描线,如:

    1. Bitmap *bmp = new Bitmap(L"d://001-1.jpg");  
    2. BitmapData data;  
    3. Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());  
    4.         // 锁定为32位像素格式  
    5. bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite, PixelFormat32bppARGB, &data);  
    6.   
    7. // 这里对图像数据扫描线操作,由于bmp已经锁定,很多方法不能调用  
    8.   
    9. bmp->UnlockBits(&data); // 解锁  
    10. Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);  
    11. g->DrawImage(bmp, 0, 0);  
    12. delete g;  
    13. delete bmp;  

       由于在LockBits方法和UnlockBits方法之间,位图对象是锁定的,很多方法无法调用,有时也感到有些不方便,甚至繁琐。比如对图像数据扫描线进行多次处理,在处理过程中想分步骤显示或者保存时,就不得不反复调用这2个方法;还有就是位图格式低于24位格式的图像无法锁定为24位或32位数据进行操作(我们大多利用24位或者32位像素扫描线进行图像处理)等等。

      可以使用一些小技巧来规避因位图对象锁定而带来的不方便,也可对低于24位格式的图像进行24位或32位图像数据扫描线操作。请看下面的例子:

    1. Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);  
    2. Bitmap *bmp = new Bitmap(L"d://001-1.jpg");  
    3. BitmapData data;  
    4. Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());  
    5. // 预先设置扫描线长度和图像数据  
    6. data.Stride = r.Width * 4;  
    7. data.Scan0 = (void*)new char[r.Height * data.Stride];  
    8. // 建立一个32位像素格式的自定义数据位图对象  
    9. Bitmap *bmp2 = new Bitmap(r.Width, r.Height, data.Stride,  
    10.     PixelFormat32bppARGB, (BYTE*)data.Scan0);  
    11. // 使用ImageLockModeRead标记使bmp图像数据拷贝到data.Scan0。  
    12. // 使用ImageLockModeUserInputBuf标记锁定图像bmp,使bmp和bmp2共享图像数据。  
    13. // 使用ImageLockModeWrite标记使bmp和bmp2同步,即图像数据处理同时  
    14. // 作用于bmp和bmp2,去掉ImageLockModeWrite后,bmp图像不改变,  
    15. // 低于24位格式的图像必须去掉ImageLockModeWrite  
    16. bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite | ImageLockModeUserInputBuf,  
    17.     PixelFormat32bppARGB, &data);  
    18.   
    19. // 这里可分步操作图像扫描线,同时也可显示bmp2,或者调用bmp2的任何方法  
    20. ImageGray(&data); // 图像灰度化,具体过程略  
    21. g->DrawImage(bmp2, 0, 0); // 画图像(或者其它方法调用)  
    22. ImageTwoValues(&dada, 127); // 图像二值化,具体过程略  
    23. g->DrawImage(bmp2, 200, 0); // 画图像(或者其它方法调用)  
    24.   
    25. bmp->UnlockBits(&data);  
    26. delete g;  
    27. delete bmp;  
    28. delete bmp2;  
    29. delete[] data.Scan0; // 必须释放  

        上面例子代码中作了较详细的说明,就不再解释。

        上面的例子为了解释位图对象共享和数据处理同步,代码显得有些凌乱,其实只要记住一点:例子中,自定义位图对象bmp2通过bmp->LockBits方法取得数据后,如果无特殊需要,bmp就可解锁甚至delete,这时也不再需要对bmp2锁定,就可通过对data的处理,达到对bmp2包含的图像数据进行改变的目的。

        将上面代码重新规划一下,使之清晰一些:

    1. BOOL GetBitmapData(WCHAR *fileName, PixelFormat pixelFormat, BitmapData *data)  
    2. {  
    3.     Bitmap *bmp = new Bitmap(fileName);  
    4.     if (bmp->GetLastStatus() != Ok)  
    5.         return FALSE;  
    6.     Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());  
    7.     UINT pixelSize = GetPixelFormatSize(pixelFormat);  
    8.     data->Stride = ((pixelSize * r.Width + 31) & 0xffffffe0) >> 3;  
    9.     data->Scan0 = (void*)new char[r.Height * data->Stride];  
    10.     bmp->LockBits(&r, ImageLockModeRead | ImageLockModeUserInputBuf,  
    11.         pixelFormat, data);  
    12.     bmp->UnlockBits(data);  
    13.     delete bmp;  
    14.     return TRUE;  
    15. }  
    16.   
    17. void __fastcall TForm2::Button1Click(TObject *Sender)  
    18. {  
    19.     // 取得图像的24位像素格式数据  
    20.     BitmapData data;  
    21.     if (!GetBitmapData(L"d://001-1.jpg", PixelFormat24bppRGB, &data))  
    22.         return;  
    23.     // 建立一个24位像素格式的自定义数据位图对象  
    24.     Bitmap *bmp = new Bitmap(data.Width, data.Height, data.Stride,  
    25.         data.PixelFormat, (BYTE*)data.Scan0);  
    26.     Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);  
    27.   
    28.     // 这里可操作图像数据扫描线,不必再锁定bmp,同时也可显示bmp,或者调用bmp的任何方法  
    29.     ImageGray(&data);           // 图像灰度化,具体过程略  
    30.     g->DrawImage(bmp, 0, 0);     // 画图像(或者其它方法调用)  
    31.     ImageTwoValues(&dada, 127); // 图像二值化,具体过程略  
    32.     g->DrawImage(bmp, 200, 0);   // 画图像(或者其它方法调用)  
    33.   
    34.     delete g;  
    35.     delete bmp;  
    36.     delete[] data.Scan0; // 必须释放  
    37. }  

        通过上面代码,就可以看出调用GetBitmapData后,所有的图像数据信息就已经包含在BitmapData结构中了,所以我们可以对这个数据结构进行任何的操作,而不再依赖任何GDI+对象,由此避免了本文前面所说的不方便。之所以又建立一个自定义数据位图对象,只是要借助它进行图像显示、保存等操作而已。

        利用类似于前面的例子代码还可以进行拼图操作:

    1. Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);  
    2. Bitmap *bmp = new Bitmap(L"d://001-1.jpg");  
    3. BitmapData data;  
    4. Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());  
    5. // 预先设置扫描线长度和图像数据,扫描线为2个图像的宽度  
    6. data.Stride = r.Width * 2 * 4;  
    7. data.Scan0 = (void*)new char[r.Height * data.Stride];  
    8.   
    9. // 拷贝图像到左边,注意这里只是读取数据,所以ImageLockModeWrite没使用  
    10. bmp->LockBits(&r, ImageLockModeRead | ImageLockModeUserInputBuf,  
    11.     PixelFormat32bppARGB, &data);  
    12. bmp->UnlockBits(&data);  
    13.   
    14. // 拼合图像到右边,为简化,使用同一张图。  
    15. // 如果图像大小不同,只要r的尺寸一致(不能大于原图)  
    16. (char*)data.Scan0 += (r.Width * 4); // 扫描线地址向右移动  
    17. bmp->LockBits(&r, ImageLockModeRead | ImageLockModeUserInputBuf,  
    18.     PixelFormat32bppARGB, &data);  
    19. bmp->UnlockBits(&data);  
    20.   
    21. (char*)data.Scan0 -= (r.Width * 4); // 还原扫描线地址  
    22.         data.Width = r.Width * 2;  // 重新设置拼图宽度  
    23.       // 拼合图数据信息已经包含在data中了,可以继续对data进行任何的操作  
    24.   
    25. // 建立一个自定义数据位图对象  
    26. Bitmap *bmp2 = new Bitmap(data.Width, data.Height, data.Stride,  
    27.     data.PixelFormat, (BYTE*)data.Scan0);  
    28. // 显示拼合后的图像。  
    29. g->DrawImage(bmp2, 0, 0);  
    30. delete g;  
    31. delete bmp;  
    32. delete bmp2;  
    33. delete[] data.Scan0; // 必须释放  

    本文代码采用BCB编译器。如有错误,可来信指正,并请提出建议:maozefa@hotmail.com

  • 相关阅读:
    Codeforces Round #271 (Div. 2) F. Ant colony 线段树
    poj 1744 tree 树分治
    HDU Shell Necklace CDQ分治+FFT
    BZOJ 1567: [JSOI2008]Blue Mary的战役地图 矩阵二维hash
    BZOJ 1042: [HAOI2008]硬币购物 容斥+背包
    HDU 6078 Wavel Sequence 树状数组优化DP
    Gym
    HDU 6058 Kanade's sum 二分,链表
    HDU 6061 RXD and functions NTT
    ZOJ 3233 Lucky Number 容斥原理
  • 原文地址:https://www.cnblogs.com/songtzu/p/2845324.html
Copyright © 2011-2022 走看看