精华内容
下载资源
问答
  • 最近做一些图像处理需要将图像中的一些像素过滤一下有网友给提了个名词腐蚀算法我不是学图像学的乍一听觉得很神奇后来从网上收集了一些VC代码研究了一下发现其它也就是那么回事尤其是腐蚀算法我在以前的验证码图片去...
  • 图像膨胀、腐蚀算法实现 python源码

    千次阅读 2020-03-21 20:46:43
    如果您觉得本文不错!记得点赞哦! 一. 图像形态学简介: 图解图像腐蚀、膨胀 ↑ ... 图像形态学操作 膨胀和腐蚀算法: 膨胀算法: 对于待操作的像素 f(x,y),不论 f(x,y-1) 、f(x,y+1) 、...

    如果您觉得本文不错!记得点赞哦!


    一. 图像形态学简介:

    图解图像腐蚀、膨胀 ↑

        经验之谈:形态学操作一般作用于二值图像,来连接相邻的元素(膨胀)或分离成独立的元素(侵蚀)。腐蚀和膨胀是针对图片中的白色(即前景)部分!


    二. 图像形态学操作 膨胀和腐蚀的算法:

        膨胀算法:

            对于待操作的像素 f(x,y),不论 f(x,y-1) 、f(x,y+1) 、f(x-1,y) 、f(x+1,y) 哪一个为255,则 f(x,y)=255。

    膨胀操作 ↑

     

            换句话说:将待操作的图像像素与以下  4-近邻矩阵 相乘,结果大于255的话,将中心像素设为255。

    膨胀:待操作像素 * 上面矩阵 > =255,f(x,y) = 255。 ↑

     

        腐蚀算法:

            对于待操作的像素 f(x,y),只有 f(x,y-1) 、f(x,y+1) 、f(x-1,y) 、f(x+1,y) 都为255,则 f(x,y)=255。

            换句话说:将待操作的图像像素与以下  4-近邻矩阵 相乘,结果小于255*4的话,将中心像素设为0。

    腐蚀:待操作像素 * 上面矩阵 < 255*4,f(x,y) = 0 。↑


    三. python实现图像膨胀和腐蚀

    # Writer : wojianxinygcl@163.com
    # Date   : 2020.3.21
    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    # Gray scale
    def BGR2GRAY(img):
    	b = img[:, :, 0].copy()
    	g = img[:, :, 1].copy()
    	r = img[:, :, 2].copy()
    
    	# Gray scale
    	out = 0.2126 * r + 0.7152 * g + 0.0722 * b
    	out = out.astype(np.uint8)
    
    	return out
    
    # Otsu Binalization
    def otsu_binarization(img, th=128):
    	H, W = img.shape
    	out = img.copy()
    
    	max_sigma = 0
    	max_t = 0
    
    	# determine threshold
    	for _t in range(1, 255):
    		v0 = out[np.where(out < _t)]
    		m0 = np.mean(v0) if len(v0) > 0 else 0.
    		w0 = len(v0) / (H * W)
    		v1 = out[np.where(out >= _t)]
    		m1 = np.mean(v1) if len(v1) > 0 else 0.
    		w1 = len(v1) / (H * W)
    		sigma = w0 * w1 * ((m0 - m1) ** 2)
    		if sigma > max_sigma:
    			max_sigma = sigma
    			max_t = _t
    
    	# Binarization
    	print("threshold >>", max_t)
    	th = max_t
    	out[out < th] = 0
    	out[out >= th] = 255
    
    	return out
    
    
    # Morphology Dilate
    def Morphology_Dilate(img, Dil_time=1):
    	H, W = img.shape
    
    	# kernel
    	MF = np.array(((0, 1, 0),
    				(1, 0, 1),
    				(0, 1, 0)), dtype=np.int)
    
    	# each dilate time
    	out = img.copy()
    	for i in range(Dil_time):
    		tmp = np.pad(out, (1, 1), 'edge')
    		for y in range(1, H):
    			for x in range(1, W):
    				if np.sum(MF * tmp[y-1:y+2, x-1:x+2]) >= 255:
    					out[y, x] = 255
    
    	return out
    
    
    # Morphology Erode
    def Morphology_Erode(img, Erode_time=1):
    	H, W = img.shape
    	out = img.copy()
    
    	# kernel
    	MF = np.array(((0, 1, 0),
    				(1, 0, 1),
    				(0, 1, 0)), dtype=np.int)
    
    	# each erode
    	for i in range(Erode_time):
    		tmp = np.pad(out, (1, 1), 'edge')
    		# erode
    		for y in range(1, H):
    			for x in range(1, W):
    				if np.sum(MF * tmp[y-1:y+2, x-1:x+2]) < 255*4:
    					out[y, x] = 0
    
    	return out
    
    
    # Read image
    img = cv2.imread("../paojie.jpg").astype(np.float32)
    
    # Grayscale
    gray = BGR2GRAY(img)
    
    # Otsu's binarization
    otsu = otsu_binarization(gray)
    
    # Morphology - dilate
    erode_result = Morphology_Erode(otsu, Erode_time=2)
    dilate_result = Morphology_Dilate(otsu,Dil_time=2)
    
    # Save result
    cv2.imwrite("Black_and_white.jpg",otsu)
    cv2.imshow("Black_and_white",otsu)
    cv2.imwrite("erode_result.jpg", erode_result)
    cv2.imshow("erode_result", erode_result)
    cv2.imwrite("dilate_result.jpg", dilate_result)
    cv2.imshow("dilate_result",dilate_result)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    四. 实验结果:

    二值图像(左),膨胀图像(中),侵蚀图像(右) ↑


    五. 参考内容:

        ①  https://www.jianshu.com/p/ba2cec49c981

        ②  https://www.cnblogs.com/wojianxin/p/12542004.html


    六. 版权声明:

        未经作者允许,请勿随意转载抄袭,抄袭情节严重者,作者将考虑追究其法律责任,创作不易,感谢您的理解和配合!

    展开全文
  • 尤其是腐蚀算法,我在以前的验证码图片去噪声的文章中提到过,只是那是我不知叫什么名词,就从用途出发,叫做 “根据周边点数去噪”。腐蚀的原理也一样,就是根据当前点的周边点数(如3X3的,周边就有8个点)来修改...
  • 图像处理 腐蚀膨胀算法
  • 程序代码不仅给出了完整的腐蚀代码,本身演示了一个如何用C#进行图像的简单框架。内含完整的代码和一个PDF解释文档(英文)。
  • 实现图像的膨胀与腐蚀算法

    千次阅读 2018-08-12 20:01:25
    用C语言实现图像的膨胀与腐蚀算法 经过几次学习opencv源代码,我决定自己动手写一下膨胀与腐蚀算法,如果具体算法原理不明确的话,可以看看前几篇我总结的膨胀腐蚀算法原理: 腐蚀算法 /**********************...

    用C语言实现图像的膨胀与腐蚀算法

    经过几次学习opencv源代码,我决定自己动手写一下膨胀与腐蚀算法,如果具体算法原理不明确的话,可以看看前几篇我总结的膨胀腐蚀算法原理:


    腐蚀算法

    /*****************************************************
    function: achieve the erode algorithm of the binary image,using 3*3 structural elements 
    parameter:
      1 IplImage* img : original image
      2 int *elementArray : 3*3 structural elements
    return: Run code   0: right  -1: error
    *****************************************************/
    //在画图中 理解有中心点的位置,并将结果赋给结构元素中心点所对应的位置,但是在实际的编程中是遍历每一个点(i,j),可以理解为对应结构元素的中心点
    int ErodeYang01(IplImage* srcImg, IplImage* dstImg, int *elementArray)
    {
        int ret = 0;
        int imgHeight, imgWidth;
        int i, j, k, l;
        int rowPosi, colPosi;   //为结构元素在计算图像中的位置 rowPosi:行 colPosi:列
    
        bool isMatch;
    
        if (srcImg == NULL || elementArray == NULL)
            return ret = -1;
    
        imgHeight = srcImg->height;
        imgWidth = srcImg->width;
    
        memset((void*)dstImg->imageData, 0, dstImg->imageSize);    // memset((void*)dstImg->imageData, 255, dstImg->imageSize); 
    
        for (i = 1; i < imgHeight; i++)                             //i,j 都从1开始 是防止逐行扫描时,访问越界,四周留出一个像素的宽度
        {
            for (j = 1; j < imgWidth; j++)
            {
                isMatch = true;
                for (k = 0; k < 3; k++)                             //k,l 为结构元素的遍历
                {
                    for (l = 0; l < 3; l++)
                    {
                        rowPosi = (i - 1 + k)*srcImg->widthStep;
                        colPosi = j - 1 + l;
    
                        if (elementArray[3 * k + l] == -1)  //此点不关心
                            continue;
    
                        if (elementArray[3 * k + l] == 1)  //前景
                        {
                            if (srcImg->imageData[rowPosi + colPosi] != -1)   //opencv中 二值化图结果只是存放了 0 与 -1  但正常理解二值化值应该 0与1 或是 0与255
                            {
                                isMatch = false;
                                break;
                            }
                        }   
                        else
                        {
                            printf("structural elements exist illegal values");
                            return ret = -1;
                        }
    
                    }
    
                }
    
                if (isMatch)
                    dstImg->imageData[i*dstImg->widthStep + j] = 255;   //赋值为图像中位置点
    
            }
        }
    
        return ret;
    }
    

    膨胀算法

    /*****************************************************
    function: achieve the dilate algorithm of the binary image,using 3*3 structural elements
    parameter:
    1 IplImage* img : original image
    2 int *elementArray : 3*3 structural elements
    return: Run code   0: right  -1: error
    *****************************************************/
    int DilateYang(IplImage* img, IplImage* dstImg, int *elementArray /* elementArray[3][3] */)
    {
        int ret = 0;
        int imgHeight, imgWidth;
        int i, j;    //图像循环变量
        int i_, j_;  //结构元素的对称集循环变量
        int k, l;    //结构元素循环变量
    
        uchar temp;
    
        bool isMatch;
    
        if (img == NULL || elementArray == NULL)
            return ret = -1;
    
        imgHeight = img->height;
        imgWidth = img->width;
    
        memset((void*)dstImg->imageData,0,dstImg->imageSize);    //这是模仿程序写的 没确定是否正确 初始化整幅图都是白色
    
        //计算结构元素的对称集 --算法中是利用结构元素的对称集进行后续处理的
        for (i = 0; i < 2; i++)
        {
            for (j = 0; j < 3 - i; j++)
            {
                temp = elementArray[i * 3 + j];
                elementArray[i * 3 + j] = elementArray[(2 - i) * 3 + (2 - j)];
                elementArray[(2 - i) * 3 + (2 - j)] = temp;
            }
        }
    
    
        for (i = 1; i < imgHeight; i++)
        {
            for (j = 1; j < imgWidth; j++)
            {
    
                for (k = 0; k < 3; k++)
                {
                    for (l = 0; l < 3; l++)
                    {
                        int rowImg = (i - 1 + k)*img->widthStep;
                        int colImg = j - l + 1;
    
                        if (elementArray[k * 3 + l] == -1)
                            continue;
    
                        if (elementArray[k * 3 + l] == 1)
                        {
                            //if(img->imageData[rowImg + colImg] != 0)
                            //  printf("%d", img->imageData[rowImg + colImg]);
    
                            if (img->imageData[rowImg + colImg] == -1)   //opencv中 二值化图结果只是存放了 0 与 -1
                            {
                                dstImg->imageData[i*dstImg->widthStep + j] = 255;
                                break;
                            }
    
                        }
                        else
                        {
                            printf("structural elements exist illegal values");
                            return ret = -1;
                        }
                    }
                }
            }
        }
    
        return ret;
    }
    
    展开全文
  • 基于FPGA的二值图像腐蚀算法的实现九层之台,起于累土1 背景知识腐蚀和膨胀是形态学处理的基础,许多形态学算法都是以这两种操作作为基础的。图1 ...

    基于FPGA的二值图像的腐蚀算法的实现

    九层之台,起于累土

    1 背景知识

    腐蚀和膨胀是形态学处理的基础,许多形态学算法都是以这两种操作作为基础的。

     

                       图1 使用腐蚀去除图像中的部件

    图1 a一幅大小为486x486的连线模板二值图像,图1b~d分别使用11x11,15X15和45X45的模板进行腐蚀。我们从这个例子看到,腐蚀缩小或细化了二值图像中的物体。事实上,我们可以将腐蚀看成是形态学滤波操作,这种操作将小于模板的图像细节从图像中滤除。

    2 腐蚀算法

    使用白色腐蚀:    

    图2 腐蚀演示

    在二值图像的腐蚀算法过程中我们使用二值图像3x3图像矩阵,由图2可知,当九个格子中不全为‘0’或者‘1’时,经过腐蚀算法后九个格子的值最终都会变成‘1’;如果九个全是‘1’或者‘0’时,那么最终的结果九个全是‘1’或者‘0’。

    3 FPGA腐蚀算法实现

     

    图3 二值图像腐蚀FPGA模块架构

    图3中我们使用串口传图,传入的是二值图像。

    FPGA源码:

    /*

    Module name:  binary_image_etch.v

    Description:  binary image etch

    */

    `timescale 1ns/1ps

     

    module binary_image_etch(

           input             clk,  //pixel clk

     input             rst_n,

     input             hs_in,

     input             vs_in,

     input [15:0]      data_in,

     input             data_in_en,

     

     output            hs_out,

     output            vs_out,

     output  reg [15:0]    data_out,

     output            data_out_en

     );

     

    wire [15:0] line0;

    wire [15:0] line1;

    wire [15:0] line2;

     

    reg [15:0] line0_data0;

    reg [15:0] line0_data1;

    reg [15:0] line0_data2;

     

    reg [15:0] line1_data0;

    reg [15:0] line1_data1;

    reg [15:0] line1_data2;

     

    reg [15:0] line2_data0;

    reg [15:0] line2_data1;

    reg [15:0] line2_data2;

     

    reg        data_out_en0;

    reg        data_out_en1;

    reg        data_out_en2;

    reg        hs_r0;

    reg        hs_r1;

    reg        hs_r2;

     

    reg        vs_r0;

    reg        vs_r1;

    reg        vs_r2;

    wire[18:0]  result_data;

     

    line3x3 line3x3_inst(

            .clken(data_in_en),

         .clock(clk),

         .shiftin(data_in),

         .shiftout(),

         .taps0x(line0),

         .taps1x(line1),

         .taps2x(line2)

      );

    //----------------------------------------------------------------------

    // Form an image matrix of three multiplied by three

    //----------------------------------------------------------------------

    always @(posedge clk or negedge rst_n) begin

      if(!rst_n) begin

        line0_data0 <= 16'b0;

     line0_data1 <= 16'b0;

     line0_data2 <= 16'b0;

     

     line1_data0 <= 16'b0;

     line1_data1 <= 16'b0;

     line1_data2 <= 16'b0;

     

     line2_data0 <= 16'b0;

     line2_data1 <= 16'b0;

     line2_data2 <= 16'b0;

     

     data_out_en0 <= 1'b0;

     data_out_en1 <= 1'b0;

     data_out_en2 <= 1'b0;

     

     hs_r0 <= 1'b0;

     hs_r1 <= 1'b0;

     hs_r2 <= 1'b0;

     

     vs_r0 <= 1'b0;

     vs_r1 <= 1'b0;

     vs_r2 <= 1'b0;

      end

      else if(data_in_en) begin

        line0_data0 <= line0;

     line0_data1 <= line0_data0;

     line0_data2 <= line0_data1;

     

     line1_data0 <= line1;

     line1_data1 <= line1_data0;

     line1_data2 <= line1_data1;

     

     line2_data0 <= line2;

     line2_data1 <= line2_data0;

     line2_data2 <= line2_data1;

     

     data_out_en0 <= data_in_en;

     data_out_en1 <= data_out_en0;

     data_out_en2 <= data_out_en1;

       

        hs_r0 <= hs_in;

     hs_r1 <= hs_r0;

     hs_r2 <= hs_r1;

     

     vs_r0 <= vs_in;

     vs_r1 <= vs_r0;

     vs_r2 <= vs_r1;

      end

    end

     

    //-----------------------------------------------------------------

    // line0_data0   line0_data1   line0_data2

    // line1_data0   line1_data1   line1_data2

    // line2_data0   line2_data1   line2_data2

    //----------------------------------------------------------------

    /*

    always @(posedge clk or negedge rst_n) begin

      if(!rst_n)

        data_out <= 16'h0000;

      else if(data_out_en1)

        if(line0_data0 ^ line0_data1 ^ line0_data2 ^ line1_data0 ^ line1_data1 ^ line1_data2 ^ line2_data0 ^ line2_data1 ^ line2_data2)

          data_out <= line1_data1;

        else

       data_out <= 16'hffff;  // 

    end

    */

     

    always @(posedge clk or negedge rst_n) begin

      if(!rst_n)

        data_out <= 16'h0000;

      else if(data_out_en1)

        if((line0_data0 == 16'h0000) && (line0_data1 == 16'h0000) && (line0_data2 == 16'h0000) && (line1_data0 == 16'h0000) && (line1_data1 == 16'h0000) && (line1_data2 == 16'h0000) && (line2_data0 == 16'h0000) && (line2_data1 == 16'h0000) && (line2_data2 == 16'h0000))

          data_out <= line1_data1;

        else if((line0_data0 == 16'hffff) && (line0_data1 == 16'hffff) && (line0_data2 == 16'hffff) && (line1_data0 == 16'hffff) && (line1_data1 == 16'hffff) && (line1_data2 == 16'hffff) && (line2_data0 == 16'hffff) && (line2_data1 == 16'hffff) && (line2_data2 == 16'hffff))

          data_out <= line1_data1;

        else

          data_out <= 16'hffff; 

    end

     

    endmodule  

    IP设置

     

    图4 line3x3 IP

      如图4所示,shift register(RAM-based)ip主要为了形成三行像素缓存。

    4.3寸TFT显示屏的行缓存大小为480,5寸TFT显示屏的行缓存大小为800。

    4实验结果

     

    图5 实验原图

     

    图6 原图显示

     

     

    图7 腐蚀后的结果

    结果分析:

        图6和图7进行对比,图6中最细的图案在图7中已经消失,比较粗的线条也相对变细,实验成功。如果大家想进行更大力度的腐蚀可以使用更大的模板。

       最后欢迎大家关注我的微信公众号FPGA开源工作室和QQ资料群

     

     

     

     

     

    展开全文
  • 基于图像腐蚀和区域生长的煤矸石图像分割算法,宋永宝,孙伟,图像分割在煤矸石自动分选中具有十分重要的作用,本文通过分析煤矸石图像特点,提出了一种融合图像腐蚀和区域生长的图像分割算法
  •  对图像处理有所了解的人都知道图像的形态学处理里最为基础的膨胀和腐蚀算法。二值图像即只有黑白两种颜色组成的图像,一般的白色为内容,黑色为背景。其实简单点理解二值图像的膨胀与腐蚀,腐蚀即是删除对象边界...

    膨胀与腐蚀算法

      对图像处理有所了解的人都知道图像的形态学处理里最为基础的膨胀和腐蚀算法。二值图像即只有黑白两种颜色组成的图像,一般的白色为内容,黑色为背景。其实简单点理解二值图像的膨胀与腐蚀,腐蚀即是删除对象边界某些像素,也就是让白色的区域瘦一圈;而膨胀则是给图像中的对象边界添加像素,即让白色的区域胖上一圈。而这个“圈”的大小,则是由参数来指定的。下面的表展示了一幅图像经过膨胀和腐蚀算法的结果。可以看出膨胀让白色区域大了一圈,白色区域若有较小的黑色洞,则洞被填上;而腐蚀则让白色区域瘦了一圈,一些面积很小的白色区域则消失。

    原图 腐蚀结果 膨胀结果

      腐蚀膨胀的算法原理并不复杂,而且网上有太多的文章都着重于介绍算法的原理思路,对用具体代码实现算法的方式讨论的不多,因而本文专注于几种实现膨胀腐蚀算法的方法。本文介绍了几种不同的腐蚀膨胀算法的实现,每一种实现都各有特点,今后若有更多的方法,也会继续更新加入至本文中。

     

    结构元素与窗口形态

      在介绍算法逻辑之前,有必要先介绍跟腐蚀膨胀算法关系密切的结构元素。结构元素是形态学的基本算子,合理选取结构元素直接影响图像处理的效果和质量。结构元素的选择在于结构元素的形状和尺寸。结构元素可以有不同的形状,圆形、正方形、菱形、六边形、线段形都是可以选择的形状。圆形结构元素,由于各向同性,因此可以得到与方向无关的运算结果,正方形、菱形可以看作是圆盘形的变异。不同形状的结构元素运算结果会有差异,应针对待处理图像的几何形状进行选择。下表对比了正方形,圆形和正菱形三种结构元素形态。

    预览
    ElementSize 121 98 61
    WindowSize 11×11 (r=5) 11×11 (r=5) 11×11 (r=5)
    非空点个数计算方式 |x|<=r&&|y|<=r x2+y2<=r2 |x|+|y|<=r

      在算法实现过程中,往往会将卷积窗口中所有像素相对中心像素的偏移存在一个数组之中,这样在对不规则形状的卷积窗口进行处理时,可以不重复判断结构元素中哪些位置为有效位置,能减少计算次数。在实现之前首先贴上基本数据结构的实现,其中visit_count用来记录像素的访问次数:

    复制代码
    #define byte unsigned char
    struct IntDouble
    {
        int X;
        int Y;
        IntDouble(int x,int y)
        {
            this->X=x;
            this->Y=y;
        }
        IntDouble()
        {
            this->X=0;
            this->Y=0;
        }
    };
    class Bitmap2d
    {
    private:
        byte* data;
        int width;
        int height;
    public:
        int visit_count;
        Bitmap2d(int width,int height,byte v)
        {
            this->data=new byte[width*height];
            memset(data,v,width*height*sizeof(byte));
            this->width=width;
            this->height=height;
            this->visit_count=0;
        }
        Bitmap2d(Bitmap2d& bitmap)
        {
            this->width=bitmap.Width();
            this->height=bitmap.Height();
            this->data=new byte[width*height];
            memcpy(this->data,bitmap.data,sizeof(byte)*Length());
            this->visit_count=0;
        }
        ~Bitmap2d()
        {
            delete[] data;
        }
        inline byte GetValue(int x,int y)
        {
            visit_count++;
            return data[x+y*width];
        }
        inline void SetValue(int x,int y,byte v)
        {
            visit_count++;
            data[x+y*width]=v;
        }
        inline int Width()
        {
            return width;
        }
        inline int Height()
        {
            return height;
        }
        inline int Length()
        {
            return width*height;
        }
        inline bool InRange(int x,int y)
        {
            return x>=0&&x<width&&y>=0&&y<height;
        }
        void ReadRaw(const char* filename)
        {
            FILE* file=fopen(filename,"rb");
            fread(data,sizeof(byte),Length(),file);
            fclose(file);
        }
        void SaveRaw(const char* filename)
        {
            FILE *const file = fopen(filename,"wb");
            fwrite(data,sizeof(byte),Length(),file);
            fclose(file);
        }
    };
    复制代码

     

    实现思路1—根据定义直接算

      首先最为简单的思路是按算法基本原理直接正向求取输出图片的像素值:

    • 膨胀:对于输出图像的所有像素点P,调查原图像中对应窗口中的像素集合S,若S中至少有一个255,则P为255。
    • 腐蚀:对于输出图像的所有像素点P,调查原图像中对应窗口中的像素集合S,若S中至少有一个0,则P为0。

      该算法的腐蚀实现函数(膨胀的类似,就不重复贴,代码工程里有)如下:

    复制代码
    Bitmap2d* Execute()
    {
        Bitmap2d* newBmp=new Bitmap2d(bmp.Width(),bmp.Height(),0);
        for(int j=0;j<bmp.Height();j++)
        {
            for(int i=0;i<bmp.Width();i++)
            {
                if(HasBlackInWindow(this->bmp,i,j))
                    newBmp->SetValue(i,j,0);
                else
                    newBmp->SetValue(i,j,255);
            }
        }
        return newBmp;
    }
    复制代码
    复制代码
    bool HasBlackInWindow(Bitmap2d& bmp,int i,int j)
    {
        for(size_t k=0;k<winOffsets.size();k++)
        {
            int tx=i+winOffsets[k].X;
            int ty=j+winOffsets[k].Y;
            if(!bmp.InRange(tx,ty))
                continue;
            if(bmp.GetValue(tx,ty)==0)
            {
                return true;
            }
        }
        return false;
    }
    复制代码

      膨胀腐蚀算法的主要时间开销来至于对像素的访问,从上述实现不难得该算法对于n*n的位图和k*k大小正方形结构元素访问像素的次数为k2n2。事实上这是实现腐蚀膨胀算法最直接但也是最慢的方式。下图是Engine数据的一个切片二值化之后分别用正方形、圆形和菱形为结构元素膨胀和腐蚀的效果图:

    腐蚀(正方形) 腐蚀(圆形) 腐蚀(菱形)
    原图预览 膨胀(正方形) 膨胀(圆形) 膨胀(菱形)

     

    实现思路2—跳过一些内部区域

      考虑到思路1的算法逻辑耗费在访问黑色区域和白色区域内部的时间较多,我们可以考虑只对黑白交界的边界考察窗口像素。这样的过程我们就可以想象成一把具有尺寸的刷子,膨胀算法刷子为白色,腐蚀算法刷子为黑色,然后让刷子在黑白交界的地方刷过,这样的过程生成的结果等价于思路1的结果。

      其优化的部分是针对远离边界的内部区域的涂刷,这样就能很大程度上减少像素的访问次数。不难想象出,对远离边界的内部区域的涂刷是不起效果的,这就是思路2对思路1改进的主要原因。设算法对n*n的图像按k*k的结构元素腐蚀,则访问像素的次数为a+4b1+(4+k2)b2,其中a为白色像素个数,b1为黑色内部像素个数,b2为黑色边界像素个数,且有a+b1+b2=n2按思路2实现的算法代码如下:

    复制代码
    Bitmap2d* Execute2()
    {
        Bitmap2d* newBmp=new Bitmap2d(bmp);
        for(int j=0;j<bmp.Height();j++)
        {
            for(int i=0;i<bmp.Width();i++)
            {
                if(bmp.GetValue(i,j)==0&&HasWhiteAdjacencyPixel(i,j))
                {
                    SetWindowValue(*newBmp,i,j,0);
                }
            }
        }
        return newBmp;
    }
    复制代码
    复制代码
    bool HasWhiteAdjacencyPixel(int i,int j)
    {
        if(i>0&&bmp.GetValue(i-1,j)==255)
            return true;
        if(i<bmp.Width()-1&&bmp.GetValue(i+1,j)==255)
            return true;
        if(j>0&&bmp.GetValue(i,j-1)==255)
            return true;
        if(j<bmp.Height()-1&&bmp.GetValue(i,j+1)==255)
            return true;
        return false;
    }
    复制代码
    复制代码
    void SetWindowValue(Bitmap2d& bmp,int i,int j,byte v)
    {
        for(size_t k=0;k<winOffsets.size();k++)
        {
            int tx=i+winOffsets[k].X;
            int ty=j+winOffsets[k].Y;
            if(!bmp.InRange(tx,ty))
                continue;
            bmp.SetValue(tx,ty,v);
        }
    }
    复制代码

     

    基于结构元素分解的算法

      对于一些具有规则形状的结构元素,可以利用矩阵分解的原理降低计算次数,例如3*3的正方形结构元素,等价于一个3*3的矩阵,这个矩阵可以为解为{1,1,1}与{1,1,1}-1的乘积。这样使用3*3的矩阵对图像进行卷积等价于先使用{1,1,1}进行卷积,再将结果使用{1,1,1}-1进行卷积。

      由于膨胀腐蚀算法本质上属于卷积的一种特殊形式,这样,正方形结构元素的膨胀腐蚀可以使用如下的方式实现:

    复制代码
    Bitmap2d* Execute4()
    {
        Bitmap2d* newBmp=new Bitmap2d(bmp);
        Bitmap2d* newBmp2=new Bitmap2d(bmp);
        if(this->mode==SQUARE)
        {
            winOffsets.clear();
            for (int i = 0; i < 2 * radius + 1; i++)
            {
                IntDouble t(i-radius,0);
                this->winOffsets.push_back(t);
            }
            for(int j=0;j<bmp.Height();j++)
            {
                for(int i=0;i<bmp.Width();i++)
                {
                    if(HasBlackInWindow(this->bmp,i,j))
                        newBmp->SetValue(i,j,0);
                    else
                        newBmp->SetValue(i,j,255);
                }
            }
            winOffsets.clear();
            for (int j = 0; j < 2 * radius + 1; j++)
            {
                IntDouble t(0,j-radius);
                this->winOffsets.push_back(t);
            }
            for(int j=0;j<newBmp->Height();j++)
            {
                for(int i=0;i<newBmp->Width();i++)
                {
                    if(HasBlackInWindow(*newBmp,i,j))
                        newBmp2->SetValue(i,j,0);
                    else
                        newBmp2->SetValue(i,j,255);
                }
            }
        }
        newBmp2->visit_count+=newBmp->visit_count;
        delete newBmp;
        return newBmp2;
    }
    复制代码

      经过测试可以知道这种方式可以大大减少像素访问次数,以k*k的结构元素腐蚀n*n的图像为例,用思路1的方法需要至少访问k2n2次像素,经过分解再处理两次只需要2kn2次访问。这个思路的详细数学原理可以参考链接

      下图是分解的方法与思路1的方法的结果对比,可以看出这两个算法的结果确实是完全等价的。

    思路1 分解的方法

     

    基于曼哈顿距离的算法

      上述思路1思路2可以适用于任意形状的处理窗口。还有一种基于曼哈顿距离的实现方式,来源于链接,这种方式主要是实现了基于菱形窗口的膨胀腐蚀。这里简单介绍一下曼哈顿距离,曼哈顿距离(Manhattan Distance)是种使用在几何度量空间的几何学用语,用以标明两个点在标准坐标系上的绝对轴距总和。其计算公式为:

      这个距离简单点理解就是“格子距离”,如下图所示:A到B的走格子的最少步数是4,那么AB的曼哈顿距离就是4。

      设我们需要膨胀的图像是下图左这样一个背景为0,内容为1的二值图像。假如我们能够求得所有0像素到离自己最近的1像素的距离的话,我们便做成了一张曼哈顿距离图(下图右)。曼哈顿距离图中像素标的数字代表该像素在左图中寻找最近的1的曼哈顿距离。假如这个像素在左图中本来就是1,则该像素处的曼哈顿距离为0。可以看出,01边界处的0像素的曼哈顿距离较小,而原理边界的0像素曼哈顿距离很大。

    原图 原图得到的曼哈顿距离图

      对于二值图像I,若能够一定处理计算得到他的曼哈顿距离图D,则想求取他的菱形结构元素膨胀结果会非常容易。不难想到,对D进行一个阈值化既可以达到结果。若将曼哈顿图D中曼哈顿距离大于等于1与小于1的像素区分开,则等于原二值图像;若将曼哈顿距离大于等于2与小于2的像素区分开,则等价于对原二值图像进行一个尺寸为1的菱形元素膨胀;若将曼哈顿距离大于等于k(k>1)与小于k的像素区分开,则等价于对原二值图像进行一个尺寸为k的菱形元素膨胀。

      而腐蚀同样可以使用这个思路来完成,前面介绍的曼哈顿距离图是适用与膨胀的,求取的是每个0像素与距离最近的1的距离。在腐蚀的场合下,我们可以求取所有1像素与距离最近的0像素距离的曼哈顿图,这样再进行阈值化,也就完成了腐蚀操作。利用曼哈顿图的好处还体现在需要使用对很多组不同大小的结构元素对相同图像进行膨胀或腐蚀的场合。一旦计算出曼哈顿距离图,就可“一次预处理,多次复用”,预处理的开销只在初次处理产生,之后的所有操作都是阈值化的过程,而阈值化我们知道只需要width*height的访问开销。

      所以问题的关键在与如何实现对二值图像I求取其曼哈顿距离图D。这里以求取膨胀的曼哈顿距离图为例进行说明。其实我们可以利用一种类似于动态规划的思想来解决这个问题。不难发现这个问题是能够分解为规模更小并且可以复用的小型子问题的和。这基于如下的事实:

    1. 对于所有I中为1的像素,D中他们为0。因为他们自己就是1像素显然到自己最近,所以不需要走格子。
    2. 对于I中的0像素p,若其四邻域像素在D中为d0、d1、d2、d3,则D(p)=min(d0,d1,d2,d3)+1。不难看出p到离其最近的1像素的通路必然经过了其四邻域像素。所以0像素p到最近的1的像素的曼哈顿距离可以基于其四邻域的曼哈顿距离求得。

      要实现这个思路,可以使用递归,但也可以使用更加直接的方式,下面的代码使用两次双循环来求得D。首先每个像素d值默认值为最大值width+height,第一次双循环,对每一个像素实际上是考察了上方和左方的像素,经过这一次循环,其d值不一定正确,仅是能够保证每个像素处的d值是相对与上方和左方的最小值加1;但第二次双循环是逆向,从下方和右方访问像素,依次再改变之前的d值,这样就实现了d值确实为min(d0,d1,d2,d3)+1。

    行序正向赋值,每个像素参考了两个父方向的d值 第二次迭代行序逆向复制,每个像素参考4个方向的d值

      采用这个思路实现的一个演示程序如下(不能跑刷新几次试试..):

      其实现的代码如下:

    复制代码
    class DistenceMap
    {
    private:
        int* data;
        int width;
        int height;
    public:
        int visit_count;
        DistenceMap(int width,int height,int v)
        {
            this->data=new int[width*height];
            for(int i=0;i<width*height;i++)
                data[i]=v;
    
            this->width=width;
            this->height=height;
            this->visit_count=0;
        }
        ~DistenceMap()
        {
            delete[] data;
        }
        inline int GetValue(int x,int y)
        {
            visit_count++;
            return data[x+y*width];
        }
        inline void SetValue(int x,int y,int v)
        {
            visit_count++;
            data[x+y*width]=v;
        }
        inline int Width()
        {
            return width;
        }
        inline int Height()
        {
            return height;
        }
        inline int Length()
        {
            return width*height;
        }
    };
    复制代码
    复制代码
    Bitmap2d* Execute3()
    {
        Bitmap2d* newBmp=new Bitmap2d(bmp);
        DistenceMap* dmap=GetDistenceMap();
        for (int i=0; i<bmp.Width(); i++)
        {
            for (int j=0; j<bmp.Height(); j++)
            {
                byte v=dmap->GetValue(i,j)<=radius?0:255;
                newBmp->SetValue(i,j,v);
            }
        }
        newBmp->visit_count+=dmap->visit_count;
        delete dmap;
        return newBmp;
    }
    复制代码
    复制代码
    DistenceMap* GetDistenceMap()
    {
        DistenceMap* distenceMap=new DistenceMap(this->bmp.Width(),this->bmp.Height(),0);
        for (int i=0; i<bmp.Width(); i++)
        {
            for (int j=0; j<bmp.Height(); j++)
            {
                if (bmp.GetValue(i, j) == 0)
                {
                    distenceMap->SetValue(i,j,0);
                } 
                else
                {
                    distenceMap->SetValue(i,j, bmp.Width()+bmp.Height());
                    if (i>0) 
                        distenceMap->SetValue(i,j,Min(distenceMap->GetValue(i,j),distenceMap->GetValue(i-1,j)+1));
                    if (j>0) 
                        distenceMap->SetValue(i,j,Min(distenceMap->GetValue(i,j), distenceMap->GetValue(i,j-1)+1));
                }
            }
        }
    
        for (int i=bmp.Width()-1; i>=0; i--)
        {
            for (int j=bmp.Height()-1; j>=0; j--)
            {
                if (i+1<bmp.Width())
                    distenceMap->SetValue(i,j,Min(distenceMap->GetValue(i,j), distenceMap->GetValue(i+1,j)+1));
                if (j+1<bmp.Height()) 
                    distenceMap->SetValue(i,j,Min(distenceMap->GetValue(i,j), distenceMap->GetValue(i,j+1)+1));
            }
        }
        return distenceMap;
    }
    复制代码

     

    总结

      本文介绍的实现方式,思路1和思路2是基本方法,其中思路2是对思路1的极大改进;矩阵分解方法适用于一些特殊形状的结构元素,其核心是把结构元素所代表的矩阵分解成两个更简单的矩阵的乘积,然后再使用这两个更简单的矩阵作为结构元素。这个思路同样能与思路1和2相配合使用;曼哈顿距离法使用一步预处理先计算出曼哈顿距离图,之后再对这个图进行阈值化,等价于使用菱形结构元素进行的膨胀腐蚀的结果,对于需要多次膨胀腐蚀的场合,这个方法非常适用。

    展开全文
  • 本文基于《OpenCV-Python图像矩阵不扩充边界腐蚀膨胀函数处理算法探究》介绍算法的基础上,用Python 的矩阵操作模拟实现了OpenCV灰度图的腐蚀和膨胀的自定义函数,并在图像处理中,使用OpenCV的膨胀和腐蚀函数和...
  • 内容索引:VC/C++源码,图形处理,图像腐蚀,图像膨胀,细化算法 基于C程序的图像腐蚀图像膨胀,细化算法代码,较简陋那种,甚至还有些不完善,仅供参考吧。
  • 图像处理 膨胀腐蚀 matlab算法实现
  • 图像腐蚀、膨胀算法

    千次阅读 2016-11-21 18:47:03
    腐蚀运算的含义:每当在目标图像中找到一个与结构元素相同的子图像时,就把该子图像中与结构元素的原点位置对应的那个像素位置标注出来,目标图像上被标注出来的所有像素组成的集合,即为腐蚀运算的结果。...
  • 膨胀与腐蚀算法

    万次阅读 2017-04-17 11:00:05
    图像处理有所了解的人都知道图像的形态学处理里最为基础的膨胀和腐蚀算法。二值图像即只有黑白两种颜色组成的图像,一般的白色为内容,黑色为背景。其实简单点理解二值图像的膨胀与腐蚀,腐蚀即是删除对象边界某些...
  • 本文转自:http://www.cnblogs.com/yuanbao/archive/2008/04/12/1149923.html最近做一些图像处理,需要将图像中的一些像素过滤一下,有网友...尤其是腐蚀算法,我在以前的验证码图片去噪声的文章中提到过,只是那是我
  • 图像处理之——膨胀、腐蚀算法详解

    万次阅读 多人点赞 2015-12-30 16:48:37
    结构元素:膨胀和腐蚀操作的最基本组成部分,用于测试输出图像,通常要比待处理的图像小还很多。二维平面结构元素由一个数值为0或1的矩阵组成。结构元素的原点指定了图像中需要处理的像素范围,
  • 最近做一些图像处理,需要将图像中的一些像素过滤一下,有网友给提了个名词:腐蚀算法。我不是学图像学的,乍一听,觉得很神奇。...尤其是腐蚀算法,我在以前的验证码图片去噪声的文章中提到过,只是那是我不知叫什...
  • 灰度膨胀和腐蚀算法

    千次阅读 2016-01-15 12:01:16
    灰度膨胀算法: 1、膨胀可以使一个孤立的高亮噪音扩大化。 2、可以使用物体的一些高亮度的关键细节丢失。...灰度腐蚀算法: 1、腐蚀可以使一个孤立的低亮噪音扩大化。 2、可以使用物体的一些低亮度的关键细节丢失。
  • 图像处理——灰度化、二值化、膨胀算法、腐蚀算法以及开运算和闭运算的的C#代码实现
  • stm32颜色识别追踪算法。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
  • C#膨胀腐蚀算法实现(vs2017可用),请在二值图的情况下使用
  • 灰度图象的腐蚀算法c语言版的,有需要的可以下载去看看
  • 用Python实现形态学处理(膨胀、腐蚀算法) 在不调用opencv库的情况下,利用卷积的思路实现膨胀腐蚀算法 文章目录用Python实现形态学处理(膨胀、腐蚀算法)前言一、膨胀与腐蚀的简介腐蚀(erode)膨胀(dilate)二、...
  • 图像处理之——膨胀、腐蚀算法详解 http://blog.csdn.net/minushuang/article/details/50435689 转载于:https://www.cnblogs.com/Crysaty/p/6510311.html
  • 基于C程序的图像腐蚀图像膨胀,细化算法代码,较简陋那种,甚至还有些不完善,仅供参考吧。
  • 数学形态学之腐蚀算法

    千次阅读 2018-03-10 10:27:31
    换句话说,用S来腐蚀X得到的集合是S完全包括在X中时S的原点位置的集合,用公式表示为:也就是说,对于图像X,用S当做刷子,当S完全被包含在X中时,S的原点集合构成的图像保留了下来,其他的部分被腐蚀掉:如下图所示...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,015
精华内容 2,406
关键字:

图像腐蚀算法