opencv应用海康案例-拍照并识别方框

在这里插入图片描述
在这里插入图片描述
从图片识别到方框并且截取出来,以下是全部代码:

// hkOpenCVtest.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <opencv2\opencv.hpp>
#include <opencv2\highgui.hpp>
//#include <opencv\opencv.hpp>
#include <opencv2\imgproc.hpp>
#include <io.h>

#define  GREY  0
#define  COLOR 1
#pragma warning(disable : 4996)
using namespace std;
using namespace cv;

//#define  DEBUGP
//文件的路径,也可以使用cin自己指定,例如cin>>path
char * filePath = "D:\\workspace\\hkjpg\\002";

string outPutPath = "D:\\workspace\\hkjpg\\003";

std::map<int, std::set<int>> generateBorders(const std::vector<cv::Point> & vecPts)
{
	std::map<int, std::set<int>> borders;
	cv::Point tl(vecPts[0]), tr(vecPts[1]), bl(vecPts[2]), br(vecPts[3]);

	// tl to tr
	double K = double(tl.y - tr.y) / (tl.x - tr.x);
	for (int i = tl.x; i < tr.x; ++i)
		borders[i].insert(cvRound(-K*(tl.x - i) + tl.y));

	// tr to br
	if (tr.x != br.x)
	{
		K = double(tr.y - br.y) / (tr.x - br.x);
		for (int i = tr.x; i < br.x; ++i)
			borders[i].insert(cvRound(-K*(tr.x - i) + tr.y));
	}

	// br to bl
	K = double(br.y - bl.y) / (br.x - bl.x);
	for (int i = bl.x; i < br.x; ++i)
		borders[i].insert(cvRound(-K*(br.x - i) + br.y));

	// bl to tl
	if (bl.x != tl.x)
	{
		K = double(bl.y - tl.y) / (bl.x - tl.x);
		for (int i = tl.x; i < bl.x; ++i)
			borders[i].insert(cvRound(-K*(bl.x - i) + bl.y));
	}

	for (auto it = borders.begin(); it != borders.end(); ++it)
	{
		if ((*it).second.size() == 2)
			continue;

		std::set<int> newone = { *(*it).second.begin(), *(--(*it).second.end()) };
		(*it).second.swap(newone);
	}

#ifdef DEBUG_CODE
	cv::Mat disp = cv::imread("1111.PNG", CV_LOAD_IMAGE_COLOR);

	for (auto it = borders.begin(); it != borders.end(); ++it)
	{
		cv::circle(disp, cv::Point((*it).first, *(*it).second.begin()), 1, cv::Scalar(255, 255, 0), -1);
		cv::circle(disp, cv::Point((*it).first, *(--(*it).second.end())), 1, cv::Scalar(0, 255, 255), -1);

	}

	cv::circle(disp, tl, 2, cv::Scalar(0, 0, 255), -1);
	cv::circle(disp, tr, 2, cv::Scalar(0, 0, 255), -1);
	cv::circle(disp, bl, 2, cv::Scalar(0, 0, 255), -1);
	cv::circle(disp, br, 2, cv::Scalar(0, 0, 255), -1);
#endif // DEBUG_CODE



	return borders;
}

cv::Mat generateMask(const cv::Mat & src, const std::vector<cv::Point> & vecPts)
{
	cv::Mat mask = cv::Mat::zeros(src.size(), CV_8UC1);

	cv::Point tl(vecPts[0]), tr(vecPts[1]), bl(vecPts[2]), br(vecPts[3]);
	std::map<int, std::set<int>> borders = generateBorders(vecPts);

	int minX = std::min(tl.x, bl.x),
		maxX = std::max(tr.x, br.x),
		minY = std::min(tl.y, tr.y),
		maxY = std::max(bl.y, br.y);

	uchar minZ = std::min(src.at<uchar>(tl), src.at<uchar>(tr));
	minZ = std::min(minZ, src.at<uchar>(bl));
	minZ = std::min(minZ, src.at<uchar>(br));


	for (size_t j = minY; j < maxY; ++j)
	{
		const uchar* pS = src.ptr<uchar>(j);
		uchar* pM = mask.ptr<uchar>(j);

		for (size_t i = minX; i < maxX; ++i)
		{
			// in the region.
			if (*borders[i].begin() < j && j < *(++borders[i].begin()))
			{
				pM[i] = pS[i];
			}
		}
	}
	return mask;

}

