2016-11-08 23:21:34 attilax 阅读数 1172

Atitit 图像处理—图像形态学(膨胀与腐蚀)

 

1.1. 膨胀与腐蚀1

1.2. 图像处理之二值膨胀及应用2

1.3. 测试原理,可以给一个5*5pic,测试膨胀算法5

1.4. Photoshop里面的处理5

1.5. 类库的处理,好像没找到jhlabs,6

1.6. Attilax 源码6

 

1.1. 膨胀与腐蚀

  说概念可能很难解释,我们来看图,首先是原图:

  膨胀以后会变成这样:

  腐蚀以后则会变成这样:

  看起来可能有些莫名其妙,明明是膨胀,为什么字反而变细了,而明明是腐蚀,为什么字反而变粗了。

实际上,所谓膨胀应该指

  较亮色块膨胀。

而所谓腐蚀应该指

较亮色块腐蚀。

 

1.2. 图像处理之二值膨胀及应用

基本原理:

膨胀是图像形态学的两个基本操作之一,另外一个是腐蚀操作。最典型的应用是在二值图像

中使用这两个基本操作,是很多识别技术中重要的中间处理步骤。在灰度图像中根据阈值同

样可以完成膨胀与腐蚀操作。对一幅二值图像f(x,y)完成膨胀操作,与对图像的卷积操作类

似,要有个操作数矩阵,最常见的为3X3的矩阵,与卷积操作不同的,是如果矩阵中的像素

点有任意一个点的值是前景色,则设置中心像素点为前景色,否则不变。

 

形态学运算中腐蚀,膨胀,开运算和闭运算:
1. 腐蚀是一种消除边界点,使边界向内部收缩的过程。

可以用来消除小且无意义的物体。
腐蚀的算法:
3x3的结构元素,扫描图像的每一个像素
用结构元素与其覆盖的二值图像做操作
如果都为1,结果图像的该像素为1。否则为0
结果:使二值图像减小一圈

2. 膨胀是将与物体接触的所有背景点合并到该物体中,使边界向外部扩张的过程。

可以用来填补物体中的空洞。
膨胀的算法:
3x3的结构元素,扫描图像的每一个像素
用结构元素与其覆盖的二值图像做操作
如果都为0,结果图像的该像素为0。否则为1
结果:使二值图像扩大一圈

3. 先腐蚀后膨胀的过程称为开运算。

用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。

4. 先膨胀后腐蚀的过程称为闭运算。

用来填充物体内细小空洞、连接邻近物体、平滑其边界的同时并不明显改变其面积。

 

 

让我们来看看实际上是怎样进行膨胀运算的。在图6.14中,左边是被处理的图象X(二值图象,我们针对的是黑点),中间是结构元素B。膨胀的方法是,拿B的中心点和X上的点及X周围的点一个一个地对,如果B上有一个点落在X的范围内,则该点就为黑;右边是膨胀后的结果。可以看出,它包括X的所有范围,就象X膨胀了一圈似的。

6.13   膨胀的示意图

6.14   膨胀运算

6.15为图6.11膨胀后的结果图,能够很明显的看出膨胀的效果。

 

 

粉红色的方格每次在X/Y前进一个像素方格,就会产生一个新的输出像素,图中深蓝色的代

表要输出的像素方格,走完全部的像素方格,就得到了所有输出像素。

 

图中,粉红色的矩阵表示卷积操作数矩阵,黑色表示源图像每个方格代表一个像素点。

 

 

 

1.3. 测试原理,可以给一个5*5pic,测试膨胀算法

 */

