2016-05-18 19:26:25 zhengalen 阅读数 2816
二值图像的腐蚀和膨胀图像数字处理中应用相当广泛,代码处理也很简单,只不过一些资料在介绍腐蚀和膨胀原理时,用一些形态学、集合上的概念和术语,搞得也有些”高深莫测“了。

    从图像处理角度看,二值图像的腐蚀和膨胀就是将一个小型二值图(结构元素,一般为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. //---------------------------------------------------------------------------  
  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. //---------------------------------------------------------------------------  
  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. //---------------------------------------------------------------------------  
  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. //---------------------------------------------------------------------------  

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

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

2015-07-24 10:09:57 mao0514 阅读数 2118

概述:

腐蚀是图像形态学的两个基本操作之一,另外一个是膨胀(Dilate)。二值图像上的腐蚀是腐蚀最典

型的运用,但是腐蚀操作同样可以运用于灰度图像。二值图像腐蚀操作最基本的效果是腐蚀图像

中前景色区域的边缘。使得前景图像区域变小,前景图像内部的背景区域被放大。

 

基本原理:

腐蚀操作要求有待处理的2D图像F(x,y)以及操作数矩阵(类似卷积操作中的Kernel矩阵),常见的

为3X3的操作数矩阵。二值图像腐蚀操作的数学定义如下:

1.      假设X是二值图像中所有像素欧几里德坐标的集合,K为3X3的操作数矩阵

2.       Kx表示操作数处理X的结果,x表示起始像素点

3.      腐蚀操作K对X的所有像素点操作,Kx是X所有像素点的子集。

一个二值图像腐蚀的例子如下,操作数矩阵为3X3,起始点为中心像素点,前景像素表示为1,背

景像素表示为0.图示如下:


当操作数在像素矩阵上移动时,任何一个在操作数之下的输入像素为背景像素时,则设置中心像素

为背景像素0,否则中心像素[0,0]下的输入像素值不变。

 

三:程序效果


四:源代码

  1. package com.gloomyfish.morphology;  
  2.   
  3. import java.awt.Color;  
  4. import java.awt.image.BufferedImage;  
  5.   
  6. public class ErosionFilter extends BinaryFilter {  
  7.     private Color backgroundColor;  
  8.       
  9.     public ErosionFilter() {  
  10.         backgroundColor = Color.WHITE;  
  11.     }  
  12.       
  13.     public Color getBackColor() {  
  14.         return backgroundColor;  
  15.     }  
  16.   
  17.     public void setBackColor(Color forgeColor) {  
  18.         this.backgroundColor = forgeColor;  
  19.     }  
  20.       
  21.     @Override  
  22.     public BufferedImage filter(BufferedImage src, BufferedImage dest) {  
  23.         int width = src.getWidth();  
  24.         int height = src.getHeight();  
  25.   
  26.         if ( dest == null )  
  27.             dest = createCompatibleDestImage( src, null );  
  28.   
  29.         int[] inPixels = new int[width*height];  
  30.         int[] outPixels = new int[width*height];  
  31.         src = super.filter(src, null); // we need to create new one  
  32.         getRGB( src, 00, width, height, inPixels );  
  33.         int index = 0, index1 = 0, newRow = 0, newCol = 0;  
  34.         int ta1 = 0, tr1 = 0, tg1 = 0, tb1 = 0;  
  35.         for(int row=0; row<height; row++) {  
  36.             int ta = 0, tr = 0, tg = 0, tb = 0;  
  37.             for(int col=0; col<width; col++) {  
  38.                 index = row * width + col;  
  39.                 ta = (inPixels[index] >> 24) & 0xff;  
  40.                 tr = (inPixels[index] >> 16) & 0xff;  
  41.                 tg = (inPixels[index] >> 8) & 0xff;  
  42.                 tb = inPixels[index] & 0xff;  
  43.                 boolean erosion = false;  
  44.                 for(int offsetY=-1; offsetY<=1; offsetY++) {  
  45.                     for(int offsetX=-1; offsetX<=1; offsetX++) {  
  46.                         if(offsetY==0 && offsetX==0) {  
  47.                             continue;  
  48.                         }  
  49.                         newRow = row + offsetY;  
  50.                         newCol = col + offsetX;  
  51.                         if(newRow <0 || newRow >=height) {  
  52.                             newRow = 0;  
  53.                         }  
  54.                         if(newCol < 0 || newCol >=width) {  
  55.                             newCol = 0;  
  56.                         }  
  57.                         index1 = newRow * width + newCol;  
  58.                         ta1 = (inPixels[index1] >> 24) & 0xff;  
  59.                         tr1 = (inPixels[index1] >> 16) & 0xff;  
  60.                         tg1= (inPixels[index1] >> 8) & 0xff;  
  61.                         tb1 = inPixels[index1] & 0xff;  
  62.                         if(tr1 == backgroundColor.getRed() && tg1 == tb1) {  
  63.                             erosion = true;  
  64.                             break;  
  65.                         }  
  66.                     }  
  67.                     if(erosion){  
  68.                         break;  
  69.                     }  
  70.                 }  
  71.                   
  72.                 if(erosion) {  
  73.                     tr = tg = tb = backgroundColor.getRed();  
  74.                 } else {  
  75.                     tr = tg = tb = 255 - backgroundColor.getRed();  
  76.                 }  
  77.                 outPixels[index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;  
  78.             }  
  79.         }  
  80.         setRGB( dest, 00, width, height, outPixels );  
  81.         return dest;  
  82.     }  
  83.   
  84.   
  85. }  
2018-06-13 13:31:11 hjxu2016 阅读数 8881

本篇博客是自己的理解,如有错误,欢迎指正

注意,本文所有例子都是24位真彩色图,并不是真正意义上的二值单通道图像。图像下载地址

https://download.csdn.net/download/hjxu2016/10436834

二值图像形态学运算时图像形态学运算的基础。二值图像形态学运算的过程就是在图像中移动结构元素,将结构元素与其下面重叠部分的图像进行交、并等集合运算。为了确定元素中的参照位置,一般把进行形态学运算时的结构元素的参考点称为原点,且远点可以选择在结构元素之中,也可以选择在结构元素之外。

二值图像的形态学处理的基本运算有腐蚀、膨胀、开运算、闭运算,击中与击不中、骨架抽取等。

一. 图像腐蚀

就像土壤侵蚀一样,这个操作会把前景物体的边界腐蚀掉(但是前景仍然是白色)

简单的讲,就是黑的吃白的。至于怎么吃,涉及结构元素以及原点的设计。先看解释


再看结构元素的定义


记录一下步骤

为了防止越界,不处理最左边、最右边、最上和最下边的元素。从第2行、第2列开始检查源图像中的像素点,如果当点对应结构元素中为白色的那些点钟有一个不是白色,则将目标图像中对应的像素点赋值成白色。

将目标图像中的当前点先赋值成黑色,如果源图像中对应结构元素中为白色的那些点中只要有一个不是白色,则将目标图像中的当前点赋值成白色。

在这里,我仅将原点赋值,结果也差不多

先看3*3的结构元素,且所有值都为1,也就是说,只要结构元素对应的像素点有一个值不是白色,为0,那么原点就赋值成黑色0.

第一步:在ResourceView资源视图中,添加Menu子菜单:(注意ID号)

第二步:打开类向导(Ctrl+W),为点运算每个ID菜单添加相应的功能处理函数,如下图所示:选择类CImageProcessingView,选择ID号。

void CImageProcessingView::OnXtxFs()
{
	// TODO: 在此添加命令处理程序代码
	if (numPicture == 0)
	{
		AfxMessageBox("请输入一张图像", MB_OK, 0);
		return;
	}

	if (m_nBitCount != 24)
	{
		AfxMessageBox("输入图片不是24位", MB_OK, 0);
		return;
	}
	AfxMessageBox("形态学-腐蚀1!", MB_OK, 0);
	//int kelnel[3][3];//定义3*3的结构元素
	/*结构元素
	{	{1,1,1}
		{1,1,1}
		{1,1,1}
	}
	*/
	int num;//记录每一行需要填充的字节
	if (m_nWidth * 3 % 4 != 0)
	{
		num = 4 - m_nWidth * 3 % 4;
	}
	else
	{
		num = 0;
	}

	//打开临时的图片  
	FILE *fpo = fopen(BmpName, "rb");
	FILE *fpw = fopen(BmpNameLin, "wb+");
	fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
	fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
	fread(m_pImage, m_nImage, 1, fpo);
	unsigned char *ImageSize;
	ImageSize = new unsigned char[m_nImage];

	int x, y, i, j, val,xx,yy;
	for (y = 1; y < m_nHeight - 1; y++)
	{
		for (x = 1; x < m_nWidth - 1; x++)
		{
			val = 255;//这里我给val赋值为255,如果发现结构元素对应的像素有是0的点,就赋值成0
			for (j = 0; j < 3; j++)
			{
				yy = y + j - 1;
				for (i = 0; i < 3; i++)
				{
					xx = x + i - 1;
					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] == 0)
					{
						val = 0;
						break;
					}
				}
				if (val == 0) break;
			}
			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num +1] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
		}
	}

	fwrite(ImageSize, m_nImage, 1, fpw);

	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level = 400;
	Invalidate();
}

