zoukankan      html  css  js  c++  java
  • 二值图像的腐蚀和膨胀

     二值图像的腐蚀和膨胀图像数字处理中应用相当广泛,代码处理也非常easy,仅仅只是一些资料在介绍腐蚀和膨胀原理时,用一些形态学、集合上的概念和术语,搞得也有些”高深莫測“了。

        从图像处理角度看,二值图像的腐蚀和膨胀就是将一个小型二值图(结构元素,一般为3*3大小)在一个大的二值图上逐点移动并进行比較,依据比較的结果作出对应处理而已。以二值图的骨架为黑色点为例:

        作图像腐蚀处理时,假设结构元素中的全部黑色点与它相应的大图像素点全然同样,该点为黑色,否则为白色。

        作图像膨胀处理时,假设结构元素中仅仅要有一个及以上黑色点与它相应的大图像素点同样,该点为黑色,否则为白色。也就是说,假设结构元素中的全部黑色点与它相应的大图像素点没有一个同样,该点为白色,否则为黑色。结构元素中的全部黑色点与它相应的大图像素点没有一个同样,说明大图的这些像素点都是白色的,假如二值图的骨架为白色点,这个对黑色骨架二值图的膨胀处理恰好是对白色骨架二值图的腐蚀处理。同理,对黑色骨架二值图的腐蚀处理也就是对白色骨架的膨胀处理。

        依据这个道理,我们全然能够把对黑色骨架和白色骨架分别所作的腐蚀和膨胀处理代码统一起来,使得原来所须要的四段处理代码变成二段甚至一段处理代码。

        以下是一个对32位像素格式二值图像数据的腐蚀和膨胀处理的所有代码:

    1. //---------------------------------------------------------------------------   
    2.   
    3. // 定义ARGB像素结构   
    4. typedef union  
    5. {  
    6.     ARGB Color;  
    7.     struct  
    8.     {  
    9.         BYTE Blue;  
    10.         BYTE Green;  
    11.         BYTE Red;  
    12.         BYTE Alpha;  
    13.     };  
    14. }ARGBQuad, *PARGBQuad;  
    15. //---------------------------------------------------------------------------   
    16.   
    17. // 获取二值图像data的字节图数据map,骨架像素是否为黑色   
    18. VOID GetDataMap(CONST BitmapData *data, BitmapData *map, BOOL blackPixel)  
    19. {  
    20.     // 字节图边缘扩展1字节,便于处理data的边缘像素   
    21.     map->Width = data->Width + 2;  
    22.     map->Height = data->Height + 2;  
    23.     map->Stride = map->Width;  
    24.     map->Scan0 = (void*)new char[map->Stride * map->Height + 1];// +1防最末字节越界   
    25.     BYTE *ps = (BYTE*)data->Scan0;  
    26.     BYTE *pd0 = (BYTE*)map->Scan0;  
    27.     BYTE *pd = pd0 + map->Stride;  
    28.     BYTE *pt = pd;  
    29.     INT srcOffset = data->Stride - data->Width * sizeof(ARGBQuad);  
    30.     UINT x, y;  
    31.   
    32.     // 假设骨架像素为黑色,获取异或字节图   
    33.     if (blackPixel)  
    34.     {  
    35.         for (y = 0; y < data->Height; y ++, ps += srcOffset)  
    36.         {  
    37.             *pd ++ = *ps ^ 255;  
    38.             for (x = 0; x < data->Width; x ++, ps += sizeof(ARGBQuad))  
    39.                 *pd ++ = *ps ^ 255;  
    40.             *pd ++ = *(ps - sizeof(ARGBQuad)) ^ 255;  
    41.         }  
    42.   
    43.     }  
    44.     // 否则,获取正常字节图   
    45.     else  
    46.     {  
    47.         for (y = 0; y < data->Height; y ++, *pd ++ = *(ps - sizeof(ARGBQuad)), ps += srcOffset)  
    48.         {  
    49.             for (x = 0, *pd ++ = *ps; x < data->Width; x ++, *pd ++ = *ps, ps += sizeof(ARGBQuad));  
    50.         }  
    51.     }  
    52.     ps = pd - map->Stride;  
    53.     for (x = 0; x < map->Width; x ++, *pd0 ++ = *pt ++, *pd ++ = *ps ++);  
    54. }  
    55. //---------------------------------------------------------------------------   
    56.   
    57. // 按结构元素模板templet制作字节掩码数组masks   
    58. // templet低3字节的低3位相应结构元素,如以下的结构元素:   
    59. //   水平     垂直     十字     方形     其他   
    60. //   ○ ○ ○    ○ ● ○    ○ ● ○    ● ● ●    ○ ● ○   
    61. //   ● ● ●    ○ ● ○    ● ● ●    ● ● ●    ● ● ●   
    62. //   ○ ○ ○    ○ ● ○    ○ ● ○    ● ● ●    ○ ● ●   
    63. // 用templet分别表示为:0x000700, 0x020202, 0x020702, 0x070707, 0x020703   
    64. VOID GetTempletMasks(DWORD templet, DWORD masks[])  
    65. {  
    66.     for (INT i = 2; i >= 0; i --, templet >>= 8)  
    67.     {  
    68.         masks[i] = 0;  
    69.         for (UINT j = 4; j; j >>= 1)  
    70.         {  
    71.             masks[i] <<= 8;  
    72.             if (templet & j) masks[i] |= 1;  
    73.         }  
    74.     }  
    75. }  
    76. //---------------------------------------------------------------------------   
    77.   
    78. VOID Erosion_Dilation(BitmapData *data, DWORD templet, BOOL blackPixel)  
    79. {  
    80.     BitmapData map;  
    81.     GetDataMap(data, &map, blackPixel);  
    82.   
    83.     PARGBQuad pd = (PARGBQuad)data->Scan0;  
    84.     BYTE *ps = (BYTE*)map.Scan0 + map.Stride;  
    85.     INT width = (INT)data->Width;  
    86.     INT height = (INT)data->Height;  
    87.     INT dstOffset = data->Stride - width * sizeof(ARGBQuad);  
    88.     INT value = blackPixel? 0 : 255;  
    89.     INT x, y;  
    90.   
    91.     if (templet == 0x0700)  // 水平结构元素单独处理,可提高处理速度   
    92.     {  
    93.         for (y = 0; y < height; y ++, (BYTE*)pd += dstOffset, ps += 2)  
    94.         {  
    95.             for (x = 0; x < width; x ++, pd ++, ps ++)  
    96.             {  
    97.                 if (*(DWORD*)ps & 0x010101)  
    98.                     pd->Blue = pd->Green = pd->Red = value;  
    99.             }  
    100.         }  
    101.     }  
    102.     else  
    103.     {  
    104.         DWORD masks[3];  
    105.         GetTempletMasks(templet, masks);  
    106.   
    107.         for (y = 0; y < height; y ++, (BYTE*)pd += dstOffset, ps += 2)  
    108.         {  
    109.             for (x = 0; x < width; x ++, pd ++, ps ++)  
    110.             {  
    111.                 if (*(DWORD*)(ps - map.Stride) & masks[0] ||  
    112.                     *(DWORD*)ps & masks[1] ||  
    113.                     *(DWORD*)(ps + map.Stride) & masks[2])  
    114.                     pd->Blue = pd->Green = pd->Red = value;  
    115.             }  
    116.         }  
    117.     }  
    118.   
    119.     delete map.Scan0;  
    120. }  
    121. //---------------------------------------------------------------------------   
    122.   
    123. // 二值图膨胀。參数:二值图数据,结构元素模板,是否黑色像素骨架   
    124. FORCEINLINE  
    125. VOID Dilation(BitmapData *data, DWORD templet, BOOL blackPixel = TRUE)  
    126. {  
    127.     Erosion_Dilation(data, templet, blackPixel);  
    128. }  
    129. //---------------------------------------------------------------------------   
    130.   
    131. // 二值图腐蚀。參数:二值图数据,结构元素模板,是否黑色像素骨架   
    132. FORCEINLINE  
    133. VOID Erosion(BitmapData *data, DWORD templet, BOOL blackPixel = TRUE)  
    134. {  
    135.     Erosion_Dilation(data, templet, !blackPixel);  
    136. }  
    137. //---------------------------------------------------------------------------  

        本文的二值图像的腐蚀和膨胀处理代码有下面特点:

        1、可使用随意的3*3结构元素进行处理。

        2、可对黑色或者白色骨架二值图进行处理。

        3、在复制字节图时对边界像素作了扩展,以便对二值图的边界处理。

        4、没有採用结构元素逐点比較的作法,而是使用结构元素掩码,每次对三个像素进行逻辑运算,特别是对水平结构元素的单独处理,大大提高了处理效率。

        5、上面的代码尽管针对的是32位像素格式的二值图,但稍作改动就可以适应24位或者8位像素格式二值图像数据。事实上,对于32位像素格式的二值图,改用以下的处理代码可提高图像处理速度(因其使用位运算一次性对像素的R、G、B分量进行了赋值):

    1. //---------------------------------------------------------------------------   
    2.   
    3. VOID _Dilation(BitmapData *data, BitmapData *map, DWORD templet)  
    4. {  
    5.     PARGBQuad pd = (PARGBQuad)data->Scan0;  
    6.     BYTE *ps = (BYTE*)map->Scan0 + map->Stride;  
    7.     INT width = (INT)data->Width;  
    8.     INT height = (INT)data->Height;  
    9.     INT dstOffset = data->Stride - width * sizeof(ARGBQuad);  
    10.     INT x, y;  
    11.   
    12.     if (templet == 0x0700)  // 水平结构元素单独处理,可提高处理速度   
    13.     {  
    14.         for (y = 0; y < height; y ++, (BYTE*)pd += dstOffset, ps += 2)  
    15.         {  
    16.             for (x = 0; x < width; x ++, pd ++, ps ++)  
    17.                 if (*(DWORD*)ps & 0x010101)  
    18.                     pd->Color &= 0xff000000;  
    19.         }  
    20.     }  
    21.     else  
    22.     {  
    23.         DWORD masks[3];  
    24.         GetTempletMasks(templet, masks);  
    25.   
    26.         for (y = 0; y < height; y ++, (BYTE*)pd += dstOffset, ps += 2)  
    27.         {  
    28.             for (x = 0; x < width; x ++, pd ++, ps ++)  
    29.                 if (*(DWORD*)(ps - map->Stride) & masks[0] ||  
    30.                     *(DWORD*)ps & masks[1] ||  
    31.                     *(DWORD*)(ps + map->Stride) & masks[2])  
    32.                     pd->Color &= 0xff000000;  
    33.         }  
    34.     }  
    35. }  
    36. //---------------------------------------------------------------------------   
    37.   
    38. VOID _Erosion(BitmapData *data, BitmapData *map, DWORD templet)  
    39. {  
    40.     PARGBQuad pd = (PARGBQuad)data->Scan0;  
    41.     BYTE *ps = (BYTE*)map->Scan0 + map->Stride;  
    42.     INT width = (INT)data->Width;  
    43.     INT height = (INT)data->Height;  
    44.     INT dstOffset = data->Stride - width * sizeof(ARGBQuad);  
    45.     INT x, y;  
    46.   
    47.     if (templet == 0x0700)  // 水平结构元素单独处理,可提高处理速度   
    48.     {  
    49.         for (y = 0; y < height; y ++, (BYTE*)pd += dstOffset, ps += 2)  
    50.         {  
    51.             for (x = 0; x < width; x ++, pd ++, ps ++)  
    52.                 if (*(DWORD*)ps & 0x010101)  
    53.                     pd->Color |= 0x00ffffff;  
    54.         }  
    55.     }  
    56.     else  
    57.     {  
    58.         DWORD masks[3];  
    59.         GetTempletMasks(templet, masks);  
    60.   
    61.         for (y = 0; y < height; y ++, (BYTE*)pd += dstOffset, ps += 2)  
    62.         {  
    63.             for (x = 0; x < width; x ++, pd ++, ps ++)  
    64.                 if (*(DWORD*)(ps - map->Stride) & masks[0] ||  
    65.                     *(DWORD*)ps & masks[1] ||  
    66.                     *(DWORD*)(ps + map->Stride) & masks[2])  
    67.                     pd->Color |= 0x00ffffff;  
    68.         }  
    69.     }  
    70. }  
    71. //---------------------------------------------------------------------------   
    72.   
    73. // 二值图膨胀。參数:二值图数据,结构元素模板,是否黑色像素骨架   
    74. VOID Dilation(BitmapData *data, DWORD templet, BOOL blackPixel = TRUE)  
    75. {  
    76.     BitmapData map;  
    77.     GetDataMap(data, &map, blackPixel);  
    78.     if (blackPixel)  
    79.         _Dilation(data, &map, templet);  
    80.     else  
    81.         _Erosion(data, &map, templet);  
    82.     delete map.Scan0;  
    83. }  
    84. //---------------------------------------------------------------------------   
    85.   
    86. // 二值图腐蚀。參数:二值图数据,结构元素模板,是否黑色像素骨架   
    87. VOID Erosion(BitmapData *data, DWORD templet, BOOL blackPixel = TRUE)  
    88. {  
    89.     Dilation(data, templet, !blackPixel);  
    90. }  
    91. //---------------------------------------------------------------------------  

        以下是使用BCB2007和GDI+位图对黑色骨架二值图进行的开运算处理,也可看作是对白色骨架二值图的闭运算处理(先腐蚀后膨胀为开运算;先膨胀后腐蚀为闭运算):

    1. //---------------------------------------------------------------------------   
    2.   
    3. // 图像数据data灰度同一时候二值化,threshold阀值   
    4. VOID GrayAnd2Values(BitmapData *data, BYTE threshold)  
    5. {  
    6.     PARGBQuad p = (PARGBQuad)data->Scan0;  
    7.     INT offset = data->Stride - data->Width * sizeof(ARGBQuad);  
    8.   
    9.     for (UINT y = 0; y < data->Height; y ++, (BYTE*)p += offset)  
    10.     {  
    11.         for (UINT x = 0; x < data->Width; x ++, p ++)  
    12.         {  
    13.             if (((p->Blue * 29 + p->Green * 150 + p->Red * 77 + 128) >> 8) < threshold)  
    14.                 p->Color &= 0xff000000;  
    15.             else  
    16.                 p->Color |= 0x00ffffff;  
    17.   
    18.         }  
    19.     }  
    20. }  
    21. //---------------------------------------------------------------------------   
    22.   
    23. // 锁定GDI+位位图扫描线到data   
    24. FORCEINLINE  
    25. VOID LockBitmap(Gdiplus::Bitmap *bmp, BitmapData *data)  
    26. {  
    27.     Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());  
    28.     bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite,  
    29.         PixelFormat32bppARGB, data);  
    30. }  
    31. //---------------------------------------------------------------------------   
    32.   
    33. // GDI+位图扫描线解锁   
    34. FORCEINLINE  
    35. VOID UnlockBitmap(Gdiplus::Bitmap *bmp, BitmapData *data)  
    36. {  
    37.     bmp->UnlockBits(data);  
    38. }  
    39. //---------------------------------------------------------------------------   
    40.   
    41. void __fastcall TForm1::Button1Click(TObject *Sender)  
    42. {  
    43.     Gdiplus::Bitmap *bmp =  new Gdiplus::Bitmap(L"d:\source1.jpg");  
    44.     Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);  
    45.     g->DrawImage(bmp, 0, 0);  
    46.     BitmapData data;  
    47.     LockBitmap(bmp, &data);  
    48.   
    49.     GrayAnd2Values(&data, 128);  
    50.     Erosion(&data, 0x020702);  
    51.     Dilation(&data, 0x020702);  
    52.   
    53.     UnlockBitmap(bmp, &data);  
    54.     g->DrawImage(bmp, data.Width, 0);  
    55.     delete g;  
    56.     delete bmp;  
    57. }  
    58. //---------------------------------------------------------------------------  

        以下是使用本文代码所作的各种处理效果图,结构元素为“十”字形:

        左上原图,右上二值图,左中腐蚀图,右中膨胀图,左下开运算图,右下闭运算图。这是对黑色骨架二值图而言,假设是白色骨架二值图,中间和下边的左右处理效果就是相反的了。

  • 相关阅读:
    iOS中点击按钮复制指定内容
    iOS开发中,获取图片之后保存或下载到本地相册中
    This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSPhotoLibraryAddUsageDescription key with a string value
    ios中友盟集成好使用总结
    ios中仿蚂蚁森林动画效果
    window frida安装
    Android_server提示端口被占用
    Android stdio Apktool源码编译
    Android ooVoo Apk附件关联分析
    cocos2d-x-2.2.6 project-creator文件夹下python.bat
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4026281.html
Copyright © 2011-2022 走看看