public class ImgGene4test {

public static void main(String[] args) {

BufferedImage dest = new BufferedImage(5,5 ,1);

imgx.setBackgroudColor_White(dest);

dest.setRGB(2, 2, new Color(0,0,0).getRGB());

imgx.save_overwrite(dest, "C:\\00capch\\p5.jpg");

System.out.println("==f");

 

}

1.4. Photoshop里面的处理

键后有个文字加粗的.不过我想这个应该满足不了你的要求.你可以先把文字栅格化,然后选择->修改->扩展选区,再进行填充.直到你想要的效果.建议你先把文字做得比你想要的大一点.再进行此操作,得到你想要效果后再缩小.因为是把选区进行扩展再填充,所以边边色起据齿,放大做再缩小这个问题就不会严重了.加分加分.

 

选中文字,在“字符”窗口的左下角,点第一个“T”按钮,即可变粗,如果还不够,那就按住ctrl+鼠标单击文字图层=》“选择”=》修改=》扩展,输入扩展数字确定=》新建图层,填充即可

 

1.5. 类库的处理,好像没找到jhlabs,

1.6. Attilax 源码

 

@Override

public BufferedImage filter(BufferedImage src, BufferedImage dest) {

BufferedImage dest2 = imgx.new_BackgroudColor_White(src.getWidth(), src.getHeight());

Matrix mtrx = new Matrix(3, 3).setImg(src);

imgx.trave(src, (x, y) -> {

System.out.println("" + x + ":" + y);

 

mtrx.fill_and_setMtrx_leftTop_XY(x, y);

 

boolean mtrx_hasAnyForgeColor = mtrx.hasAnyForgeColor(mtrx_item_color -> {

// dark,,so is forge color.. bkgrd lit..

return (imgx.isDarkColor(imgx.gray(mtrx_item_color)));

 

});

if (mtrx_hasAnyForgeColor) {

int forgeColor2 = mtrx.getForgeColor();

mtrxCenterXy_inImg = mtrx.getCenterXy();

try {

dest2.setRGB((int) mtrxCenterXy_inImg.get("x"), (int) mtrxCenterXy_inImg.get("y"), forgeColor2);

} catch (ArrayIndexOutOfBoundsException e) {

System.out.println("ArrayIndexOutOfBoundsException  x:" + x + ",y:" + y);

}

 

}

});

 

return dest2;

}

 

 

图像处理之二值膨胀及应用 - 流浪的鱼 - 博客频道 - CSDN.NET.html

图像处理之二值腐蚀 - 流浪的鱼 - 博客频道 - CSDN.NET.html

图像的膨胀与腐蚀、细化 - 公爵女子 - 博客园.html

java,不依赖于第三方jar的图片处理类 - dragonsoar - ITeye技术网站.html

 

作者:: 绰号:老哇的爪子 ( 全名::Attilax Akbar Al Rapanui 阿提拉克斯 阿克巴 阿尔 拉帕努伊 ) 

汉字名:艾提拉(艾龙)   EMAIL:1466519819@qq.com

转载请注明来源: http://blog.csdn.net/attilax

Atiend

 

2019-05-07 16:11:20 weixin_43887189 阅读数 658

形态学操作

图像形态学操作是基于形状的一系列图像处理操作的合集,主要针对二值图像(二值图像前景物体为1,背景为0)进行处理。
形态学有四个基本操作:腐蚀,膨胀,开,闭。
本文主要记录膨胀和腐蚀。
原图

膨胀

膨胀跟卷积操作类似。
假设有图像A任意形状的内核B(与卷积不同的是,B可以是线、矩形、圆形或者十字等形状。但是通常B为正方形或者圆形),结构元素B在A上面移动遍历所有像素点。B的内核中心点被定义为锚点,进行膨胀操作时,将内核 B划过图像,将内核 B覆盖区域的最大像素值提取,并代替锚点位置的像素。由于二值图像最大值就是1,所以就是用1替换,即变成了白色前景物体。显然,这一最大化操作将会导致图像中的亮区开始扩展。因此膨胀看起来的效果就是让前景物体胀大了一圈一样。对于前景物体中一些细小的断裂处,如果结构元素大小相等,这些断裂的地方就会被连接起来。于是图像高亮部分被进行“领域扩张”,效果图拥有比原图更大的高亮区域。

膨胀处理

腐蚀

腐蚀跟膨胀操作刚好相反。
腐蚀看起来的效果就是让前景物体缩小了一圈一样。对于前景物体中一些细小的连接处,如果结构元素大小相等,这些连接处就会被断开。
以与膨胀相同的图像作为样本,我们使用腐蚀操作。从下面的结果图我们看到亮区(背景)变细,而黑色区域(字母)则变大了。
腐蚀操作

源码实现
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "highgui.h"
#include <stdlib.h>
#include <stdio.h>

using namespace cv;

/// 全局变量
Mat src, erosion_dst, dilation_dst;

int erosion_elem = 0;
int erosion_size = 0;
int dilation_elem = 0;
int dilation_size = 0;
int const max_elem = 2;
int const max_kernel_size = 21;

/** Function Headers */
void Erosion( int, void* );
void Dilation( int, void* );

/** @function main */
int main( int argc, char** argv )
{
  /// Load 图像
  src = imread( argv[1] );

  if( !src.data )
  { return -1; }

  /// 创建显示窗口
  namedWindow( "Erosion Demo", CV_WINDOW_AUTOSIZE );
  namedWindow( "Dilation Demo", CV_WINDOW_AUTOSIZE );
  cvMoveWindow( "Dilation Demo", src.cols, 0 );

  /// 创建腐蚀 Trackbar
  createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo",
                  &erosion_elem, max_elem,
                  Erosion );

