[OpenCV4] 湖南大学数字图像处理实验7

 

实验内容

1)实现基于阈值法对图像分割

2)实现区域生长方法对图像分割

3)实现任一种不同于以上的分割算法实现图像分割

运行效果

图像灰度读入后:

 

Opencv提供了多种阈值宏,可以方便地调用:

 

4.1 THRESH_BINARY 

最常见的作法,设置一个阈值,大于的时候取最大值(maxval是threshold的参数),否则取0。

 

4.2 THRESH_BINARY_INV 

与THRESH_BINARY相反,大于阈值取0,否则取最大值,maxval对于THRESH_BINARY和THRESH_BINARY_INV有意义。

 

4.3 THRESH_TRUNC 

截断型,大于阈值的统一将为阈值,其余不变。

 

4.4 THRESH_TOZERO 

过滤型,大于阈值的保持不变,其余设置为0。

 

4.5 THRESH_TOZERO_INV 

大于阈值0,其他不变。

 

4.6 THRESH_MASK 

这个不知道是什么,也没有解释

 

4.7 THRESH_OTSU 

大津阈值

 

4.8 THRESH_TRIANGLE 

矩形阈值

 

分别使用不同的threshold的mode与不同的阈值组合进行图像分割:

 

 

 

 

 

 

下面是基于区域生长算法对应地分割的效果图:

 

 

首先输入源图像:

 

进行背景转换:

 

 

对图像进行锐化处理:

 

对锐化后的图像进行二值化:

 

 

应用距离变换算法:

 

使用WaterShed算法得到的极值点的区域

 

 

 

最终得到的分割结果:

对于区域生长的方法:

 

可以看到生成的随机数的位置和最终生长出的区域有间断的情况,因为只用了一个随机点,效果不是很好,所以接着做了改进,修改为两个随机点,图片的左边和右边各一个,修改后的效果:

可以看到,此时使用两个随机点,效果比较令人满意。

项目源码

DigitalImageExp7.cpp

// DigitalImageExp7.cpp 

#include "thresholdBasedSegmentation.h"
#include "otherSegmentationMethods.h"
#include "regionGrowingBasedSegmentation.h"

int main()
{
	String imageName("../media/cat.jpg"); // by default
	Mat src = imread(samples::findFile(imageName), IMREAD_COLOR);
	if (src.empty()) {
		cout << "OPEN ERROR" << endl;
		exit(-1);
	}

	// Use the threshold base segmentation method
	thresholdBasedSegmentation(src);
	// Call the distance transformation and Watershed method
	otherSegmentationTransformation(src);
	// Use the region growing segmentation method
	regionGrowingBasedSegmentation(src, 50);

	return 0;
}

regionGrowingBasedSegmtation.h 

#pragma once

#include <opencv2/opencv.hpp>
#include <iostream>
#include <stack>

using namespace cv;
using namespace std;

void regionGrowingBasedSegmentation(Mat src, int threshold);

regionGrowingBasedSegmtation.cpp

#include "regionGrowingBasedSegmentation.h"
#define DEBUG

static int rInt(int a, int b) {
	int temp = int(double(rand()) / RAND_MAX * (b - a) + a);
	return temp;
}