结果如下所示,是不是感觉腐蚀过头了。

我们再换一个2*2的核,且所有值为1看看结果

void CImageProcessingView::OnXtxFs2()
{
	// TODO: 在此添加命令处理程序代码
	if (numPicture == 0)
	{
		AfxMessageBox("请输入一张图像", MB_OK, 0);
		return;
	}

	if (m_nBitCount != 24)
	{
		AfxMessageBox("输入图片不是24位", MB_OK, 0);
		return;
	}
	AfxMessageBox("形态学-腐蚀1!", MB_OK, 0);
	//int kelnel[2][2];//定义3*3的结构元素
	/*结构元素
	{	{1,1,1}
	{1,1,1}
	}
	*/
	int num;//记录每一行需要填充的字节
	if (m_nWidth * 3 % 4 != 0)
	{
		num = 4 - m_nWidth * 3 % 4;
	}
	else
	{
		num = 0;
	}

	//打开临时的图片  
	FILE *fpo = fopen(BmpName, "rb");
	FILE *fpw = fopen(BmpNameLin, "wb+");
	fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
	fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
	fread(m_pImage, m_nImage, 1, fpo);
	unsigned char *ImageSize;
	ImageSize = new unsigned char[m_nImage];

	int x, y, i, j, val, xx, yy;
	for (y = 1; y < m_nHeight - 1; y++)
	{
		for (x = 1; x < m_nWidth - 1; x++)
		{
			val = 255;
			for (j = 0; j < 2; j++)//这边是2*2
			{
				yy = y + j - 1;
				for (i = 0; i < 2; i++)
				{
					xx = x + i - 1;
					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] == 0)
					{
						val = 0;
						break;
					}
				}
				if (val == 0) break;
			}
			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
		}
	}

	fwrite(ImageSize, m_nImage, 1, fpw);

	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level = 400;
	Invalidate();
}