  createTrackbar( "Kernel size:\n 2n +1", "Erosion Demo",
                  &erosion_size, max_kernel_size,
                  Erosion );

  /// 创建膨胀 Trackbar
  createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo",
                  &dilation_elem, max_elem,
                  Dilation );

  createTrackbar( "Kernel size:\n 2n +1", "Dilation Demo",
                  &dilation_size, max_kernel_size,
                  Dilation );

  /// Default start
  Erosion( 0, 0 );
  Dilation( 0, 0 );

  waitKey(0);
  return 0;
}

/**  @function Erosion  */
void Erosion( int, void* )
{
  int erosion_type;
  if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }
  else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }
  else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }

  Mat element = getStructuringElement( erosion_type,
                                       Size( 2*erosion_size + 1, 2*erosion_size+1 ),
                                       Point( erosion_size, erosion_size ) );

  /// 腐蚀操作
  erode( src, erosion_dst, element );//src原图像,erosion_dst输出图像,element服饰操作的内核,如果不指定,默认为一个简单的3X3矩阵。否则就需要我们利用getStructuringElement函数来明确指出它的形状。然后,我们还需要指定内核大小,以及 锚点 位置。不指定锚点位置,则默认锚点在内核中心位置。
  imshow( "Erosion Demo", erosion_dst );
}

/** @function Dilation */
void Dilation( int, void* )
{
  int dilation_type;
  if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }
  else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }
  else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }

  Mat element = getStructuringElement( dilation_type,
                                       Size( 2*dilation_size + 1, 2*dilation_size+1 ),
                                       Point( dilation_size, dilation_size ) );
  ///膨胀操作
  dilate( src, dilation_dst, element );
  imshow( "Dilation Demo", dilation_dst );
}

更改Trackbars的位置就会产生不一样的输出图像。
还可以通过增加第三个Trackbar来控制膨胀或腐蚀的次数。

2016-07-17 12:34:21 qq_29540745 阅读数 14561

图像的腐蚀与膨胀

一、原理:

⑴ 图像形态学处理的概念
        数字图像处理中的形态学处理是指将数字形态学作为工具从图像中提取对于表达和描绘区域形状有用处的图像分量,比如边界、骨架以及凸壳,还包括用于预处理或后处理的形态学过滤、细化和修剪等。图像形态学处理中我们感兴趣的主要是二值图像。

⑵ 二值图像的逻辑运算
        逻辑运算尽管本质上很简单,但对于实现以形态学为基础额图像处理算法是一种有力的补充手段。在图像处理中用到的主要逻辑运算是:与、或和非(求补),它们可以互相组合形成其他逻辑运算。

⑶ 膨胀和腐蚀

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

定义结构元素B为:

1 1
1 0
图像元素与结构元素相乘,从而求得右下角元素值
(i-1,j+1) (i,j+1)
(i-1,j) 所求此点(i,j)

① 膨胀
⑴ 用结构元素B,扫描图像A的每一个像素
⑵ 用结构元素与其覆盖的二值图像做“或”操作
⑶ 如果有一个元素为0,结果图像的该像素为0。否则为255

② 腐蚀
         对Z中的集合A和B,B对A进行腐蚀的整个过程如下: 
⑴ 用结构元素B,扫描图像A的每一个像素
⑵ 用结构元素与其覆盖的二值图像做“与”操作
⑶ 如果都为0,结果图像的该像素为0。否则为255

腐蚀处理的结果是使原来的二值图像减小一圈。

二、我再加一个轮廓提取,非常简单的方法:用的是9X9的模板;

(i-1,j+1) (i,j+1) (i+1,j+1)
(i-1,j) 所求此点(i,j) (i+1,j)
(i-1,j-1) (i,j-1) (i+1,j_1)
三、代码

