【OpenCV学习笔记 008】基于形态学运算的图像变换

一、形态学滤波对图像进行腐蚀、膨胀运算

1.概念及原理

(1)腐蚀和膨胀是形态学中最基本的运算,而结构元素又是数学形态学中最基本的工具。结构元素可以简单理解为像素的结构以及一个原点。使用形态学滤波就是对像素的每个元素应用这个结构,当结构元素的原点和像素对齐时,它与图像的相交部分定义了一组进行形态学运算的像素。结构元素可以是任何形状,我们一般使用简单的方形、圆形、或菱形,原点即位于中心位置。

(2)腐蚀替换当前像素位像素集合中找到的最小像素值,膨胀则相反。为了想象出两个运算的效果,可以考虑背景(黑色)和前景(白色)。腐蚀情况下,给定像素的结构元素接触到背景,该像素被置为背景。膨胀则是给定像素位置接触到前景物体,该像素被置为白色。因此,腐蚀后的图像尺寸变小,膨胀即相反。

2.实验

由于形态学滤波通常使用二值图像,所以我们先把图像转换为二值图像然后对图像进行腐蚀和膨胀。

以下实验都是对该二值图像进行处理。

源码示例

#include<iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv/cv.h>

using namespace std;
using namespace cv;

Mat g_pGrayImage;
Mat g_pBinaryImage;

int main(){

	// 从文件中加载原图
	Mat pSrcImage = cvLoadImage("horse.jpg", CV_LOAD_IMAGE_UNCHANGED);
	// 转为灰度图
	g_pGrayImage.create(pSrcImage.rows, pSrcImage.cols, CV_8U);
	cvtColor(pSrcImage, g_pGrayImage, CV_BGR2GRAY);
	// 创建二值图
	g_pBinaryImage.create(g_pGrayImage.rows, g_pGrayImage.cols, CV_8U);
	// 转为二值图
	threshold(g_pGrayImage, g_pBinaryImage, 100, 255, CV_THRESH_BINARY);
	//显示二值图像
	cvNamedWindow("BinaryImage");
	imshow("BinaryImage", g_pBinaryImage);

	//腐蚀图像
	Mat eroded;	//目标图像
	erode(g_pBinaryImage, eroded, Mat());
	//显示腐蚀后的图像
	cvNamedWindow("Eroded Image");
	imshow("Eroded Image", eroded);

	//膨胀图像
	Mat dilated;	//目标图像
	dilate(g_pBinaryImage, dilated, Mat());
	//显示膨胀后的图像
	cvNamedWindow("Dilated Image");
	imshow("Dilated Image", dilated);

	waitKey(0);
	system("pause");
	return 0;
}

运行效果

二、形态学滤波对图像进行开闭运算

1.概念及原理

(1)闭运算定义为对图像进行先膨胀再腐蚀,开运算定义为对图像进行先腐蚀再膨胀

(2)闭滤波器可以填充白色前景物中的小洞,开滤波器可以移除场景中比较小的物体。这些滤波器通常在物体检测中使用,闭滤波器可以将误分割成碎片的物体重新连接,而开滤波器可以处理图像噪点引起的小像素块。

2.实验

通过合适的参数调用cv::morphologyEx 使用更高级的形态学滤波。

源码示例

#include<iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv/cv.h>

using namespace std;
using namespace cv;

Mat g_pGrayImage;
Mat g_pBinaryImage;

int main(){

	// 从文件中加载原图
	Mat pSrcImage = cvLoadImage("horse.jpg", CV_LOAD_IMAGE_UNCHANGED);
	// 转为灰度图
	g_pGrayImage.create(pSrcImage.rows, pSrcImage.cols, CV_8U);
	cvtColor(pSrcImage, g_pGrayImage, CV_BGR2GRAY);
	// 创建二值图
	g_pBinaryImage.create(g_pGrayImage.rows, g_pGrayImage.cols, CV_8U);
	// 转为二值图
	threshold(g_pGrayImage, g_pBinaryImage, 100, 255, CV_THRESH_BINARY);
	//显示二值图像
	cvNamedWindow("BinaryImage");
	imshow("BinaryImage", g_pBinaryImage);

	//闭运算图像,使用5*5的结构元素使得滤波效果更明显
	Mat element5(5, 5, CV_8U, Scalar(1));
	Mat closed;
	morphologyEx(g_pBinaryImage, closed, MORPH_CLOSE, element5);

	//显示闭运算后的图像
	cvNamedWindow("闭运算图像");
	imshow("闭运算图像", closed);

	//开运算图像
	Mat opened;	//目标图像
	morphologyEx(g_pBinaryImage, opened, MORPH_OPEN, element5);
	//显示开运算后的图像
	cvNamedWindow("开运算图像");
	imshow("开运算图像", opened);

	waitKey(0);
	system("pause");
	return 0;
}

