从图片识别到方框并且截取出来,以下是全部代码:
// 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%左右