结果如下,粗细还可以


在看一下特殊结构元素

void CImageProcessingView::OnXtxFs3()
{
	// TODO: 在此添加命令处理程序代码
	if (numPicture == 0)
	{
		AfxMessageBox("请输入一张图像", MB_OK, 0);
		return;
	}

	if (m_nBitCount != 24)
	{
		AfxMessageBox("输入图片不是24位", MB_OK, 0);
		return;
	}
	AfxMessageBox("形态学-腐蚀1!", MB_OK, 0);
	//int kelnel[3][3] = {	{0,1,0},
	//						{1,1,1},
	//						{0,1,0} };//定义3*3的结构元素
	int kelnel[3][3] = {{ 1,0,1 },
						{ 0,0,0 },
						{ 1,0,1 } };//定义3*3的结构元素

	int num;//记录每一行需要填充的字节
	if (m_nWidth * 3 % 4 != 0)
	{
		num = 4 - m_nWidth * 3 % 4;
	}
	else
	{
		num = 0;
	}

	//打开临时的图片  
	FILE *fpo = fopen(BmpName, "rb");
	FILE *fpw = fopen(BmpNameLin, "wb+");
	fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
	fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
	fread(m_pImage, m_nImage, 1, fpo);
	unsigned char *ImageSize;
	ImageSize = new unsigned char[m_nImage];

	int x, y, i, j, val, xx, yy;
	for (y = 1; y < m_nHeight - 1; y++)
	{
		for (x = 1; x < m_nWidth - 1; x++)
		{
			val = 255;
			for (j = 0; j < 3; j++)
			{
				yy = y + j - 1;
				for (i = 0; i < 3; i++)
				{
					xx = x + i - 1;
					if (kelnel[j][i]) continue;
					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] == 0)
					{
						val = 0;
						break;
					}
				}
				if (val == 0) break;
			}
			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
		}
	}

	fwrite(ImageSize, m_nImage, 1, fpw);

	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level = 400;
	Invalidate();
}

所以,结构元素的设计是会影响腐蚀结果的。

一. 图像膨胀

我认为膨胀和腐蚀正好相反,简单的讲,就是白的吃黑的。至于怎么吃,涉及结构元素以及原点的设计。


记录一下步骤

为了防止越界,不处理最左边、最右边、最上和最下边的元素。从第2行、第2列开始检查源图像中的像素点,如果当点对应结构元素中为白色的那些点中只要有一个是白色,则将目标图像中的当前点赋值成白色。