程序运行后的效果

三、形态学滤波对图像进行边缘及角点检测

实验一

检测灰度图中的直线。

源码示例

#include<iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv/cv.h>

using namespace std;
using namespace cv;

class MorphoFeatures{

private:
	//用于生成二值图像的阈值
	int ithreshold;
	//角点检测中用到的结构元素
	Mat cross;
	Mat diamond;
	Mat square;
	Mat x;
	void applyThreshold(Mat &result);
public:
	Mat getEdges(const Mat &image);
	void setThreshold(float t);
};

//获取二值的边缘图像
void MorphoFeatures::applyThreshold(Mat &result){

	//使用阈值化
	if (ithreshold > 0)
	{
		threshold(result, result, ithreshold, 255, THRESH_BINARY);
	}
}

//morphologyEx + 合适的滤波器实现直线的检测
Mat MorphoFeatures::getEdges(const Mat &image){

	//得到梯度图
	Mat result;
	morphologyEx(image, result, MORPH_GRADIENT, Mat());
	//阈值化以得到二值图像
	applyThreshold(result);
	return result;
}

//设置直方图的阈值[0,1]
void MorphoFeatures::setThreshold(float t){

	ithreshold = t;
}

int main(){

	Mat image = cvLoadImage("floor.jpg",0);
	MorphoFeatures morpho;
	morpho.setThreshold(80);
	//获取边缘
	Mat edges;
	edges = morpho.getEdges(image);

	namedWindow("floor");
	imshow("floor",image);
	namedWindow("edges");
	imshow("edges", edges);
	waitKey(0);
	system("pause");
	return 0;
}

检测后的效果

实验二

检测灰度图中的角点

角点检测使用四种不同的结构元素检测图像角点,分别为十字型、菱型、x型和方形元素,尺寸规定为5*5。与边缘检测不同,角点的检测复杂。运算过程主要分三步:

第一步,先用十字形的结构元素膨胀原图像,这种情况下只会在边缘处“扩张”,角点不发生变化。接着用菱形的结构元素腐蚀原图像,只有拐角处才会被“收缩”,而直线边缘不发生变化。

第二步,用X型的结构元素膨胀原图像,角点膨胀的比边要多。这样第二次用方块腐蚀时,角点恢复原状,而边要腐蚀的更多。

第三步,将一二步的两幅输出图像相减,结果只保留了各个拐角处的细节。

源码示例

#include<iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv/cv.h>

using namespace std;
using namespace cv;
class MorphoFeatures{

private:
	//用于生成二值图像的阈值
	int ithreshold;
	//角点检测中用到的结构元素
	Mat cross;
	Mat diamond;
	Mat square;
	Mat x;
	void applyThreshold(Mat &result);
public:
	MorphoFeatures():ithreshold(50), cross(5, 5, CV_8U, Scalar(0)), diamond(5, 5, CV_8U, Scalar(1)), square(5, 5, CV_8U, Scalar(1)),x(5,5,CV_8U,Scalar(0)){

		//创建十字形元素
		for (int i = 0; i < 5; i++)
		{
			cross.at<uchar>(2, i) = 1;
			cross.at<uchar>(i, 2) = 1;
		}

		//创建菱形元素
		diamond.at<uchar>(0, 0) = 0;
		diamond.at<uchar>(0, 1) = 0;
		diamond.at<uchar>(1, 0) = 0;
		diamond.at<uchar>(4, 4) = 0;
		diamond.at<uchar>(3, 4) = 0;
		diamond.at<uchar>(4, 3) = 0;
		diamond.at<uchar>(4, 0) = 0;
		diamond.at<uchar>(4, 1) = 0;
		diamond.at<uchar>(3, 0) = 0;
		diamond.at<uchar>(0, 4) = 0;
		diamond.at<uchar>(0, 3) = 0;
		diamond.at<uchar>(1, 4) = 0;

		//创建x形元素
		for (int i = 0; i < 5; i++)
		{
			x.at<uchar>(i, i) = 1;
			x.at<uchar>(4-i, i) = 1;
		}

	}

