图像处理之定位

图像处理之定位

图像处理中常用的定位方法

1、投影定位

投影,在立体几何中,是空间直线在某个方向上的投影,那么图像处理中也是这种投影思想。

最简单的投影:

水平投影(英文名ground plan horizontal projection),水平面方向的正投影叫水平投影。

图像中字符识别时,

水平投影是指二维图像按行向y轴方向投影,将图像数组进行行求和;

垂直投影是指二维图象按列向x轴方向投影,将图像数组进行列求和。

对于二值图像或明显特征的灰度图分割前景与背景,经常用到投影法。在OCR字符分割中,通常会用到该方法,可以很容易的获得每个OCR字符在X轴、Y轴的起始位置与终止位置坐标,进而将每个OCR字符分割出来。

思路:
1.设定大概的ROI区域,取得包含目标OCR区域的图像。
2.先通过垂直投影,获取每个字符的左右边界位置,即X轴坐标,并记录。通过取ROI获得初次分割的图像。
3.再通过对得到的图像进行水平投影,获取每个字符的上下边界,即Y轴坐标,并记录。通过取ROI获得单个目标图像。

  图像处理之定位_第1张图片 

原图(来自百度百科:https://baike.baidu.com/item/%E7%BE%8E%E5%85%83/484146?fr=aladdin)

垂直投影代码如下:

/// 
/// 垂直投影
/// 
/// 
/// 分割后的单个字符
vector verticalProjectionMat_1(Mat srcImg)//垂直投影
{
	if (nullptr == srcImg.data)
	{
		std::cout << "No image!!!" << std::endl;
		return srcImg;
	}

	Mat binImg;
	if (3 == srcImg.channels())
	{
		cvtColor(srcImg, binImg, COLOR_RGB2GRAY);
		//std::cout << "binImg.channels()=" << binImg.channels() << std::endl;
	}
	else if (1 == srcImg.channels())
	{
		srcImg.copyTo(binImg);
	}
	blur(binImg, binImg, Size(3, 3));//均值滤波
	threshold(binImg, binImg, 0, 255, /*CV_*/THRESH_OTSU);

	cv::namedWindow("srcImg", 0);
	cv::imshow("srcImg", srcImg);
	cv::waitKey(0);

	cv::namedWindow("binImg", 0);
	cv::imshow("binImg", binImg);
	cv::waitKey(0);


	int perPixelValue;//每个像素的值
	int width = srcImg.cols;
	int height = srcImg.rows;
	int* projectValArry = new int[width];//创建用于储存每列白色像素个数的数组
	memset(projectValArry, 0, width * 4.0);//初始化数组
	for (int col = 0; col < width; col++)//列遍历
	{
		for (int row = 0; row < height; row++)
		{
			perPixelValue = binImg.at(row, col);
			if (perPixelValue == 0)//如果是白底黑字
			{
				projectValArry[col]++;
			}
		}
	}
	//Mat verticalProjectionMat_1(height, width, CV_8UC1);//垂直投影的画布
	//for (int i = 0; i < height; i++)
	//{
	//	for (int j = 0; j < width; j++)
	//	{
	//		perPixelValue = 255;  //背景设置为白色
	//		verticalProjectionMat_1.at(i, j) = perPixelValue;
	//	}
	//}

	//垂直投影的画布,白底.
	Mat verticalProjectionMat_1(height, width, CV_8UC1, cv::Scalar(255));
	//绘制垂直投影直方图
	for (int i = 0; i < width; i++)
	{
		for (int j = 0; j < projectValArry[i]; j++)
		{
			perPixelValue = 0;//直方图设置为黑色  
			verticalProjectionMat_1.at(height - 1 - j, i) = perPixelValue;
		}
	}
	cv::namedWindow("垂直投影", 0);
	cv::imshow("垂直投影", verticalProjectionMat_1);
	cv::waitKey(0);


	vector roiList;//用于储存分割出来的每个字符
	int startIndex = 0;//记录进入字符区域的索引
	int endIndex = 0;  //记录进入空白区域的索引
	bool inBlock = false;//是否遍历到了字符区内
	for (int i = 0; i < srcImg.cols; i++)//cols=width
	{
		if (!inBlock && projectValArry[i] != 0)//进入字符区
		{
			inBlock = true;
			startIndex = i;
		}
		else if (projectValArry[i] == 0 && inBlock)//进入空白区
		{
			endIndex = i;
			inBlock = false;
			//分割出单个字符图像
			Mat roiImg = srcImg(Range(0, srcImg.rows), Range(startIndex, endIndex + 1));
			roiList.push_back(roiImg);

			//cv::namedWindow("roiImg", 0);
			//cv::imshow("roiImg", roiImg);
			//cv::waitKey(0);
		}
	}

	char szName[30] = { 0 };
	for (int i = 0; i < roiList.size(); i++)
	{
		sprintf_s(szName, "..\\IMG_output\\字符分割\\_%d.jpg", i);
		
		cv::namedWindow("分割1", 0);
		cv::imshow("分割1", roiList[i]);
		cv::imwrite(szName, roiList[i]);
		cv::waitKey(0);
	}


	delete[] projectValArry;//怎么new的,就怎么delete掉!!!
	return roiList;
}

根据图像本身的颜色特征,选择R通道(前景明显,背景较干净,便于处理)的单色图像作为输入图像,进行阈值处理(图像处理阈值分割之OTSU/大津阈值原理及其实现)、投影。

图像处理之定位_第2张图片

二值化图像、垂直投影图像:

图像处理之定位_第3张图片

初次分割后得到的单个字符图像:

通过垂直投影后,得到了OCR单个字符的左右边界精确位置坐标。接下来通过水平投影,即可得到单个字符上下边界的精确位置坐标。

水平投影代码如下:

/// 
/// 水平投影
/// 
/// 
/// 分割后的单个字符
vector horizontalProjectionMat_1(Mat srcImg)//水平投影
{
	Mat binImg;
	if (3 == srcImg.channels())
	{
		cvtColor(srcImg, binImg, COLOR_RGB2GRAY);

		//std::vector rgbImg(3);
		//split(srcImg, rgbImg);//分离出图片的B,G,R颜色通道.

		//cv::namedWindow("B_rgbImg[0]", 0);
		//cv::namedWindow("G_rgbImg[1]", 0);
		//cv::namedWindow("R_rgbImg[2]", 0);
		//cv::imshow("B_rgbImg[0]", rgbImg[0]);
		//cv::imshow("G_rgbImg[1]", rgbImg[1]);
		//cv::imshow("R_rgbImg[2]", rgbImg[2]);
		//cv::waitKey(0);

		//binImg = rgbImg[2];
		//	std::cout << "binImg.channels()=" << binImg.channels() << std::endl;
	}
	else if (1 == srcImg.channels())
	{
		srcImg.copyTo(binImg);
	}
	blur(binImg, binImg, Size(3, 3));
	threshold(binImg, binImg, 0, 255, /*CV_*/THRESH_OTSU);
	
	int perPixelValue = 0;//每个像素的值
	int width = srcImg.cols;
	int height = srcImg.rows;
	int* projectValArry = new int[height];//创建一个储存每行白色像素个数的数组
	memset(projectValArry, 0, height*4.0);//初始化数组
	for (int row = 0; row < height; row++)//遍历每个像素点
	{
		for (int col = 0; col < width; col++)
		{
			perPixelValue = binImg.at(row, col);
			if (perPixelValue == 0)//白底黑字
			{
				projectValArry[row]++;//每一行的有效像素数.
			}
		}
	}

	创建画布,并设为白底.
	//Mat horizontalProjectionMatIMG_1(height, width, CV_8UC1);//创建画布
	//for (int i = 0; i < height; i++)
	//{
	//	for (int j = 0; j < width; j++)
	//	{
	//		perPixelValue = 255;
	//		horizontalProjectionMatIMG_1.at(i, j) = perPixelValue;//设置背景为白色
	//	}
	//}

	//垂直投影的画布,白底.
	Mat horizontalProjectionMatIMG_1(height, width, CV_8UC1, cv::Scalar(255));

	for (int i = 0; i < height; i++)//水平直方图
	{
		for (int j = 0; j < projectValArry[i]; j++)
		{
			perPixelValue = 0;
			horizontalProjectionMatIMG_1.at(i, width - 1 - j) = perPixelValue;//设置直方图为黑色
		}
	}
	vector roiList;//用于储存分割出来的每个字符
	int startIndex = 0;//记录进入字符区的索引
	int endIndex = 0;//记录进入空白区域的索引
	bool inBlock = false;//是否遍历到了字符区内
	for (int i = 0; i 

最终得到的图像:

 

2、积分图定位

在定位OCR区域时,也可以使用积分图进行精确定位到OCR区域后,再进行垂直、水平投影分割,直接得到OCR字符小图。

关于积分图,这里就不再赘述了,详情请看:图像处理之图像积分图integral()

 

原图:

 

 

3、匹配定位

 

 

 

opencv

 

halcon

 

c++

 

 

 

 

一、

GMS了解一下

要求实时(精度也不差): orb+GMS

要求精度: A-SIFT+GMS

相同程度匹配,速度精度比RANSAC效果好

二、

OpenCV官方提出的“解决方案”:Features2D + Homography to find a known object。地址:Features2D + Homography to find a known object。该方案先用SURF来提取特征点然后用FLANN进行匹配,过滤出足够好的匹配点之后,用一个矩形来定位出被探测的物体。如下图:

图像处理之定位_第4张图片

 

三、

sift+ransac

sift/surf+flann+ransac,一般情况都好使。实时性的话,要看具体的要求。

四、

形状匹配

 

 

 

 

 

 

 

【36、这一秒不放弃,下一秒就有希望!坚持下去才可能成功!】

 

 

 

你可能感兴趣的:(OpenCV,图像处理,目标检测_识别,定位,opencv)