#include<opencv2/opencv.hpp>
#include<iostream>
using  namespace cv;
using namespace std;

Mat srcImage, grayImage, binarygray, erosion, dilation, outline;


static void g_erosion(int, void*);
static void g_dilation(int, void*);
static void g_outline(int, void*);
static void ShowHelpText();

int main()
{
	system("color 3f");
	ShowHelpText();

	srcImage = imread("D://vvoo//cell.jpg");
	cvtColor(srcImage, grayImage, CV_RGB2GRAY);

	int threshold;
	cout << "input threshold: " << endl;
	cin >> threshold;

	//二值化
	binarygray = Mat::zeros(grayImage.rows, grayImage.cols, grayImage.type());
	{
		for (int i = 0; i <grayImage.rows; i++)
		{
			for (int j = 0; j < grayImage.cols; j++)
			{
				if (grayImage.data[i*grayImage.step + j] > threshold)
				{
					binarygray.data[i*binarygray.step + j] = 255;
				}
				else
				{
					binarygray.data[i*binarygray.step + j] = 0;
				}
			}
		}
	}
	//腐蚀
	g_erosion(0, 0);
	//膨胀
	g_dilation(0, 0);
	//轮廓提取
	g_outline(0, 0);

	imshow("原图", srcImage);
	imshow("binarygray", binarygray);

	waitKey(0);
	return 0;
}
static void g_erosion(int, void*)
{
	erosion = Mat::zeros(binarygray.rows, binarygray.cols, binarygray.type());
	{
		for (int i = 1; i < binarygray.rows; i++)
		{
			for (int j = 1; j < binarygray.cols; j++)
			{
				if (binarygray.data[(i - 1)*binarygray.step + j] + binarygray.data[(i - 1)*binarygray.step + j + 1] + binarygray.data[i*binarygray.step + j + 1] == 0)
				{
					erosion.data[i*erosion.step + j] = 0;
				}
				else
				{
					erosion.data[i*erosion.step + j] = 255;
				}
			}

		}

	}
	imshow("erosion_1", erosion);
}
static void g_dilation(int, void*)
{
	dilation = Mat::zeros(binarygray.rows, binarygray.cols, binarygray.type());

	for (int i = 1; i < binarygray.rows; i++)
	{
		for (int j = 1; j < binarygray.cols; j++)
		{
			if (binarygray.data[(i - 1)*binarygray.step + j] == 0 || binarygray.data[(i - 1)*binarygray.step + j - 1] == 0 || binarygray.data[i*binarygray.step + j + 1] == 0)
			{
				dilation.data[i*dilation.step + j] = 0;
			}
			else
			{
				dilation.data[i*dilation.step + j] = 255;
			}
		}

	}

	imshow("dilation_1", dilation);
}
static void g_outline(int, void*)
{
	outline = Mat::zeros(binarygray.rows, binarygray.cols, binarygray.type());

	for (int i = 1; i < binarygray.rows; i++)
	{
		for (int j = 1; j < binarygray.cols; j++)
		{
			if (binarygray.data[i*binarygray.step + j + 1] + binarygray.data[(i - 1)*binarygray.step + j]
				+ binarygray.data[i*binarygray.step + j - 1] + binarygray.data[(i - 1)*binarygray.step + j - 1]
				+ binarygray.data[(i + 1)*binarygray.step + j - 1] + binarygray.data[(i + 1)*binarygray.step + j]
				+ binarygray.data[(i - 1)*binarygray.step + j + 1] + binarygray.data[(i + 1)*binarygray.step + j + 1] == 2040)
			{
				outline.data[i*erosion.step + j] = 255;
			}
			if (binarygray.data[i*binarygray.step + j + 1] + binarygray.data[(i - 1)*binarygray.step + j]
				+ binarygray.data[i*binarygray.step + j - 1] + binarygray.data[(i - 1)*binarygray.step + j - 1]
				+ binarygray.data[(i + 1)*binarygray.step + j - 1] + binarygray.data[(i + 1)*binarygray.step + j]
				+ binarygray.data[(i - 1)*binarygray.step + j + 1] + binarygray.data[(i + 1)*binarygray.step + j + 1] == 0)
			{
				outline.data[i*erosion.step + j] = 255;
			}
		}


	}
	imshow("outline", outline);
}
static void ShowHelpText()
{
	cout << "\n\n本程序涉及到:"<<"腐蚀(erosion)、膨胀(dilation)、轮廓提取(outline)。\n\n" << endl;
}
四、运行结果




