opencv二维码定位

#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2\/opencv.hpp>
#include<math.h>
#include<Windows.h>
using namespace std;
using namespace cv;

vector<Point2f> check(vector<Point2f> p1, vector<Point2f> p2);
double distance_1(Point2f p1,Point2f p2);
bool isTimingPattern(list<uchar>& l1);
double var(vector<int> s);
void extending(vector<Point2f>& p, double& dis);
void putTextZH(Mat &dst, const char* str, Point org, Scalar color, int fontSize,  //下面两个函数可以封装为一个h文件
	const char *fn = "Arial", bool italic = false, bool underline = false);
void GetStringSize(HDC hDC, const char* str, int* w, int* h);
int main()
{
	Mat src = imread("4.png");
	Mat gray, binary, edge;
	Mat threshold_gray;
	cvtColor(src, gray, COLOR_BGR2GRAY);
	GaussianBlur(gray, gray, Size(5, 5), 0);
	threshold(gray, threshold_gray, 100, 255, THRESH_BINARY);
	Canny(gray, edge, 100, 300);
	
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;
	findContours(edge, contours, hierarchy, CV_RETR_TREE, CHAIN_APPROX_SIMPLE);  //只记录顶点
	//嵌套contours,寻找嵌套数大于等于5的
	vector<int> indices;
	for (size_t i = 0; i < contours.size(); i++)
	{
		int num = 0;
		int k = i;  //很关键
		while (hierarchy[k][2] != -1){
			k = hierarchy[k][2];
			num++;
		}
		if (num >= 5){
			indices.push_back(i);
		}
	}
	//画出粗定位区域
	vector<RotatedRect> rects;
	Mat img_dc = src.clone();
	for (size_t i = 0; i < indices.size(); i++)
	{
		rects.push_back(minAreaRect(contours[indices[i]]));
		drawContours(img_dc, contours,indices[i] , Scalar(0, 255,0), 2);
	}

	vector<vector<Point2f> > points;

	for (size_t i = 0; i < rects.size(); i++)
	{

		Point2f p1[4];
		rects[i].points(p1);
		vector<Point2f> p2(begin(p1), end(p1));
		points.push_back(p2);
	}
	//排除错误的定位
	vector<vector<Point2f> > timing_pattern_src;
	for (int i = 0; i < points.size(); i++)
	{
		vector<Point2f> pair_points;
		for (int j = i + 1; j < points.size(); j++)
		{
			pair_points=check(points[i], points[j]);
			timing_pattern_src.push_back(pair_points);
		}
	}
	//矫正
	vector<vector<Point2f> > timing_pattern(timing_pattern_src.begin(), timing_pattern_src.end());
	for (int i = 0; i < timing_pattern.size(); i++)
	{
		Point2f a0 = Point2f(timing_pattern[i][0].x + (timing_pattern[i][2].x - timing_pattern[i][0].x) * 1 / 14, timing_pattern[i][0].y + (timing_pattern[i][2].y - timing_pattern[i][0].y) * 1 / 14);
		Point2f a1 = Point2f(timing_pattern[i][1].x + (timing_pattern[i][3].x - timing_pattern[i][1].x) * 1 / 14, timing_pattern[i][1].y + (timing_pattern[i][3].y - timing_pattern[i][1].y) * 1 / 14);
		Point2f a2 = Point2f(timing_pattern[i][2].x + (timing_pattern[i][0].x - timing_pattern[i][2].x) * 1 / 14, timing_pattern[i][2].y + (timing_pattern[i][0].y - timing_pattern[i][2].y) * 1 / 14);
		Point2f a3 = Point2f(timing_pattern[i][3].x + (timing_pattern[i][1].x - timing_pattern[i][3].x) * 1 / 14, timing_pattern[i][3].y + (timing_pattern[i][1].y - timing_pattern[i][3].y) * 1 / 14);
		timing_pattern[i][0] = a0;
		timing_pattern[i][1] = a1;
		timing_pattern[i][2] = a2;
		timing_pattern[i][3] = a3;
	}
	//对timing pattern 计数
	vector<vector<int> > correct_pair_index;
	for (int i = 0; i < timing_pattern.size(); i++){
		for (int j = 0; j < 4; j += 2){
			LineIterator it(threshold_gray, timing_pattern[i][j], timing_pattern[i][j + 1]);
			list<uchar> val;
			for (int k = 0; k < it.count; k++){
				val.push_back(*(uchar*)*it);
				//cout << **it << endl;
				it++;
			}
			if (isTimingPattern(val)){
				vector<int> ddd;
				ddd.push_back(i);
				ddd.push_back(j);
				line(img_dc, timing_pattern[i][j], timing_pattern[i][j + 1], Scalar(255,0,0), 1);    //画出pattern
				correct_pair_index.push_back(ddd);
			}
		}
	}
	//得到二维码的区域
	vector<Point2f> rect_verticle;
	for (int i = 0; i < correct_pair_index.size(); i++)
	{
		rect_verticle.push_back(timing_pattern_src[correct_pair_index[i][0]][correct_pair_index[i][1]]);
		rect_verticle.push_back(timing_pattern_src[correct_pair_index[i][0]][correct_pair_index[i][1]+1]);
	}

	
	//判断两两之间点的距离,寻找二维码的中间标记
	Point2f centerPoint;
	vector<Point2f> QRCodePoints;
	for (int i = 0; i < rect_verticle.size(); i++)
		for (int j = i + 1; j < rect_verticle.size(); j++)
		{
			double s = distance_1(rect_verticle[i], rect_verticle[j]);
			if (s < 10)
			{
				centerPoint = Point2f((rect_verticle[i].x + rect_verticle[j].x)*0.5, (rect_verticle[i].y + rect_verticle[j].y)*0.5);
			}
		}
	for (int i = 0; i < rect_verticle.size(); i++)
	{
		double s = distance_1(rect_verticle[i],centerPoint );
		if (s>10)
			QRCodePoints.push_back(rect_verticle[i]);
	}
	double distance_pattern_1 = distance_1(centerPoint, QRCodePoints[0]);
	double distance_pattern_2 = distance_1(centerPoint, QRCodePoints[1]);
	double distance_pattern = (distance_pattern_1 + distance_pattern_2)*0.5;
	double extend_distance = distance_pattern / 1.5;
	
	extending(QRCodePoints, extend_distance);
	Rect rect_end(QRCodePoints[0], QRCodePoints[1]);
	rectangle(img_dc, rect_end, Scalar(0, 0, 255), 4);
//很重要的提醒,这里也许可以用RatateREct的构造函数,用两个点做输入,未验证。


/*	cv::Point2f* vertices = new cv::Point2f[4];
	rect_end.points(vertices);
	for (int j = 0; j < 4; j++)
	{
		cv::line(img_dc, vertices[j], vertices[(j + 1) % 4], cv::Scalar(0, 0, 255),5);
	}*/

	putTextZH(img_dc, "二维码定位", Point(0, 20), Scalar(100, 100, 255), 30, "Arial");
	putTextZH(img_dc, "缺陷:一幅图最多定位一个二维码", Point(0, 50), Scalar(100, 100, 255) ,30, "Arial");
	putTextZH(img_dc, "      对于有旋转角度的二维码效果不好,待改进。。",Point(0,80), Scalar(100, 100, 255), 30, "Arial");
	//imwrite("QRcodeLocal.jpg", img_dc);
	imshow(" location", img_dc);
	waitKey(0);
	return 0;
}