	Mat getEdges(const Mat &image);
	void setThreshold(float t);
	Mat getCorners(const Mat&image);
	void drawOnImage(const Mat &binary, Mat &image);
};

//获取二值的边缘图像
void MorphoFeatures::applyThreshold(Mat &result){

	//使用阈值化
	if (ithreshold > 0)
	{
		threshold(result, result, ithreshold, 255, THRESH_BINARY);
	}
}

//morphologyEx + 合适的滤波器实现直线的检测
Mat MorphoFeatures::getEdges(const Mat &image){

	//得到梯度图
	Mat result;
	morphologyEx(image, result, MORPH_GRADIENT, Mat());
	//阈值化以得到二值图像
	applyThreshold(result);
	return result;
}

//设置直方图的阈值[0,1]
void MorphoFeatures::setThreshold(float t){

	ithreshold = t;
}

//连接使用这些结构以得到最终的角点映射图
Mat MorphoFeatures::getCorners(const Mat&image){

	Mat result;
	//十字形膨胀
	dilate(image, result, cross);
	//菱形腐蚀
	erode(result, result, diamond);
	Mat result2;
	//X形膨胀
	dilate(image, result2, x);
	//方形腐蚀
	erode(result2, result2, square);
	//通过对两张图像做差值得到角点图像
	absdiff(result2, result, result);
	//阈值化以得到二值图像
	applyThreshold(result);

	return result;
}

//在二值图像中的每个检测点上绘制一个圆  更好的可视化结果
void MorphoFeatures::drawOnImage(const Mat &binary, Mat &image){

	Mat_<uchar>::const_iterator it = binary.begin<uchar>();
	Mat_<uchar>::const_iterator itend = binary.end<uchar>();

	//遍历每个像素
	for (int i = 0; it != itend; ++it, ++i)
	{
		if (*it)
			circle(image, Point(i%image.step, i/image.step), 5, Scalar(255, 0, 0));
	}
}

int main(){

	Mat image = cvLoadImage("floor.jpg");
	Mat grayImage;
	cvtColor(image, grayImage,CV_BGR2GRAY);

	//显示原图
	namedWindow("Image");
	imshow("Image", grayImage);

	MorphoFeatures morpho;
	//得到角点
	Mat corners;
	corners = morpho.getCorners(grayImage);
	//在图像中显示角点
	morpho.drawOnImage(corners, grayImage);

	namedWindow("Corners on Image");
	imshow("Corners on Image", grayImage);

	waitKey(0);
	return 0;
}

角点检测效果图

这里需要注意由于要把输入图像转化为二值图像,因此阈值的选择会影响角点检测效果,在此可以输入不同的阈值查看角点检测效果。

四、分水岭算法对图像进行分割

1.概念及原理

(1)分水岭分割方法是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明。在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。

实验

实验主要分为

1.对二值图像进行反转,因为我们一般用255白色表示前景 0黑色表示背景

2.有二值图腐蚀移除噪点和微小物体 获得前景

3.膨胀二值图获取背景

4.前景与背景相加得到markers标记图像

5.设置markers标记进行分水岭分割

源码示例

#include<iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv/cv.h>

using namespace std;
using namespace cv;

class WatershedSegmenter{

private:
	//用来表示标记(图)
	Mat markers;
public:
	//设置标记图
	void setMarkers(const Mat&makerImage);
	Mat process(const Mat&image);
	Mat getSegmentation();
	Mat getWatersheds();
};

void WatershedSegmenter::setMarkers(const Mat&makerImage){
	//转换为整数图像 watershed()的输入参数必须为一个32位有符号的标记,所以要先进行转换
	makerImage.convertTo(markers, CV_32S);
}

Mat WatershedSegmenter::process(const Mat&image){
	//使用算法
	watershed(image, markers);
	return markers;
}