五、调用Opencv的erode()函数和dilate()函数实现腐蚀和膨胀功能

1)erode函数,使用像素邻域内的局部极小运算符来腐蚀一张图片,从src输入,由dst输出。支持就地(in-place)操作。

看一下函数原型:

 void erode(
  InputArray src,
  OutputArray dst,
  InputArray kernel,
  Point anchor=Point(-1,-1),
  int iterations=1,
  int borderType=BORDER_CONSTANT,
  const Scalar& borderValue=morphologyDefaultBorderValue()
 );

参数原型

  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像通道的数量可以是任意的,但图像深度应为CV_8U,CV_16U,CV_16S,CV_32F或 CV_64F其中之一。
  • 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
  • 第三个参数,InputArray类型的kernel,腐蚀操作的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。(具体看上文中浅出部分dilate函数的第三个参数讲解部分)
  • 第四个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于单位(element)的中心,我们一般不用管它。
  • 第五个参数,int类型的iterations,迭代使用erode()函数的次数,默认值为1。
  • 第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT。
  • 第七个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。

同样的,使用erode函数,一般我们只需要填前面的三个参数,后面的四个参数都有默认值。而且往往结合getStructuringElement一起使用。

2)dilate函数原型

函数原型:

C++: void dilate(
  InputArray src,
  OutputArray dst,
  InputArray kernel,
  Point anchor=Point(-1,-1),
  int iterations=1,
  int borderType=BORDER_CONSTANT,
  const Scalar& borderValue=morphologyDefaultBorderValue() 
);

参数详解:

  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像通道的数量可以是任意的,但图像深度应为CV_8U,CV_16U,CV_16S,CV_32F或 CV_64F其中之一。
  • 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
  • 第三个参数,InputArray类型的kernel,膨胀操作的核。若为NULL时,表示的是使用参考点位于中心3x3的核。

我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵。其中,getStructuringElement函数的第一个参数表示内核的形状,我们可以选择如下三种形状之一:

矩形: MORPH_RECT

    • 交叉形: MORPH_CROSS
    • 椭圆形: MORPH_ELLIPSE

而getStructuringElement函数的第二和第三个参数分别是内核的尺寸以及锚点的位置。

3)代码实现

#include<opencv2/opencv.hpp>
#include<iostream>
using  namespace cv;
using namespace std;

#define WINDOWN_NAME_1 "原图"
#define WINDOWN_NAME_2 "腐蚀图"
#define WINDOWN_NAME_3 "膨胀图"

int main()
{
	Mat srcImage = imread("D://vvoo//cell.jpg");

	//获取自定义核
	Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
	Mat out_erosion, out_dilate;

	//进行膨胀操作
	erode(srcImage, out_erosion, element);
	dilate(srcImage, out_dilate, element);

	imshow(WINDOWN_NAME_1, srcImage);
	imshow(WINDOWN_NAME_2, out_erosion);
	imshow(WINDOWN_NAME_3, out_dilate);

	waitKey(0);
	return 0;

}

4)运行结果



和自己写的比较下比较一下,差别比较大,主要是因为结构元素大小的关系,我的是2*2,Opencv是15*15的。

我也是初学者,欢迎纠正!



六、参考资料

1.system("color 3f");//输出窗口和字体颜色可变化,3代表窗口颜色(绿色),f代表窗口里字体颜色(白色)

全部颜色为:


2.图像腐蚀、膨胀、细化基本原理

3. 形态学图像处理(一): 膨胀与腐蚀

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

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

    作图像腐蚀处理时,如果结构元素中的所有黑色点与它对应的大图像素点完全相同,该点为黑色,否则为白色。

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

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

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

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

// 定义ARGB像素结构
typedef union
{
	ARGB Color;
	struct
	{
		BYTE Blue;
		BYTE Green;
		BYTE Red;
		BYTE Alpha;
	};
}ARGBQuad, *PARGBQuad;
//---------------------------------------------------------------------------