// th: threshold: determne when the connection of two neighbor pixel
void regionGrowingBasedSegmentation(Mat para_src, int th)
{
	String imageName("../media/cat.jpg"); // by default
	Mat src_pre, src;
	if (para_src.empty())
	{
		// Load the default image
		src_pre = imread(samples::findFile(imageName), IMREAD_GRAYSCALE);
	}
	else {
		src_pre = para_src.clone();
	}
	// Convert the image to Gray
	cvtColor(src_pre, src, COLOR_BGR2GRAY);
	int randX1 = rInt(src.rows/2, src.rows - 1);
	int randY1 = rInt(src.cols/2, src.cols - 1);
	int randX2 = rInt(10, src.rows / 2);
	int randY2 = rInt(10, src.cols / 2);

#ifdef DEBUG
	cout << "src.rows = " << src.rows <<
		"\nsrc.cols = " << src.cols << endl;
	cout << "randX1 = " << randX1 << endl
		<< "randY1 = " << randY1 << endl;
	cout << "randX2 = " << randX2 << endl
		<< "randY2 = " << randY2 << endl;
#endif // DEBUG

	
	Point2i pt;
	Point2i ptGrowing;						        // The point to grow
	int nGrowLable = 0;								// Check if it has been growed before
	int nSrcValue = 0;								// The growing point value 
	int nCurValue = 0;								// The current pixel's value
	Mat matDst = Mat::zeros(src.size(), CV_8UC1);	// Create a new region filled with black
	// Set the growing direction
	int DIR[8][2] = { {-1,-1}, {0,-1}, {1,-1}, {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0} };
	stack<Point2i> vcGrowPt;						// Create a gorwing point stack
	

	for (int times = 0 ; times < 2 ; ++times) {
		if (times == 0) {
			pt = Point2i(randX1, randY1);
		}
		else {
			pt = Point2i(randX2, randY2);
		}
		vcGrowPt.push(pt);							// Push the growing point into the stack
		matDst.at<uchar>(pt.y, pt.x) = 255;				// Mark the growing point
		nSrcValue = src.at<uchar>(pt.y, pt.x);			// Update the growing point's gray scale value

		while (!vcGrowPt.empty())					// Determine when to end the process
		{
			pt = vcGrowPt.top();						// Get one growing point out
			vcGrowPt.pop();

			// Try to connect the 8 neighbor pixels
			for (int i = 0; i < 8; ++i)
			{
				// Set the next point to grow
				ptGrowing.x = pt.x + DIR[i][0];
				ptGrowing.y = pt.y + DIR[i][1];

				// Check the boundaries
				if (ptGrowing.x < 0 || ptGrowing.y < 0 ||
					ptGrowing.x >(src.cols - 1) || (ptGrowing.y > src.rows - 1))
					continue;

				// Get the grayScale value of the current pixel
				nGrowLable = matDst.at<uchar>(ptGrowing.y, ptGrowing.x);

				// The pixels has not been accessed before
				if (nGrowLable == 0)
				{
					nCurValue = src.at<uchar>(ptGrowing.y, ptGrowing.x);
					if (abs(nSrcValue - nCurValue) < th)					// If the pixel grayscale value is close to threshold enough
					{
						matDst.at<uchar>(ptGrowing.y, ptGrowing.x) = 255;		// set the pixel's grayscale to 255
						vcGrowPt.push(ptGrowing);					// Continue the BFS process
					}
				}
			}
		}
	}
	imshow("GROWING RESULT", matDst);
	waitKey();
}

otherSegmentationMethods.h 

#pragma once
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int otherSegmentationTransformation(const Mat & para_src);

otherSegmentationMethods.cpp 

#include "otherSegmentationMethods.h"