//以图像形式返回结果
Mat WatershedSegmenter::getSegmentation(){

	Mat temp;
	//从32S到8U(0-255)会进行饱和运算,所以像素高于255的一律复制为255
	markers.convertTo(temp, CV_8U);
	return temp;
}

//以图像的形式返回分水岭 分割线
Mat WatershedSegmenter::getWatersheds(){

	Mat temp;
	//在设置标记图像,即执行setMarkers()后,边缘的像素会被赋值为-1,其他的用正整数表示
	//下面的这个转换可以让边缘像素变为-1*255+255=0,即黑色,其余的溢出,赋值为255,即白色
	markers.convertTo(temp, CV_8U, 255, 255);
	return temp;
}

int main(){

	Mat image = cvLoadImage("group.jpg");
	Mat grayImage;
	cvtColor(image, grayImage, CV_BGR2GRAY);
	//转换为二值图
	Mat binaryImage;
	threshold(grayImage, binaryImage, 80,255, CV_THRESH_BINARY);

	// 1.二值图 这里进行了像素反转,因为一般我们用255白色表示前景(物体),用0黑色表示背景
	Mat reverseBinaryImage;
	bitwise_not(binaryImage,reverseBinaryImage);

	namedWindow("reverseBinaryImage");
	imshow("reverseBinaryImage", reverseBinaryImage);
	//2.由二值图像获得前景 腐蚀 移除噪点与微小物体
	Mat fg;
	erode(reverseBinaryImage, fg, Mat(), Point(-1, -1), 6);

	//3.识别不包含物体的背景 膨胀二值图来获取背景(只有草地,没有树林)
	Mat bg;
	dilate(reverseBinaryImage, bg, Mat(), Point(-1, -1), 6);
	threshold(bg, bg, 1, 128, THRESH_BINARY_INV);

	//4.组合这些图形成标记图形
	Mat markers(binaryImage.size(), CV_8U, Scalar(0));
	markers = fg + bg; //使用重载操作符+

	//5.创建分水岭分割对象
	WatershedSegmenter segmenter;
	//设置标记并进行处理
	segmenter.setMarkers(markers);
	segmenter.process(image);

	namedWindow("Segmentation");
	imshow("Segmentation", segmenter.getSegmentation());

	namedWindow("Watersheds");
	imshow("Watersheds", segmenter.getWatersheds());

	waitKey(0);
	return 0;
}

实验过程中的一些图片

原图和像素反转二值图

fg前景图和bg背景图

标记图像和边界图像

五、GrabCut算法提取前景物体

源码示例

    #include<opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<imgproc/imgproc.hpp>
    #include<iostream>  

    using namespace std;
    using namespace cv;  

    int main()
    {
        Mat image=imread("d:/test/opencv/group.jpg");
        namedWindow("Image");
        imshow("Image",image);
        Rect rectangle(10,100,380,180);
        Mat result;
        Mat bgModel,fgModel;
        grabCut(image,result,rectangle,bgModel,fgModel,5,GC_INIT_WITH_RECT);
        compare(result,GC_PR_FGD,result,CMP_EQ);
        Mat foreground(image.size(),CV_8UC3,Scalar(255,255,255));
        image.copyTo(foreground,result);
        namedWindow("Foreground");
        imshow("Foreground",foreground);
        waitKey();
        return 0;
    }  

提取到前景效果图

时间: 2024-11-10 01:13:14

【OpenCV学习笔记 008】基于形态学运算的图像变换的相关文章

OpenCV 学习笔记(模板匹配)

OpenCV 学习笔记(模板匹配) 模板匹配是在一幅图像中寻找一个特定目标的方法之一.这种方法的原理非常简单,遍历图像中的每一个可能的位置,比较各处与模板是否"相似",当相似度足够高时,就认为找到了我们的目标. 在 OpenCV 中,提供了相应的函数完成这个操作. matchTemplate 函数:在模板和输入图像之间寻找匹配,获得匹配结果图像 minMaxLoc 函数:在给定的矩阵中寻找最大和最小值,并给出它们的位置 在具体介绍这两个函数之前呢,我们还要介绍一个概念,就是如何来评价两

OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波