// 获取二值图像data的字节图数据map,骨架像素是否为黑色
VOID GetDataMap(CONST BitmapData *data, BitmapData *map, BOOL blackPixel)
{
	// 字节图边缘扩展1字节,便于处理data的边缘像素
	map->Width = data->Width + 2;
	map->Height = data->Height + 2;
	map->Stride = map->Width;
	map->Scan0 = (void*)new char[map->Stride * map->Height + 1];// +1防最末字节越界
	BYTE *ps = (BYTE*)data->Scan0;
	BYTE *pd0 = (BYTE*)map->Scan0;
	BYTE *pd = pd0 + map->Stride;
	BYTE *pt = pd;
	INT srcOffset = data->Stride - data->Width * sizeof(ARGBQuad);
	UINT x, y;

	// 如果骨架像素为黑色,获取异或字节图
	if (blackPixel)
	{
		for (y = 0; y < data->Height; y ++, ps += srcOffset)
		{
			*pd ++ = *ps ^ 255;
			for (x = 0; x < data->Width; x ++, ps += sizeof(ARGBQuad))
				*pd ++ = *ps ^ 255;
			*pd ++ = *(ps - sizeof(ARGBQuad)) ^ 255;
		}

	}
	// 否则,获取正常字节图
	else
	{
		for (y = 0; y < data->Height; y ++, *pd ++ = *(ps - sizeof(ARGBQuad)), ps += srcOffset)
		{
			for (x = 0, *pd ++ = *ps; x < data->Width; x ++, *pd ++ = *ps, ps += sizeof(ARGBQuad));
		}
	}
	ps = pd - map->Stride;
	for (x = 0; x < map->Width; x ++, *pd0 ++ = *pt ++, *pd ++ = *ps ++);
}
//---------------------------------------------------------------------------

// 按结构元素模板templet制作字节掩码数组masks
// templet低3字节的低3位对应结构元素,如下面的结构元素:
//   水平     垂直     十字     方形     其它
//   ○ ○ ○    ○ ● ○    ○ ● ○    ● ● ●    ○ ● ○
//   ● ● ●    ○ ● ○    ● ● ●    ● ● ●    ● ● ●
//   ○ ○ ○    ○ ● ○    ○ ● ○    ● ● ●    ○ ● ●
// 用templet分别表示为:0x000700, 0x020202, 0x020702, 0x070707, 0x020703
VOID GetTempletMasks(DWORD templet, DWORD masks[])
{
	for (INT i = 2; i >= 0; i --, templet >>= 8)
	{
		masks[i] = 0;
		for (UINT j = 4; j; j >>= 1)
		{
			masks[i] <<= 8;
			if (templet & j) masks[i] |= 1;
		}
	}
}
//---------------------------------------------------------------------------

VOID Erosion_Dilation(BitmapData *data, DWORD templet, BOOL blackPixel)
{
	BitmapData map;
	GetDataMap(data, &map, blackPixel);

	PARGBQuad pd = (PARGBQuad)data->Scan0;
	BYTE *ps = (BYTE*)map.Scan0 + map.Stride;
	INT width = (INT)data->Width;
	INT height = (INT)data->Height;
	INT dstOffset = data->Stride - width * sizeof(ARGBQuad);
	INT value = blackPixel? 0 : 255;
	INT x, y;

	if (templet == 0x0700)	// 水平结构元素单独处理,可提高处理速度
	{
		for (y = 0; y < height; y ++, (BYTE*)pd += dstOffset, ps += 2)
		{
			for (x = 0; x < width; x ++, pd ++, ps ++)
			{
				if (*(DWORD*)ps & 0x010101)
					pd->Blue = pd->Green = pd->Red = value;
			}
		}
	}
	else
	{
		DWORD masks[3];
		GetTempletMasks(templet, masks);

		for (y = 0; y < height; y ++, (BYTE*)pd += dstOffset, ps += 2)
		{
			for (x = 0; x < width; x ++, pd ++, ps ++)
			{
				if (*(DWORD*)(ps - map.Stride) & masks[0] ||
					*(DWORD*)ps & masks[1] ||
					*(DWORD*)(ps + map.Stride) & masks[2])
					pd->Blue = pd->Green = pd->Red = value;
			}
		}
	}

	delete map.Scan0;
}
//---------------------------------------------------------------------------

