OpenCV--FCN实现图像切割(附源码)

 环境:VS2017+OpenCV3.3+C++

     什么是图像切割?在一幅图像中,如果我们只对其中的部分目标感兴趣,这些目标通常占据一定的区域,并且在某些特性(如灰度、轮廓、颜色和纹理等)上和临近的图像有差别。这些特性差别可能非常明显,也可能很细微,以至肉眼察觉不出来。随着计算机图像处理技术的发展,我们可以通过计算机来获取和处理图像信息。图像识别的基础是图像分割,其作用是把反映物体真实情况的、占据不同区域的、具有不同特性的目标区分开来,并形成数字特征。图像分割是图像识别和图像理解的基本前提步骤,图像分割质量的好坏直接影响后续图像处理的效果,甚至决定其成败,因此,图像分割的作用是至关重要的。

     今天我来说下,如何使用FCN实现图像切割,模型下载,我们一步一步来实现这个功能。

1、头文件引入

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

2、模型文件位置

String fcn_label_txt = "D:/new_cv/opencv/sources/samples/data/dnn/pascal-classes.txt";
String fcn_txt = "D:/new_cv/opencv/sources/samples/data/dnn/fcn8s-heavy-pascal.prototxt";
String fcn_model = "D:/new_cv/opencv/sources/samples/data/dnn/fcn8s-heavy-pascal.caffemodel";

3、读入label值

    对于图像分割和常规的检测是不一样的,他是对输入图像全局的每一个像素进行的识别,也就是说每一个像素点都应该有一个对应的label和value值,如果你能明白这个思想下面就好办了,我们读取label值内容如下:

background 0 0 0
aeroplane 128 0 0
bicycle 0 128 0
bird 128 128 0
boat 0 0 128
bottle 128 0 128
bus 0 128 128
car 128 128 128
cat 64 0 0
chair 192 0 0
cow 64 128 0
diningtable 192 128 0
dog 64 0 128
horse 192 0 128
motorbike 64 128 128
person 192 128 128
pottedplant 0 64 0
sheep 128 64 0
sofa 0 192 0
train 128 192 0
tvmonitor 0 64 128

    我们可以看到每个值都是三通道的,如果是之前的检测结果,应该用vector<String>存储,换成通道value,我们用vector<Vec3b>来存储,方法如下

vector<Vec3b> readColors()
{
	vector<Vec3b> colors;
	ifstream fp(fcn_label_txt);//创建流
	if (!fp.is_open())
	{
		cout << "can not open label file" << endl;
		exit(-1);
	}
	string line;
	while (!fp.eof())//如果流没有结束
	{
		getline(fp, line);
		if (line.length())//判断是否可以打开
		{
			stringstream ss(line);//创建字符流
			string name;
			ss >> name;
		//	cout << name << endl;;
			int temp;
			Vec3b color;
			ss >> temp;
			cout << temp;
			color[0] = (uchar)temp;
			ss >> temp;
			color[1] = (uchar)temp;
			ss >> temp;
			color[2] = (uchar)temp;
	//		cout << temp << endl;
			colors.push_back(color);
		}
	}
	return colors;
}

4、图片处理与模型初始化

	Mat src = imread("D:/test/test.jpg");
	if (src.empty())
	{
		cout << "imput img is empty" << endl;
		return -1;
	}
	//resize
	resize(src, src, Size(500, 500));
	imshow("src", src);
	vector<Vec3b> labels = readColors();
	Mat blobimg = blobFromImage(src);


	Net net = readNetFromCaffe(fcn_txt,fcn_model);
	if (net.empty())
	{
		cout << "init net error";
		return -1;
	}

	float time = getTickCount(); //计时器
	cout << time;
	net.setInput(blobimg, "data");
	Mat score = net.forward("score");
	float tt = getTickCount() - time;
	cout << "time consume: " << (tt / getTickFrequency() * 1000) << endl;

5、定义查找表

    为什么要生成查找表的?从结果角度来说,我们要知道输出图像,每一个像素属于哪个通道(21个label的坐标)以及对应的value值是多少,所以我们要声明一个对应的查找表,后续来做map

	cout << score.size <<" "<< score.size[1]<<" " << score.size[2]<<" " << score.size[3] << endl;
// 21*500*500
	const int rows = score.size[2];//height
	const int cols = score.size[3];//width
	const int chls = score.size[1];//channels

	Mat maxC1(rows, cols, CV_8UC1); //存储最终选定的通道(21分之一)
	Mat MaxVal(rows, cols, CV_32FC1);//存储label的key

  接下来是一个分割算法当中比较绕的地方了那就是查找表的生成

//我们要遍历的score 是数据结构为 21个通道,每个通道是500*500像素的结果
	//set 查找表
	for (int c = 0; c < chls; c++) //一共21个通道代表21个通道在每个下像素点的置信度
	{
		for (int row = 0; row < rows; row++)//500行
		{
			const float *ptrScore = score.ptr<float>(0, c, row);//指向每个通道的第1个位置,存储的是21个label的置信度 
		//	cout << *ptrScore << endl;
			uchar *ptrMaxC1 = maxC1.ptr<uchar>(row); //为了
			float *ptrMaxVal = MaxVal.ptr<float>(row);
			for(int col = 0; col < cols; col++) //500列
			{
				if (ptrScore[col] > ptrMaxVal[col])  //21个通道的同一个位置取最大值
				{
					ptrMaxVal[col] = ptrScore[col]; //存储最终的值也就是color帐的结果
					ptrMaxC1[col] = (uchar)c;//存储最终选择的通道
				}
			}
		}
	}

6、输出结果 

    最后我们就可以声明一个500*500的zero Mat对象,依次map查找表中的位置,输出结果:

	Mat result = Mat::zeros(rows, cols, CV_8UC3);//初始化全0的图像矩阵
	for (int row = 0; row < rows; row++)
	{
		const uchar *ptrMaxC1 = maxC1.ptr<uchar>(row); //指向对应的查找表中的行
		Vec3b *ptrColor = result.ptr<Vec3b>(row);//指向查找表中的行
		for (int col = 0; col < cols; col++)
		{
			ptrColor[col] = labels[ptrMaxC1[col]];//对应位置
		}
	}
	imshow("result", result);

结果如下: 哈哈哈哈哈哈哈^_^,猜猜原图是什么?

     坚持一件事情或许很难,但坚持下来一定很酷!^_^

源代码:https://github.com/haiqiang2017/open-dnn/blob/master/DNN/fcn.cpp

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值