立体匹配——census算法

参考文章:https://blog.csdn.net/MyStylee/article/details/78491271

Census原理:
在视图中选取任一点,以该点为中心划出一个例如3 × 3的矩形,矩形中除中心点之外的每一点都与中心点进行比较,灰度值小于中心点记为1,灰度大于中心点的则记为0,以所得长度为8的只有0和1的序列作为该中心点的census序列,即中心像素的灰度值被census 序列替换。经过census变换后的图像使用汉明距离计算相似度,所谓图像匹配就是在匹配图像中找出与参考像素点相似度最高的点,而汉明距正是匹配图像像素与参考像素相似度的度量。具体而言,对于欲求取视差的左右视图,要比较两个视图中两点的相似度,可将此两点的census值逐位进行异或运算,计算结果为1的个数,记1的个数作为此两点之间的汉明值。汉明值是两点间相似度的一种体现,汉明值愈小,两点相似度愈大实现算法时先异或再统计1的个数即可,汉明距越小即相似度越高。
立体匹配——census算法_第1张图片
代码实现:

#if 1

//*************************Census*********************
#include 
#include 
#include 
#include 

using namespace std;
using namespace cv;

//-------------------定义汉明距离----------------------------
int disparity;
int GetHammingWeight(uchar value);//求1的个数

//-------------------定义Census处理图像函数---------------------
int hWind = 1;//定义窗口大小为(2*hWind+1)
Mat ProcessImg(Mat &Img);//将矩形内的像素与中心像素相比较,将结果存于中心像素中
Mat Img_census, Left_census, Right_census;

//--------------------得到Disparity图像------------------------
Mat getDisparity(Mat &left, Mat &right);

//--------------------处理Disparity图像-----------------------
Mat ProcessDisparity(Mat &disImg);

int ImgHeight, ImgWidth;

//int num = 0;//异或得到的海明距离
Mat LeftImg, RightImg;
Mat DisparityImg(ImgHeight, ImgWidth, CV_8UC1, Scalar::all(0));
Mat DisparityImg_Processed(ImgHeight, ImgWidth, CV_8UC1, Scalar::all(0));
Mat DisparityImg_Processed_2(ImgHeight, ImgWidth, CV_8UC1);


int main()
{
	//LeftImg = imread("./im2.png", 0);		//这个效果不好,视差范围旋转35
	//RightImg = imread("./im6.png", 0);
	LeftImg = imread("./pic/2.ppm", 0);
	RightImg = imread("./pic/3.ppm", 0);

#if 0
	namedWindow("renwu_left", 1);
	namedWindow("renwu_right", 1);
	imshow("renwu_left", LeftImg);
	imshow("renwu_right", RightImg);
	waitKey(500);
#endif // 1

	ImgHeight = LeftImg.rows;//288
	ImgWidth = LeftImg.cols;//384

	Left_census = ProcessImg(LeftImg);//处理左图,得到左图的CENSUS图像 Left_census
	namedWindow("Left_census", 1);
	imshow("Left_census", Left_census);
	waitKey(500);
	imwrite("./renwu_left.jpg", Left_census);

	Right_census = ProcessImg(RightImg);
	namedWindow("Right_census", 1);
	imshow("Right_census", Right_census);
	waitKey(500);
	//  imwrite(save_dir  + "renwu_right.jpg", Right_census);

	DisparityImg = getDisparity(Left_census, Right_census);
	namedWindow("Disparity", 1);
	imshow("Disparity", DisparityImg);
	//  imwrite(save_dir  + "disparity.jpg", DisparityImg);
	waitKey(500);

	//视差优化
	DisparityImg_Processed = ProcessDisparity(DisparityImg);
	namedWindow("DisparityImg_Processed", 1);
	imshow("DisparityImg_Processed", DisparityImg_Processed);
	//  imwrite(save_dir + "disparity_processed.jpg", DisparityImg_Processed);
	waitKey(0);
	return 0;
}


//-----------------------对图像进行census编码---------------
Mat ProcessImg(Mat &Img)
{
	int64 start, end;
	start = getTickCount();

	Mat Img_census = Mat(Img.rows, Img.cols, CV_8UC1, Scalar::all(0));
	uchar center = 0;

	for (int i = 0; i < ImgHeight - hWind; i++)
	{
		for (int j = 0; j < ImgWidth - hWind; j++)
		{
			center = Img.at<uchar>(i + hWind, j + hWind);//获取像素灰度信息数值
			uchar census = 0;
			uchar neighbor = 0;
			for (int p = i; p <= i + 2 * hWind; p++)//行
			{
				for (int q = j; q <= j + 2 * hWind; q++)//列
				{
					if (p >= 0 && p < ImgHeight  && q >= 0 && q < ImgWidth)
					{

						if (!(p == i + hWind && q == j + hWind))
						{
							//--------- 将二进制数存在变量中-----
							neighbor = Img.at<uchar>(p, q);

							if (neighbor > center)
							{
								census = census * 2;//向左移一位,相当于在二进制后面增添0
							}
							else
							{
								census = census * 2 + 1;//向左移一位并加一,相当于在二进制后面增添1
							}
							//cout << "census = " << static_cast(census) << endl;
						}
					}
				}

			}
			Img_census.at<uchar>(i + hWind, j + hWind) = census;//census编码图像赋值
		}
	}
	end = getTickCount();
	cout << "time is = " << end - start << " ms" << endl;
	return Img_census;
}

//------------得到汉明距离---------------
int GetHammingWeight(uchar value)
{
	int num = 0;
	if (value == 0)
		return 0;
	while (value)
	{
		++num;
		value = (value - 1)&value;
	}
	return num;
}