// 二值图膨胀。参数:二值图数据,结构元素模板,是否黑色像素骨架
FORCEINLINE
VOID Dilation(BitmapData *data, DWORD templet, BOOL blackPixel = TRUE)
{
	Erosion_Dilation(data, templet, blackPixel);
}
//---------------------------------------------------------------------------

// 二值图腐蚀。参数:二值图数据,结构元素模板,是否黑色像素骨架
FORCEINLINE
VOID Erosion(BitmapData *data, DWORD templet, BOOL blackPixel = TRUE)
{
	Erosion_Dilation(data, templet, !blackPixel);
}
//---------------------------------------------------------------------------

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

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

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

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

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

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

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

VOID _Dilation(BitmapData *data, BitmapData *map, DWORD templet)
{
	PARGBQuad pd = (PARGBQuad)data->Scan0;
	BYTE *ps = (BYTE*)map->Scan0 + map->Stride;
	INT width = (INT)data->Width;
	INT height = (INT)data->Height;
	INT dstOffset = data->Stride - width * sizeof(ARGBQuad);
	INT x, y;

	if (templet == 0x0700)	// 水平结构元素单独处理,可提高处理速度
	{
		for (y = 0; y < height; y ++, (BYTE*)pd += dstOffset, ps += 2)
		{
			for (x = 0; x < width; x ++, pd ++, ps ++)
				if (*(DWORD*)ps & 0x010101)
					pd->Color &= 0xff000000;
		}
	}
	else
	{
		DWORD masks[3];
		GetTempletMasks(templet, masks);

		for (y = 0; y < height; y ++, (BYTE*)pd += dstOffset, ps += 2)
		{
			for (x = 0; x < width; x ++, pd ++, ps ++)
				if (*(DWORD*)(ps - map->Stride) & masks[0] ||
					*(DWORD*)ps & masks[1] ||
					*(DWORD*)(ps + map->Stride) & masks[2])
					pd->Color &= 0xff000000;
		}
	}
}
//---------------------------------------------------------------------------

VOID _Erosion(BitmapData *data, BitmapData *map, DWORD templet)
{
	PARGBQuad pd = (PARGBQuad)data->Scan0;
	BYTE *ps = (BYTE*)map->Scan0 + map->Stride;
	INT width = (INT)data->Width;
	INT height = (INT)data->Height;
	INT dstOffset = data->Stride - width * sizeof(ARGBQuad);
	INT x, y;

	if (templet == 0x0700)	// 水平结构元素单独处理,可提高处理速度
	{
		for (y = 0; y < height; y ++, (BYTE*)pd += dstOffset, ps += 2)
		{
			for (x = 0; x < width; x ++, pd ++, ps ++)
				if (*(DWORD*)ps & 0x010101)
					pd->Color |= 0x00ffffff;
		}
	}
	else
	{
		DWORD masks[3];
		GetTempletMasks(templet, masks);

		for (y = 0; y < height; y ++, (BYTE*)pd += dstOffset, ps += 2)
		{
			for (x = 0; x < width; x ++, pd ++, ps ++)
				if (*(DWORD*)(ps - map->Stride) & masks[0] ||
					*(DWORD*)ps & masks[1] ||
					*(DWORD*)(ps + map->Stride) & masks[2])
					pd->Color |= 0x00ffffff;
		}
	}
}
//---------------------------------------------------------------------------

// 二值图膨胀。参数:二值图数据,结构元素模板,是否黑色像素骨架
VOID Dilation(BitmapData *data, DWORD templet, BOOL blackPixel = TRUE)
{
	BitmapData map;
	GetDataMap(data, &map, blackPixel);
	if (blackPixel)
		_Dilation(data, &map, templet);
	else
		_Erosion(data, &map, templet);
	delete map.Scan0;
}
//---------------------------------------------------------------------------

// 二值图腐蚀。参数:二值图数据,结构元素模板,是否黑色像素骨架
VOID Erosion(BitmapData *data, DWORD templet, BOOL blackPixel = TRUE)
{
	Dilation(data, templet, !blackPixel);
}
//---------------------------------------------------------------------------

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

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