vector<Point2f> check(vector<Point2f> p1, vector<Point2f> p2)
{
	vector<Point2f> pair_points;
	double min_distance1=99999, min_distance2=99999;
	double s=0;
	int index_i[2] = { 0, 0 }, index_j[2] = { 0, 0 };
	for (int i = 0; i < p1.size(); i++)
		for (int j = 0; j < p2.size(); j++)
		{
			s = distance_1(p1[i], p2[j]);
			if (s < min_distance2){
				if (s < min_distance1){
					min_distance2 = min_distance1;
					min_distance1 = s;
					index_i[1] = index_i[0];    //min_distance1被移到了2,所以index也要移动啊!!!!
					index_j[1] = index_j[0];
					index_i[0] = i;
					index_j[0] = j;
				}
				else{
					min_distance2 = s;
					index_i[1] = i;
					index_j[1] = j;
				}
			}
		}
	pair_points.push_back(p1[index_i[0]]);
	pair_points.push_back(p2[index_j[0]]);
	pair_points.push_back(p1[index_i[1]]);
	pair_points.push_back(p2[index_j[1]]);
	return pair_points;
}

double distance_1(Point2f p1, Point2f p2)
{
	double s = sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));
	return s;

}

bool isTimingPattern(list<uchar>& l1)
{
	while (*l1.begin() != 0)             //全是255,则这里会出错
		l1.pop_front();
	while (*(l1.rbegin()) != 0)
		l1.pop_back();
	
	vector<int> counts;
	int count = 1;
	auto it1 = l1.begin();
	uchar l = *it1;
	for (++it1; it1 != l1.end(); it1++){
		if (*it1 == l){
			count++;
		}
		else{
			counts.push_back(count);
			count = 1;
		}
		l = *it1;
	}
	counts.push_back(count);
	if ((int)counts.size() < 7)
		return false;
	//计算方差,判断
	double variance = var(counts);
	cout << variance << endl;
	if (variance<3)
		return true;
	return false;

}

double var(vector<int> s)
{

	double sum(0.0);
	double sqsum(0.0);
	for (int i = 0; i < s.size(); i++){
		int v = s[i];
		sum += v;
		sqsum += v*v;
	}
	double scale = 1.0 / s.size();
	double mean = sum*scale;
	double variance = sqsum*scale - mean*mean;
	return variance;
}