http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 2013-03-23 17:44 16963人阅读 评论(28) 收藏 举报 分类: 机器视觉(34) 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] KAZE系列笔记: OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 OpenCV学习笔记(28)KA

Opencv学习笔记(六)SURF学习笔记

原创文章,转载请注明出处:http://blog.csdn.net/crzy_sparrow/article/details/7392345 本人挺菜的,肯定有非常多错误纰漏之处 ,希望大家不吝指正. 看了harris角点检測之后,開始研究SURF角点检測,发现挺复杂的,一时也仅仅了解了大概,把了解的东西总结下,以便下次深入学习. SURF角点检測算法是对SIFT的一种改进,主要体如今速度上,效率更高.它和SIFT的主要差别是图像多尺度空间的构建方法不同. 在计算视觉领域,尺度空间被象征性的表述

opencv学习笔记(四)投影

opencv学习笔记(四)投影 任选了一张图片用于测试,图片如下所示: 1 #include <cv.h> 2 #include <highgui.h> 3 using namespace std; 4 using namespace cv; 5 int main() 6 { 7 IplImage * src = cvLoadImage("cat.png", 0); //强制转化读取图像为灰度图 8 cvShowImage("灰度图像", s

OpenCV学习笔记(01)我的第一个OpenCV程序(环境配置)

昨天刚刚考完编译原理,私心想着可以做一些与考试无关的东西了.一直想做和图像处理相关的东西,趁这段时间有空学习一下OpenCV,搭建环境真是一件麻烦的事情,搞了近三个小时终于OK了.先来张图: 大致描述一下步骤吧: 一.安装前准备 1.VS2012(网上看到很多用的VS2010,但是基本不影响) 2.OpenCV 安装包(我下载的是最新的2.4.9) 二.安装OpenCV 1.解压OPenCV 说是安装,其实就是解压,OpenCV的Windows安装程序就是一个自解压程序: 这里我解压到C:\Pr

opencv学习笔记(03)——遍历图像(迭代器法)

1 #include <opencv2\highgui\highgui.hpp> 2 #include <opencv2\imgproc\imgproc.hpp> 3 #include <opencv2\core\core.hpp> 4 5 void colorReduce(cv::Mat& img, int div=64); 6 7 8 int main() 9 { 10 cv::Mat img_orginal = cv::imread("F:\\i

OpenCV学习笔记[3]Java Demo人脸识别

OpenCV学习笔记:Java Demo人脸识别 [简介] 我记得在很久以前,CSDN似乎搞过一个活动,给一个橘子林的照片,让程序计算相片里有多少个橘子.之所以对这个问题记忆犹新,是因为在专业学习初期,相比于排序遍历搜索等简单算法而言,"图像识别"算法一直是难以理解的东西,而我偏偏又痴迷于此,不管自己多么无知,对于令我迷惑的问题总是充满着解决的渴望. 通过对OpenCV的初步了解,我发现图像识别的很多问题都可以用它方便的解决,本次将是一个来自官方的人脸识别的实例,我们提供图像,使用内置

蛋哥的学习笔记之-基于Unity的Shader编程:X-1 音乐水波特效

蛋哥的学习笔记之-基于Unity的Shader编程:X-1 音乐水波特效 热度 13728 2015-7-11 23:34 |个人分类:蛋哥的学习笔记之-基于Unity的Shader编程| 音乐, Unity, Shader, 水波, Shader, Shader, Shader, Shader 一.要干些啥: 很久很久没有写文档了,前段时间做了个个人很喜欢的,自认为比较原创的小特效,所以写个文档纪念下(本人特别喜欢音乐) 思路其实很简单,首先用顶点着色器实现一般的水波特效,然后解析音频数据(我

nginx学习笔记之基于端口的虚拟主机基于主机名的虚拟主机root、alias、index配置

nginx学习笔记之基于端口的虚拟主机基于主机名的虚拟主机root.alias.index配置 实验环境: centos 测试节点IP:172.16.3.101 基于端口的虚拟主机: vim /etc/nginx/nginx.conf # 向里面的http {}里面加入如下内容   server { # server定义一个虚拟主机         listen 8080; # 监听本机所有IP端口8080         server_name www.test.com; # 虚拟主机名为:w