void rotate_arbitrarily_angle(Mat &src, Mat &dst, float angle)
{
	float radian = (float)(angle / 180.0 * CV_PI);

	//填充图像
	int maxBorder = (int)(max(src.cols, src.rows)* 1.414); //即为sqrt(2)*max
	int dx = (maxBorder - src.cols) / 2;
	int dy = (maxBorder - src.rows) / 2;
	copyMakeBorder(src, dst, dy, dy, dx, dx, BORDER_CONSTANT);

	//旋转
	Point2f center((float)(dst.cols / 2), (float)(dst.rows / 2));
	Mat affine_matrix = getRotationMatrix2D(center, angle, 1.0);//求得旋转矩阵
	warpAffine(dst, dst, affine_matrix, dst.size());

	//计算图像旋转之后包含图像的最大的矩形
	float sinVal = abs(sin(radian));
	float cosVal = abs(cos(radian));
	Size targetSize((int)(src.cols * cosVal + src.rows * sinVal),
		(int)(src.cols * sinVal + src.rows * cosVal));

	//剪掉多余边框
	int x = (dst.cols - targetSize.width) / 2;
	int y = (dst.rows - targetSize.height) / 2;
	Rect rect(x, y, targetSize.width, targetSize.height);
	dst = Mat(dst, rect);
}



// 获取指定像素点放射变换后的新的坐标位置
CvPoint getPointAffinedPos(CvPoint src, int h, int w, double degree)
{
	int diaLength = int(sqrt((h*h + w*w)));
	Point center;
	center.x = center.y = diaLength / 2;
	src.x += diaLength / 2 - w / 2;
	src.y += diaLength / 2 - h / 2;
	double angle = degree * CV_PI / 180.0;

	CvPoint dst;
	int x = src.x - center.x;
	int y = src.y - center.y;
	dst.x = cvRound(x * cos(angle) + y * sin(angle) + center.x);
	dst.y = cvRound(-x * sin(angle) + y * cos(angle) + center.y);
	return dst;
}
void GetImageRect(IplImage* orgImage, CvRect rectInImage, IplImage* imgRect)
{
	//从图像orgImage中提取一块(rectInImage)子图像imgRect
	IplImage *result = imgRect;
	CvSize size;
	size.width = rectInImage.width;
	size.height = rectInImage.height;
	//result=cvCreateImage( size, orgImage->depth, orgImage->nChannels );
	//从图像中提取子图像
	cvSetImageROI(orgImage, rectInImage);
	cvCopy(orgImage, result);
	cvResetImageROI(orgImage);
}

void getFiles(string path, vector<string>& files)
{
	//文件句柄  
	long long   hFile = 0;
	//文件信息  
	struct _finddata_t fileinfo;
	memset(&fileinfo, 0, sizeof(fileinfo));
	string p;
	if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
	{
		do
		{
			//如果是目录,迭代之  
			//如果不是,加入列表  
			if ((fileinfo.attrib &  _A_SUBDIR))
			{
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
					getFiles(p.assign(path).append("\\").append(fileinfo.name), files);
			}
			else
			{
				//如果是文件的化,放进来,这个加了路径名的path的命名
				files.push_back(p.assign(path).append("\\").append(fileinfo.name));
			}
			memset(&fileinfo, 0, sizeof(fileinfo));
		} while (_findnext(hFile, &fileinfo) == 0);
		_findclose(hFile);
	}
}