代码,建立类向导

void CImageProcessingView::OnXtxPz()
{
	// TODO: 在此添加命令处理程序代码
	// TODO: 在此添加命令处理程序代码
	if (numPicture == 0)
	{
		AfxMessageBox("请输入一张图像", MB_OK, 0);
		return;
	}

	if (m_nBitCount != 24)
	{
		AfxMessageBox("输入图片不是24位", MB_OK, 0);
		return;
	}
	AfxMessageBox("形态学-膨胀1!", MB_OK, 0);
	//int kelnel[3][3] = {	{0,1,0},
	//						{1,1,1},
	//						{0,1,0} };//定义3*3的结构元素
	int kelnel[3][3] = { { 0,1,0 },
						{ 1,1,1 },
						{ 0,1,0 } };//定义3*3的结构元素

	int num;//记录每一行需要填充的字节
	if (m_nWidth * 3 % 4 != 0)
	{
		num = 4 - m_nWidth * 3 % 4;
	}
	else
	{
		num = 0;
	}

	//打开临时的图片  
	FILE *fpo = fopen(BmpName, "rb");
	FILE *fpw = fopen(BmpNameLin, "wb+");
	fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
	fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
	fread(m_pImage, m_nImage, 1, fpo);
	unsigned char *ImageSize;
	ImageSize = new unsigned char[m_nImage];

	int x, y, i, j, val, xx, yy;
	for (y = 1; y < m_nHeight - 1; y++)
	{
		for (x = 1; x < m_nWidth - 1; x++)
		{
			val = 0;//先赋值黑色,再赋值白色
			for (j = 0; j < 3; j++)
			{
				yy = y + j - 1;
				for (i = 0; i < 3; i++)
				{
					xx = x + i - 1;
					if (kelnel[j][i] == 0) continue;
					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] != 0)
					{
						val = 255;
						break;
					}
				}
				if (val == 255) break;
			}
			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
		}
	}

	fwrite(ImageSize, m_nImage, 1, fpw);

	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level = 400;
	Invalidate();
}

白的把黑的吃掉了


三. 图像开运算

开运算就是使用用一个结构元素,对目标图像先腐蚀,再膨胀。先黑的吃白的,再白的吃黑的,可以消除小的白色的点

void CImageProcessingView::OnXtxKys()
{
	// TODO: 在此添加命令处理程序代码
	if (numPicture == 0)
	{
		AfxMessageBox("请输入一张图像", MB_OK, 0);
		return;
	}

	if (m_nBitCount != 24)
	{
		AfxMessageBox("输入图片不是24位", MB_OK, 0);
		return;
	}
	AfxMessageBox("形态学-开运算!", MB_OK, 0);
	//int kelnel[3][3] = {	{0,1,0},
	//						{1,1,1},
	//						{0,1,0} };//定义3*3的结构元素
	int kelnel[3][3] = { { 0,1,0 },
	{ 1,1,1 },
	{ 0,1,0 } };//定义3*3的结构元素

	int num;//记录每一行需要填充的字节
	if (m_nWidth * 3 % 4 != 0)
	{
		num = 4 - m_nWidth * 3 % 4;
	}
	else
	{
		num = 0;
	}

	//打开临时的图片  
	FILE *fpo = fopen(BmpName, "rb");
	FILE *fpw = fopen(BmpNameLin, "wb+");
	fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
	fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
	fread(m_pImage, m_nImage, 1, fpo);
	unsigned char *ImageSize;
	ImageSize = new unsigned char[m_nImage];

	int x, y, i, j, val, xx, yy;
	//先腐蚀
	for (y = 1; y < m_nHeight - 1; y++)
	{
		for (x = 1; x < m_nWidth - 1; x++)
		{
			val = 255;//先赋值成白色
			for (j = 0; j < 3; j++)
			{
				yy = y + j - 1;
				for (i = 0; i < 3; i++)
				{
					xx = x + i - 1;
					if (kelnel[j][i]) continue;
					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] == 0)//只要有一个不是白色,赋值成白色
					{
						val = 0;
						break;
					}
				}
				if (val == 0) break;
			}
			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
		}
	}
	//再膨胀
	for (y = 1; y < m_nHeight - 1; y++)
	{
		for (x = 1; x < m_nWidth - 1; x++)
		{
			val = 0;//先赋值成黑色
			for (j = 0; j < 3; j++)
			{
				yy = y + j - 1;
				for (i = 0; i < 3; i++)
				{
					xx = x + i - 1;
					if (kelnel[j][i] == 0) continue;
					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] != 0)//只要有一个是白色,赋值成白色
					{
						val = 255;
						break;
					}
				}
				if (val == 255) break;
			}
			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
		}
	}

	fwrite(ImageSize, m_nImage, 1, fpw);

	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level = 400;
	Invalidate();
}