int otherSegmentationTransformation(const Mat & para_src)
{
	String imageName("../media/cat.jpg"); // by default
	Mat src;
	if (para_src.empty())
	{
		// Load the default image
		src = imread(samples::findFile(imageName), IMREAD_COLOR);
	}
	else {
		src = para_src.clone();
	}
	// Show source image
	imshow("Source Image", src);
	// Change the background from white to black, since that will help later to extract
	// better results during the use of Distance Transform
	for (int i = 0; i < src.rows; i++) {
		for (int j = 0; j < src.cols; j++) {
			if (src.at<Vec3b>(i, j) == Vec3b(255, 255, 255))
			{
				src.at<Vec3b>(i, j)[0] = 0;
				src.at<Vec3b>(i, j)[1] = 0;
				src.at<Vec3b>(i, j)[2] = 0;
			}
		}
	}
	// Show output image
	imshow("Black Background Image", src);
	// Create a kernel that we will use to sharpen our image
	Mat kernel = (Mat_<float>(3, 3) <<
		1, 1, 1,
		1, -8, 1,
		1, 1, 1); // an approximation of second derivative, a quite strong kernel
// do the laplacian filtering as it is
// well, we need to convert everything in something more deeper then CV_8U
// because the kernel has some negative values,
// and we can expect in general to have a Laplacian image with negative values
// BUT a 8bits unsigned int (the one we are working with) can contain values from 0 to 255
// so the possible negative number will be truncated
	Mat imgLaplacian;
	filter2D(src, imgLaplacian, CV_32F, kernel);
	Mat sharp;
	src.convertTo(sharp, CV_32F);
	Mat imgResult = sharp - imgLaplacian;
	// convert back to 8bits gray scale
	imgResult.convertTo(imgResult, CV_8UC3);
	imgLaplacian.convertTo(imgLaplacian, CV_8UC3);
	// imshow( "Laplace Filtered Image", imgLaplacian );
	imshow("New Sharped Image", imgResult);
	// Create binary image from source image
	Mat bw;
	cvtColor(imgResult, bw, COLOR_BGR2GRAY);
	threshold(bw, bw, 40, 255, THRESH_BINARY | THRESH_OTSU);
	imshow("Binary Image", bw);
	// Perform the distance transform algorithm
	Mat dist;
	distanceTransform(bw, dist, DIST_L2, 3);
	// Normalize the distance image for range = {0.0, 1.0}
	// so we can visualize and threshold it
	normalize(dist, dist, 0, 1.0, NORM_MINMAX);
	imshow("Distance Transform Image", dist);
	// Threshold to obtain the peaks
	// This will be the markers for the foreground objects
	threshold(dist, dist, 0.4, 1.0, THRESH_BINARY);
	// Dilate a bit the dist image
	Mat kernel1 = Mat::ones(3, 3, CV_8U);
	dilate(dist, dist, kernel1);
	imshow("Peaks", dist);
	// Create the CV_8U version of the distance image
	// It is needed for findContours()
	Mat dist_8u;
	dist.convertTo(dist_8u, CV_8U);
	// Find total markers
	vector<vector<Point> > contours;
	findContours(dist_8u, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	// Create the marker image for the watershed algorithm
	Mat markers = Mat::zeros(dist.size(), CV_32S);
	// Draw the foreground markers
	for (size_t i = 0; i < contours.size(); i++)
	{
		drawContours(markers, contours, static_cast<int>(i), Scalar(static_cast<int>(i) + 1), -1);
	}
	// Draw the background marker
	circle(markers, Point(5, 5), 3, Scalar(255), -1);
	// Something went wrong with the following line code
	// So I just made it a line of comment
	// imshow("Markers", markers * 10000);
	// Perform the watershed algorithm
	watershed(imgResult, markers);
	Mat mark;
	markers.convertTo(mark, CV_8U);
	bitwise_not(mark, mark);
	//    imshow("Markers_v2", mark); // uncomment this if you want to see how the mark
	// image looks like at that point
	// Generate random colors
	vector<Vec3b> colors;
	for (size_t i = 0; i < contours.size(); i++)
	{
		int b = theRNG().uniform(0, 256);
		int g = theRNG().uniform(0, 256);
		int r = theRNG().uniform(0, 256);
		colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
	}
	// Create the result image
	Mat dst = Mat::zeros(markers.size(), CV_8UC3);
	// Fill labeled objects with random colors
	for (int i = 0; i < markers.rows; i++)
	{
		for (int j = 0; j < markers.cols; j++)
		{
			int index = markers.at<int>(i, j);
			if (index > 0 && index <= static_cast<int>(contours.size()))
			{
				dst.at<Vec3b>(i, j) = colors[index - 1];
			}
		}
	}
	// Visualize the final image
	imshow("Final Result", dst);
	waitKey();
	return 0;
}

thresholdBasedSegmentation.cpp 

// thresholdBasedSegmentation.cpp

#include "thresholdBasedSegmentation.h"

static int threshold_value = 0;
static int threshold_type = 3;
static int const max_value = 255;
static int const max_type = 4;
static int const max_binary_value = 255;
static Mat src, src_gray, dst;
static const char* window_name = "Threshold Demo";
static const char* trackbar_type = "Type: \n 0: Binary \n 1: Binary Inverted \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted";
static const char* trackbar_value = "Value";

// Define the callback function
static void Threshold_Demo(int, void*)
{
	/* 0: Binary
	 1: Binary Inverted
	 2: Threshold Truncated
	 3: Threshold to Zero
	 4: Threshold to Zero Inverted
	*/
	threshold(src_gray, dst, threshold_value, max_binary_value, threshold_type);
	imshow(window_name, dst);
}

int thresholdBasedSegmentation(const Mat & para_src)
{
	String imageName("../media/cat.jpg"); // by default
	Mat src;
	if (para_src.empty())
	{
		// Load the default image
		src = imread(samples::findFile(imageName), IMREAD_COLOR); 
	}
	else {
		src = para_src.clone();
	}
	// Convert the image to Gray
	cvtColor(src, src_gray, COLOR_BGR2GRAY); 
	// Create a window to display results
	namedWindow(window_name, WINDOW_AUTOSIZE); 
	// Create a Trackbar to choose type of Threshold
	createTrackbar(trackbar_type,
		window_name, &threshold_type,
		max_type, Threshold_Demo); 
	// Create a Trackbar to choose Threshold value
	createTrackbar(trackbar_value,
		window_name, &threshold_value,
		max_value, Threshold_Demo); 
	// Call the callback function to initialize
	Threshold_Demo(0, 0);
	waitKey();
	return 0;
}

thresholbasedSegmentation.h

#pragma once

#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int thresholdBasedSegmentation( const Mat & para_src );

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值