bool picSegment(Mat &ResizeImg, int type, int n, int threshhold = 1)
{
	Mat BinRGBImg;
	if (!type)
	{
		//灰度变换
		Mat gray;
		cvtColor(ResizeImg, gray, COLOR_BGR2GRAY);
		threshold(gray, BinRGBImg, 100, 255, THRESH_BINARY_INV);
#ifdef DEBUGP
		imshow("灰度阈值图", gray);
#endif
	}
	else
	{
		//基于颜色信息二值化
		unsigned char pixelB, pixelG, pixelR;  //记录各通道值
		unsigned char DifMax = 60;             //基于颜色区分的阈值设置
		unsigned char B = 25, G = 25, R = 25;

		if (1 == threshhold)
		{
			DifMax = 60;             //基于颜色区分的阈值设置
			B = 25;
			G = 25;
			R = 25; //各通道的阈值设定,
		}
		else if (2 == threshhold)
		{
			DifMax = 100;//基于颜色区分的阈值设置
			B = 40;
			G = 98;
			R = 8; //各通道的阈值设定,
		}



		BinRGBImg = ResizeImg.clone();  //二值化之后的图像
		for (int i = 0; i < ResizeImg.rows; i++)   //通过颜色分量将图片进行二值化处理
		{
			for (int j = 0; j < ResizeImg.cols; j++)
			{
				pixelB = ResizeImg.at<Vec3b>(i, j)[0]; //获取图片各个通道的值
				pixelG = ResizeImg.at<Vec3b>(i, j)[1];
				pixelR = ResizeImg.at<Vec3b>(i, j)[2];

				if (abs(pixelB - B) < DifMax && abs(pixelG - G) < DifMax && abs(pixelR - R) < DifMax)
				{                                           //将各个通道的值和各个通道阈值进行比较
					BinRGBImg.at<Vec3b>(i, j)[0] = 255;     //符合颜色阈值范围内的设置成白色
					BinRGBImg.at<Vec3b>(i, j)[1] = 255;
					BinRGBImg.at<Vec3b>(i, j)[2] = 255;
				}
				else
				{
					BinRGBImg.at<Vec3b>(i, j)[0] = 0;        //不符合颜色阈值范围内的设置为黑色
					BinRGBImg.at<Vec3b>(i, j)[1] = 0;
					BinRGBImg.at<Vec3b>(i, j)[2] = 0;
				}
			}
		}
#ifdef DEBUGP
		imshow("基于颜色信息二值化", BinRGBImg);        //显示二值化处理之后的图像
		cvWaitKey(0);
#endif
	}//

	//----------------------------
	Mat BinOriImg;     //形态学处理结果图像


	if (type)
	{
		Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); //设置形态学处理窗的大小
		dilate(BinRGBImg, BinOriImg, element);     //进行多次膨胀操作
		dilate(BinOriImg, BinOriImg, element);
		dilate(BinOriImg, BinOriImg, element);

		erode(BinOriImg, BinOriImg, element);      //进行多次腐蚀操作
		erode(BinOriImg, BinOriImg, element);
		erode(BinOriImg, BinOriImg, element);
#ifdef DEBUGP
		imshow("形态学处理后", BinOriImg);        //显示形态学处理之后的图像
		cvWaitKey(0);
#endif
		cvtColor(BinOriImg, BinOriImg, CV_BGR2GRAY);   //将形态学处理之后的图像转化为灰度图像
		threshold(BinOriImg, BinOriImg, 100, 255, THRESH_BINARY); //灰度图像二值化
	}
	else
	{
		Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); //设置形态学处理窗的大小
		dilate(BinRGBImg, BinOriImg, element);     //进行多次膨胀操作
												   //dilate(BinOriImg, BinOriImg, element);
												   //dilate(BinOriImg, BinOriImg, element);

		erode(BinOriImg, BinOriImg, element);      //进行多次腐蚀操作
												   //erode(BinOriImg, BinOriImg, element);
												   //erode(BinOriImg, BinOriImg, element);
#ifdef DEBUGP
		imshow("形态学处理后", BinOriImg);        //显示形态学处理之后的图像
		cvWaitKey(0);