void extending(vector<Point2f>& p, double& dis)
{

	if (p[0].x < p[1].x)
	{
		p[0].x -= dis;
		p[1].x += dis;

	}
	else{
		p[0].x += dis;
		p[1].x -= dis;
	}
	if (p[0].y < p[1].y)
	{
		p[0].y -= dis;
		p[1].y += dis;
	}
	else{
		p[0].y += dis;
		p[1].y -= dis;
	}
}
void putTextZH(Mat &dst, const char* str, Point org, Scalar color, int fontSize, const char* fn, bool italic, bool underline)
{
	CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3));

	int x, y, r, b;
	if (org.x > dst.cols || org.y > dst.rows) return;
	x = org.x < 0 ? -org.x : 0;
	y = org.y < 0 ? -org.y : 0;

	LOGFONTA lf;
	lf.lfHeight = -fontSize;
	lf.lfWidth = 0;
	lf.lfEscapement = 0;
	lf.lfOrientation = 0;
	lf.lfWeight = 5;
	lf.lfItalic = italic;   //斜体
	lf.lfUnderline = underline; //下划线
	lf.lfStrikeOut = 0;
	lf.lfCharSet = DEFAULT_CHARSET;
	lf.lfOutPrecision = 0;
	lf.lfClipPrecision = 0;
	lf.lfQuality = PROOF_QUALITY;
	lf.lfPitchAndFamily = 0;
	strcpy_s(lf.lfFaceName, fn);

	HFONT hf = CreateFontIndirectA(&lf);
	HDC hDC = CreateCompatibleDC(0);
	HFONT hOldFont = (HFONT)SelectObject(hDC, hf);

	int strBaseW = 0, strBaseH = 0;
	int singleRow = 0;
	char buf[1 << 12];
	strcpy_s(buf, str);
	char *bufT[1 << 12];  // 这个用于分隔字符串后剩余的字符,可能会超出。
	//处理多行
	{
		int nnh = 0;
		int cw, ch;

		const char* ln = strtok_s(buf, "\n", bufT);
		while (ln != 0)
		{
			GetStringSize(hDC, ln, &cw, &ch);
			strBaseW = max(strBaseW, cw);
			strBaseH = max(strBaseH, ch);

			ln = strtok_s(0, "\n", bufT);
			nnh++;
		}
		singleRow = strBaseH;
		strBaseH *= nnh;
	}

	if (org.x + strBaseW < 0 || org.y + strBaseH < 0)
	{
		SelectObject(hDC, hOldFont);
		DeleteObject(hf);
		DeleteObject(hDC);
		return;
	}

	r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1;
	b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1;
	org.x = org.x < 0 ? 0 : org.x;
	org.y = org.y < 0 ? 0 : org.y;

	BITMAPINFO bmp = { 0 };
	BITMAPINFOHEADER& bih = bmp.bmiHeader;
	int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4));

	bih.biSize = sizeof(BITMAPINFOHEADER);
	bih.biWidth = strBaseW;
	bih.biHeight = strBaseH;
	bih.biPlanes = 1;
	bih.biBitCount = 24;
	bih.biCompression = BI_RGB;
	bih.biSizeImage = strBaseH * strDrawLineStep;
	bih.biClrUsed = 0;
	bih.biClrImportant = 0;

	void* pDibData = 0;
	HBITMAP hBmp = CreateDIBSection(hDC, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0);

	CV_Assert(pDibData != 0);
	HBITMAP hOldBmp = (HBITMAP)SelectObject(hDC, hBmp);

	//color.val[2], color.val[1], color.val[0]
	SetTextColor(hDC, RGB(255, 255, 255));
	SetBkColor(hDC, 0);
	//SetStretchBltMode(hDC, COLORONCOLOR);

	strcpy_s(buf, str);
	const char* ln = strtok_s(buf, "\n", bufT);
	int outTextY = 0;
	while (ln != 0)
	{
		TextOutA(hDC, 0, outTextY, ln, strlen(ln));
		outTextY += singleRow;
		ln = strtok_s(0, "\n", bufT);
	}
	uchar* dstData = (uchar*)dst.data;
	int dstStep = dst.step / sizeof(dstData[0]);
	unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep;
	unsigned char* pStr = (unsigned char*)pDibData + x * 3;
	for (int tty = y; tty <= b; ++tty)
	{
		unsigned char* subImg = pImg + (tty - y) * dstStep;
		unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep;
		for (int ttx = x; ttx <= r; ++ttx)
		{
			for (int n = 0; n < dst.channels(); ++n){
				double vtxt = subStr[n] / 255.0;
				int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n];
				subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv);
			}

			subStr += 3;
			subImg += dst.channels();
		}
	}

	SelectObject(hDC, hOldBmp);
	SelectObject(hDC, hOldFont);
	DeleteObject(hf);
	DeleteObject(hBmp);
	DeleteDC(hDC);
}
void GetStringSize(HDC hDC, const char* str, int* w, int* h)
{
	SIZE size;
	GetTextExtentPoint32A(hDC, str, strlen(str), &size);
	if (w != 0) *w = size.cx;
	if (h != 0) *h = size.cy;
}

 

效果如下:

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值