四. 图像闭运算

开运算就是使用用一个结构元素,对目标图像先膨胀,再腐蚀。先白的吃黑的,再黑的吃白的,可以消除小的黑色的点

void CImageProcessingView::OnXtxBys()
{
	// TODO: 在此添加命令处理程序代码
	if (numPicture == 0)
	{
		AfxMessageBox("请输入一张图像", MB_OK, 0);
		return;
	}

	if (m_nBitCount != 24)
	{
		AfxMessageBox("输入图片不是24位", MB_OK, 0);
		return;
	}
	AfxMessageBox("形态学-闭运算!", MB_OK, 0);
	//int kelnel[3][3] = {	{0,1,0},
	//						{1,1,1},
	//						{0,1,0} };//定义3*3的结构元素
	int kelnel[3][3] = { { 0,1,0 },
	{ 1,1,1 },
	{ 0,1,0 } };//定义3*3的结构元素

	int num;//记录每一行需要填充的字节
	if (m_nWidth * 3 % 4 != 0)
	{
		num = 4 - m_nWidth * 3 % 4;
	}
	else
	{
		num = 0;
	}

	//打开临时的图片  
	FILE *fpo = fopen(BmpName, "rb");
	FILE *fpw = fopen(BmpNameLin, "wb+");
	fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
	fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
	fread(m_pImage, m_nImage, 1, fpo);
	unsigned char *ImageSize;
	ImageSize = new unsigned char[m_nImage];

	int x, y, i, j, val, xx, yy;
	//先膨胀
	for (y = 1; y < m_nHeight - 1; y++)
	{
		for (x = 1; x < m_nWidth - 1; x++)
		{
			val = 0;//先赋值成黑色
			for (j = 0; j < 3; j++)
			{
				yy = y + j - 1;
				for (i = 0; i < 3; i++)
				{
					xx = x + i - 1;
					if (kelnel[j][i]) continue;
					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] != 0)//只要有一个是白色,则赋值成白色
					{
						val = 255;
						break;
					}
				}
				if (val == 255) break;
			}
			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
		}
	}
	//再腐蚀
	for (y = 1; y < m_nHeight - 1; y++)
	{
		for (x = 1; x < m_nWidth - 1; x++)
		{
			val = 255;//先赋值成白色
			for (j = 0; j < 3; j++)
			{
				yy = y + j - 1;
				for (i = 0; i < 3; i++)
				{
					xx = x + i - 1;
					if (kelnel[j][i] == 0) continue;
					if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] == 0)//只要有一个不是白色,赋值成黑色
					{
						val = 0;
						break;
					}
				}
				if (val == 0) break;
			}
			ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
			ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
		}
	}

	fwrite(ImageSize, m_nImage, 1, fpw);

	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level = 400;
	Invalidate();
}


2019-07-01 14:20:03 qq_40271663 阅读数 58

https://github.com/Zaledzl/picture_processing/tree/master
放在github里面了
这是一个用python做图像处理的教程 里面包含了,阈值化,局部阈值化,各种分割算法,膨胀腐蚀,孔洞填充,批量处理,特征提取,神经网络分类算法等等, 而且有相当全面的中文注释做支撑,欢迎大家学习交流

2019-10-26 21:51:01 weixin_40757930 阅读数 170

 

主要内容

例子

联系(例子)

图像处理(Image Processing)

对已有的图像进行变换、分析、重构,得到的仍是图像。

模糊、灰度化、增强对比度

加滤镜;

腐蚀,膨胀。

增强现实(AR)——它既需要 CG(computer Graphics),又需要 CV,当然也不会漏掉 DIP。

 

它用 DIP 进行预处理,用 CV 进行跟踪物体的识别与姿态获取,用 CG 进行虚拟三维物体的叠加。

 

 

 

 

计算机视觉(Computer Vision)

给定图像,推断景象特性实现的是从模型到图像的变换,也就是说从图像数据提取信息

景象的三维结构,运动检测,识别物体

 

模式识别(Pattern Recognition)

从特征空间到类别空间的变换。研究内容包括特征提取;特征选择;分类器设计等

特征提取(PCA,LDA,LFA,Kernel,Mean Shift,SIFT,ISOMAP,LLE);特征选择;分类器设计(SVM,AdaBoost)等

机器视觉

就是工业数字图像处理

 

没有更多推荐了,返回首页