#endif
	}

	//--------------------------------------
	double length, area, rectArea;     //定义轮廓周长、面积、外界矩形面积
	double rectDegree = 0.0;           //矩形度=外界矩形面积/轮廓面积
	double long2Short = 0.0;           //体态比=长边/短边
	CvRect rect;           //外界矩形
	CvBox2D box, boxTemp;  //外接矩形
	CvPoint2D32f pt[4];    //矩形定点变量
	double axisLong = 0.0, axisShort = 0.0;        //矩形的长边和短边
	double axisLongTemp = 0.0, axisShortTemp = 0.0;//矩形的长边和短边
	double LengthTemp;     //中间变量
	float  angle = 0;      //记录车牌的倾斜角度
	float  angleTemp = 0;
	bool   TestPlantFlag = 0;  //车牌检测成功标志位



	CvMemStorage *storage = cvCreateMemStorage(0);
	CvSeq * seq = 0;     //创建一个序列,CvSeq本身就是一个可以增长的序列,不是固定的序列
	CvSeq * tempSeq = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	int cnt = cvFindContours(&(IplImage(BinOriImg)), storage, &seq, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
	//第一个参数是IplImage指针类型,将MAT强制转换为IplImage指针类型
	//返回轮廓的数目 
	//获取二值图像中轮廓的个数
	cout << "number of contours   " << cnt << endl;  //打印轮廓个数
	for (tempSeq = seq; tempSeq != NULL; tempSeq = tempSeq->h_next)
	{
		length = cvArcLength(tempSeq);       //获取轮廓周长
		area = cvContourArea(tempSeq);       //获取轮廓面积
		printf("周长=%f,面积=%f\n", length, area);
		//if ((area > 1500 && area < 8000) && (length > 100) && (length < 300))     //矩形区域面积大小判断
		if ((area > 5000 && area < 8000) && (length < 500))
		{
			rect = cvBoundingRect(tempSeq, 1);//计算矩形边界
			boxTemp = cvMinAreaRect2(tempSeq, 0);  //获取轮廓的矩形
			cvBoxPoints(boxTemp, pt);              //获取矩形四个顶点坐标
			angleTemp = boxTemp.angle;                 //得到倾斜角度
			printf("x=%f,y=%f\n", pt[0].x, pt[0].y);
			//if ((pt[0].x > 200)&&((pt[0].x < 400)))
			{
				axisLongTemp = sqrt(pow(pt[1].x - pt[0].x, 2) + pow(pt[1].y - pt[0].y, 2));  //计算长轴(勾股定理)
				axisShortTemp = sqrt(pow(pt[2].x - pt[1].x, 2) + pow(pt[2].y - pt[1].y, 2)); //计算短轴(勾股定理)

				if (axisShortTemp > axisLongTemp)   //短轴大于长轴,交换数据
				{
					LengthTemp = axisLongTemp;
					axisLongTemp = axisShortTemp;
					axisShortTemp = LengthTemp;
				}
				else
					angleTemp += 90;

				rectArea = axisLongTemp * axisShortTemp;  //计算矩形的面积
				rectDegree = area / rectArea;     //计算矩形度(比值越接近1说明越接近矩形)

				long2Short = axisLongTemp / axisShortTemp; //计算长宽比
														   //if (long2Short > 2.2 && long2Short < 3.8 && rectDegree > 0.63 && rectDegree < 1.37 && rectArea > 2000 && rectArea < 50000)
				{
					Mat GuiRGBImg = ResizeImg.clone();
					TestPlantFlag = true;             //检测车牌区域成功
					for (int i = 0; i < 4; ++i)       //划线框出车牌区域
						cvLine(&(IplImage(GuiRGBImg)), cvPointFrom32f(pt[i]), cvPointFrom32f(pt[((i + 1) % 4) ? (i + 1) : 0]), CV_RGB(255, 0, 0));
#ifdef DEBUGP
					imshow("提取结果图", GuiRGBImg);    //显示最终结果图
					cvWaitKey(0);
#endif

					Mat mask, dst,rot;
					vector<vector<Point>> contour;
					std::vector<cv::Point>  pts;

					mask = Mat::zeros(GuiRGBImg.size(), CV_8U);

					for (int i = 0; i < 4; ++i)
					{
						Point p;
						p.x = pt[i].x;
						p.y = pt[i].y;
						pts.push_back(p);
					}
					

					//Mat genMat = generateMask(GuiRGBImg, vecPts);
					contour.push_back(pts);

					drawContours(mask, contour, 0, Scalar::all(255), -1);
					GuiRGBImg.copyTo(dst, mask);

					rotate_arbitrarily_angle(dst, rot, angleTemp);
					/*imshow("mask", dst);
					cvWaitKey(0);*/

					//imshow("rot", rot);
					//cvWaitKey(0);

					/*CvPoint tmpPt;
					tmpPt.x = rect.x;
					tmpPt.y = rect.y;
					CvPoint topLeft = getPointAffinedPos(tmpPt, rect.height, rect.width, angleTemp);*/

					box = boxTemp;
					angle = angleTemp;
					axisLong = axisLongTemp;
					axisShort = axisShortTemp;
					cout << "倾斜角度:" << angle << endl;
					//Sleep(10 * 1000);
					//cv::destroyWindow("提取结果图");

					//Mat roi = GuiRGBImg(Rect(pt[1].x,  pt[1].y, pt[3].x - pt[1].x, pt[1].y - pt[0].y));
					int xmin = 2000;
					int ymin = 2000;
					int xmax = 0;
					int ymax = 0;
					int pos1 = 0;
					int pos2 = 0;
					int pos3 = 0;
					int pos4 = 0;

					for (int i = 0; i < 3; i++)
					{
						if (xmin > pt[i].x)
						{
							xmin = pt[i].x;
							pos1 = i;
						}
						if (xmax < pt[i].x)
						{
							xmax = pt[i].x;
							pos4 = i;
						}

					}

					for (int i = 0; i < 3; i++)
					{
						if (ymin > pt[i].y)
						{
							ymin = pt[i].y;
						}
						if (ymax < pt[i].y)
						{
							ymax = pt[i].y;
						}

					}
					//Mat roi = GuiRGBImg(Rect(pt[1].x, pt[1].y, 85, 70));topLeft
					Mat roi = GuiRGBImg(Rect(xmin , ymin , 85, 70));
					//Mat roi = GuiRGBImg(Rect(xmin, ymin, xmax-xmin, ymax-ymin));
					//提取的关键就是Rect(0,0,30,30),其中0 ,0表示感兴趣区域的左上角位置,后面的30,30表示感兴趣部分的宽度和高度
					Mat img1;
					roi.copyTo(img1);//将感兴趣区域赋值到img1;

					Mat ResizeImg, RotationImg;
					Point2f RPt(320, 320);

					resize(img1, ResizeImg, Size(640, 640 * img1.rows / img1.cols));

					rotate_arbitrarily_angle(ResizeImg, RotationImg, angle);

					Mat sc1, dc2;
					//cvCopy(&sc1,&dc2, &boxTemp);
					//GetImageRect(IplImage* orgImage, CvRect rectInImage, IplImage* imgRect)
#ifdef DEBUGP
					/*imshow("cvCopy", dc2);
					cvWaitKey(0);*/
#endif
					//RotationImg = getRotationMatrix2D(RPt, angle, 1);
#ifdef DEBUGP
					/*imshow("getRotationMatrix2D", RotationImg);
					cvWaitKey(0);*/
#endif
					//定义保存图像的完整路径
					char strSaveName[20] = { 0 };
					sprintf(strSaveName, "0000%d", n);
					string strImgSavePath = outPutPath + "\\" + strSaveName;
					//定义保存图像的格式
					strImgSavePath += ".jpg";
					//strImgSavePath += ".png";

					//保存操作
					imwrite(strImgSavePath.c_str(), RotationImg);

					//namedWindow("gag", 1);
#ifdef DEBUGP
					imshow("gag", ResizeImg);
					cvWaitKey(0);
#endif
					return true;
					//IplImage *image=NULL;
					将ROI区域图像保存在image中:左上角x、左上角y、矩形长、宽
					//cvSetImageROI(&(IplImage(GuiRGBImg)), cvRect(pt[0].x, pt[0].y, axisLong, axisShort));
					//cvShowImage("imageROI", &(IplImage(GuiRGBImg)));
					执行cvSetImageROI()之后显示image图像是只显示ROI标识的一部分,即改变了指针image,
					但是它仍旧保留有原来图像的信息,在执行这一句cvResetImageROI(image),之后,image指示原来的图像信息。
					cvResetImageROI(image);
					cvShowImage("image2", image);
					//cvWaitKey(0);
				}
			}
		}
	}
	return false;
}