//--------------------得到视差图像--------------
Mat getDisparity(Mat &left, Mat &right)
{
	int DSR_min = 0;//视差搜索范围上限
	int DSR_max = 16;//视差搜索范围下限
	Mat disparity(ImgHeight, ImgWidth, CV_8UC1);

	cout << "ImgHeight = " << ImgHeight << "   " << "ImgWidth = " << ImgWidth << endl;
	for (int i = 0; i < ImgHeight; i++)
	{
		for (int j = 0; j < ImgWidth; j++)
		{
			uchar L;
			uchar R;
			uchar diff;
			int count = 0;//视差图像素

			L = left.at<uchar>(i, j);
			Mat Dif(1, DSR_max - DSR_min, CV_8UC1);
			//          Mat Dif(1, DSR, CV_32F);

			for (int k = DSR_min; k < DSR_max; k++)
			{
				//cout << "k = " << k << endl;
				int y = j - k;
				if (y < 0)
				{
					Dif.at<uchar>(k) = 0;
				}
				if (y >= 0)
				{
					R = right.at<uchar>(i, y);

#if 0
					cout << (65 ^ 35) << endl; //98; cout << 65 ^ 34 << endl;//这样会报错
					cout << (65 | 35) << endl; //99; 
					cout << (65 & 35) << endl; //1
#endif // 0

					//bitwise_xor(L, R, );
					diff = L ^ R;
					diff = GetHammingWeight(diff);
					Dif.at<uchar>(count++) = diff;
					//                  Dif.at(k) = diff;
										//cout << count << endl;
				}
			}
			//---------------寻找最佳匹配点--------------
			Point minLoc;
			minMaxLoc(Dif, NULL, NULL, &minLoc, NULL);
			int loc = minLoc.x;//x相当于矩阵中的列。坐标和矩阵不一样,坐标是P(x, y),对应矩阵是y行x列
			disparity.at<uchar>(i, j) = loc * 16;//乘以16是因为设置的视差下限是DSR_max,16*16=256,正好可以将视差图(0-15)*16变成灰度图(0-255)
		}
	}
	return disparity;
}

//-------------对得到的视差图进行处理视差优化-------------------
Mat ProcessDisparity(Mat &disImg)
{
	Mat ProcessDisImg(ImgHeight, ImgWidth, CV_8UC1);//存储处理后视差图
	for (int i = 0; i < ImgHeight; i++)
	{
		for (int j = 0; j < ImgWidth; j++)
		{
			uchar pixel = disImg.at<uchar>(i, j);
			if (pixel < 100)
				pixel = 0;
			ProcessDisImg.at<uchar>(i, j) = pixel;
		}
	}
	return ProcessDisImg;
}


#endif // 0

左图和右图:
立体匹配——census算法_第2张图片
运行结果为:

补充:
(1)事先在工程目录pic文件下存放:立体校正后的左图 “2.ppm"和立体校正后的右图"3.ppm”。图片获取网址:http://vision.middlebury.edu/stereo/data/scenes2001/data/tsukuba/
数据集:http://vision.middlebury.edu/stereo/data/
(2)统计一个整数转换成二进制数后1的个数
https://blog.csdn.net/earther19949/article/details/99840713

#include
#include

int NumberOf1_Suluton1(int i) {
	int count = 0;
	while (i) {
		count++;
		i &= (i - 1);
	}
	return count;
}

int NumberOf1_Sulution2(int i) {
	int count = 0;
	int Flag = 1;
	while (Flag) {
		if (Flag & i)
			count++;
		Flag <<= 1;
	}
	return count;
}
int main(void) {
	int i = 2;
	printf("%d\n", NumberOf1_Suluton1(i));
	printf("%d\n", NumberOf1_Sulution2(i));
	return 0;
}

(3)Mat:

https://blog.csdn.net/sinat_33718563/article/details/78213233

(a)三通道图像读取方式
Vec3b可以看作是vector,简单而言就是一个uchar类型的,长度为3的vector向量。(在程序开头处加上#include以包含所需要的类文件vector,还有一定要加上using namespace std;)
使用由于在OpenCV中,使用imread读取到的Mat图像数据,都是用uchar类型的数据存储,对于RGB三通道的图像,每个点的数据都是一个Vec3b类型的数据。使用at定位方法如下:
img.at(row, col)[0] = 255;  // 这是指修改B通道数据
img.at(row, col)[1] = 255;  // 这是指修改G通道数据
img.at(row, col)[2] = 255;  // 这是指修改R通道数据

(b)单通道图像读取方式

Mat img1 = imread(filename,IMREAD_GRAYSCALE);  
for( size_t nrow = 0; nrow < img1.rows; nrow++)  
{  
    for(size_t ncol = 0; ncol < img1.cols; ncol++)  
    {  
        uchar val = img1 .at<uchar>(nrow,ncol);      
    }  
}  

(4)Opencv minMaxLoc()函数的使用

void cv::minMaxLoc	(	InputArray 	src,
double * 	minVal,
double * 	maxVal = 0,
Point * 	minLoc = 0,
Point * 	maxLoc = 0,
InputArray 	mask = noArray() 
)	
参量:
src	输入单通道数组。
minVal	指向返回的最小值的指针;如果不需要,则使用NULL。
maxVal 指向返回的最大值的指针;如果不需要,则使用NULL。
minLoc	指向返回的最小位置的指针(在2D情况下);如果不需要,则使用NULL。
maxLoc	指向返回的最大位置的指针(在2D情况下);如果不需要,则使用NULL。
mask 用于选择子阵列的可选遮罩。

函数cv :: minMaxLoc查找最小和最大元素值及其位置;该功能不适用于多通道阵列。如果需要在所有通道中查找最小或最大元素,请先使用Mat :: reshape将数组重新解释为单通道。

你可能感兴趣的:(立体匹配)