// 图像数据data灰度同时二值化,threshold阀值
VOID GrayAnd2Values(BitmapData *data, BYTE threshold)
{
	PARGBQuad p = (PARGBQuad)data->Scan0;
	INT offset = data->Stride - data->Width * sizeof(ARGBQuad);

	for (UINT y = 0; y < data->Height; y ++, (BYTE*)p += offset)
	{
		for (UINT x = 0; x < data->Width; x ++, p ++)
		{
			if (((p->Blue * 29 + p->Green * 150 + p->Red * 77 + 128) >> 8) < threshold)
				p->Color &= 0xff000000;
			else
				p->Color |= 0x00ffffff;

		}
	}
}
//---------------------------------------------------------------------------

// 锁定GDI+位位图扫描线到data
FORCEINLINE
VOID LockBitmap(Gdiplus::Bitmap *bmp, BitmapData *data)
{
	Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());
	bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite,
		PixelFormat32bppARGB, data);
}
//---------------------------------------------------------------------------

// GDI+位图扫描线解锁
FORCEINLINE
VOID UnlockBitmap(Gdiplus::Bitmap *bmp, BitmapData *data)
{
	bmp->UnlockBits(data);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
	Gdiplus::Bitmap *bmp =  new Gdiplus::Bitmap(L"d:\\source1.jpg");
	Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
	g->DrawImage(bmp, 0, 0);
	BitmapData data;
	LockBitmap(bmp, &data);

	GrayAnd2Values(&data, 128);
	Erosion(&data, 0x020702);
	Dilation(&data, 0x020702);

	UnlockBitmap(bmp, &data);
	g->DrawImage(bmp, data.Width, 0);
	delete g;
	delete bmp;
}
//---------------------------------------------------------------------------

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

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

    如有错误或者建议,请来信指导:maozefa@hotmail.com

 

2012-11-05 17:45:37 qq917141931 阅读数 400
转自:http://blog.csdn.net/maozefa/article/details/7060342
        二值图像的腐蚀和膨胀图像数字处理中应用相当广泛,代码处理也很简单,只不过一些资料在介绍腐蚀和膨胀原理时,用一些形态学、集合上的概念和术语,搞得也有些”高深莫测“了。

    从图像处理角度看,二值图像的腐蚀和膨胀就是将一个小型二值图(结构元素,一般为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; data->Height; ++, ps += srcOffset)  
  36.          
  37.             *pd ++ *ps 255;  
  38.             for (x 0; data->Width; ++, ps += sizeof(ARGBQuad))  
  39.                 *pd ++ *ps 255;  
  40.             *pd ++ *(ps sizeof(ARGBQuad)) 255;  
  41.          
  42.   
  43.      
  44.     // 否则,获取正常字节图  
  45.     else  
  46.      
  47.         for (y 0; data->Height; ++, *pd ++ *(ps sizeof(ARGBQuad)), ps += srcOffset)  
  48.          
  49.             for (x 0, *pd ++ *ps; data->Width; ++, *pd ++ *ps, ps += sizeof(ARGBQuad));  
  50.          
  51.      
  52.     ps pd map->Stride;  
  53.     for (x 0; map->Width; ++, *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 2; >= 0; --, templet >>= 8)  
  67.      
  68.         masks[i] 0;  
  69.         for (UINT 4; 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? 255;  
  89.     INT x, y;  
  90.   
  91.     if (templet == 0x0700)  // 水平结构元素单独处理,可提高处理速度  
  92.      
  93.         for (y 0; height; ++, (BYTE*)pd += dstOffset, ps += 2)  
  94.          
  95.             for (x 0; width; ++, 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; height; ++, (BYTE*)pd += dstOffset, ps += 2)  
  108.          
  109.             for (x 0; width; ++, 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; height; ++, (BYTE*)pd += dstOffset, ps += 2)  
  15.          
  16.             for (x 0; width; ++, 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; height; ++, (BYTE*)pd += dstOffset, ps += 2)  
  27.          
  28.             for (x 0; width; ++, 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; height; ++, (BYTE*)pd += dstOffset, ps += 2)  
  50.          
  51.             for (x 0; width; ++, 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; height; ++, (BYTE*)pd += dstOffset, ps += 2)  
  62.          
  63.             for (x 0; width; ++, 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 (PARGBQuad)data->Scan0;  
  7.     INT offset data->Stride data->Width sizeof(ARGBQuad);  
  8.   
  9.     for (UINT 0; data->Height; ++, (BYTE*)p += offset)  
  10.      
  11.         for (UINT 0; data->Width; ++, ++)  
  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. //---------------------------------------------------------------------------  

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

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

    如有错误或者建议,请来信指导

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