int main(int argc, const char * argv[])
{
	//string file_path;
	vector<string> files;
	getFiles(filePath, files);
	int size = files.size();


	for (int i = 0; i < size; i++)
	{
		string picname = files[i];
		Mat OriginalImg;
		OriginalImg = imread(picname, IMREAD_COLOR);//读取原始彩色图像
		//OriginalImg = imread("E:\\workspace\\1-MyWork\\2-巡逻机器人资料汇总\\src\\CH-HCNetSDKV6.1.4.6_build20191220_Win64\\hkOpencv\\hkOpenCVtest\\x64\\Release\\002.jpg", IMREAD_COLOR);//读取原始彩色图像
		if (OriginalImg.empty())  //判断图像对否读取成功
		{
			cout << "错误!读取图像失败\n";
			return -1;
		}
		//imshow("原图", OriginalImg); //显示原始图像
		//cvWaitKey(0);
		cout << "Width:" << OriginalImg.rows << "\tHeight:" << OriginalImg.cols << endl;//打印图像长宽


		Mat ResizeImg;
		if (OriginalImg.cols > 640)
			resize(OriginalImg, ResizeImg, Size(640, 640 * OriginalImg.rows / OriginalImg.cols));
		//imshow("尺寸变换图", ResizeImg);
		//cvWaitKey(0);

		if (!picSegment(ResizeImg, GREY, i, 0))
		{
			if (!picSegment(ResizeImg, COLOR, i, 1))
			{
				picSegment(ResizeImg, COLOR, i, 2);
			}
		}

		// insert code here...
		//Mat Imags = imread("D:\\workspace\\hkjpg\\002\\1590649791.jpg");//读取图片
		//imshow("source", Imags);//显示图片在 名为"source"的窗口里
		//Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));//获得用来腐蚀的内核矩阵
		//Mat Imags2;//用来存放腐蚀之后的图片
		//erode(Imags, Imags2, element);//进行腐蚀操作
		//imshow("target", Imags2);//显示图片 在名为"target"的窗口里
		//cvWaitKey(0);//等待按键操作再执行后续 按随意键将进行下一步 return 程序结束

	}//for
	return 0;
}




在这里插入图片描述
效果不是很好,准确率80%左右

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值