原理:首先对图片进行一系列的处理,步骤:去噪——sobel滤波——otsu阈值化——闭运算;然后绘制矩形,选择最接近的一个矩形;最后矩形轮廓提取。
#include<opencv2/opencv.hpp>
#include<iostream>
#include<opencv2/highgui/highgui_c.h>
#include<vector>
#include<cstdlib>
using namespace cv;
using namespace std;
bool verify(RotatedRect rect) {
float error = 0.4;
const float aspect = 4.7272;
int min = 15 * aspect * 15; // 面积下限
int max = 125 * aspect * 125; // 面积上限
float rmin = aspect - aspect * error; // 宽高比下限
float rmax = aspect + aspect * error; // 宽高比上限
int area = rect.size.width * rect.size.height; // 计算面积
float r = rect.size.width / rect.size.height; // 计算宽高比
r = r < 1 ? 1 / r : r;
return area >= min && area <= max && r >= rmin && r <= rmax;
}
int main()
{
string in = "D://2.jpg";
//将图像转换为单通道灰度图像
Mat image = imread(in, IMREAD_GRAYSCALE);
Mat image2 = imread(in);
Mat image3 = imread(in, IMREAD_GRAYSCALE);
if (image.empty()) {
return -1;
}
imshow("【原始图】", image);
blur(image, image, Size(5, 5)); //均值滤波
imshow("【去噪后】", image);
Sobel(image, image, CV_8U, 1, 0, 3, 1, 0);
imshow("【sobel滤波】", image);
threshold(image, image, 0, 255, CV_THRESH_OTSU);
imshow("【otsu阈值化】", image);
Mat element = getStructuringElement(MORPH_RECT, Size(17, 3));
morphologyEx(image, image, CV_MOP_CLOSE, element); //形态学变换
imshow("【闭运算】", image);
vector<vector<Point>> contours;
findContours(image, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
map<int, RotatedRect> _map;//定义了一个用int作为关键字检索矩形的map对象
for (int i = 0; i < contours.size(); i++) {
drawContours(image, contours, i, Scalar(255), 1); // 绘制轮廓
// 绘制矩形。minAreaRect()函数作用是包含点集最小面积的矩形。
//这个矩形是可以有偏转角度的,可以与图像的边界不平行,()输入的点集,输出是矩形的四个点坐标
RotatedRect rect = minAreaRect(contours[i]);
Point2f vertices[4]; //2维float类型的点
rect.points(vertices); //提取旋转矩形的四个角点
for (int i = 0; i < 4; i++) {
line(image, vertices[i], vertices[(i + 1) % 4], Scalar(255), 2); //四个角点连成线,最终形成旋转的矩形。
}
// 验证
if (verify(rect)) {
_map[i] = rect;
}
}
imshow("【轮廓提取】", image);
// 绘制通过验证的矩形
int min_diff = 100000;
int index = 0;
const float square = 27.75;
map<int, RotatedRect>::iterator iter;//迭代器(iterator)是一种检查容器内元素并遍历元素的数据类型
iter = _map.begin();
while (iter != _map.end()) {
RotatedRect rect = iter->second;
Point2f vertices[4];
rect.points(vertices);
for (int j = 0; j < 4; j++) {
line(image, vertices[j], vertices[(j + 1) % 4], Scalar(255), 10);
}
// 选择最接近的矩形(没看明白)
int perimeter = arcLength(contours[iter->first], true); //计算封闭的轮廓长度
int area = contourArea(contours[iter->first]); //计算轮廓面积
if (area != 0) {
int squareness = perimeter * perimeter / area;
float diff = abs(squareness - square);
if (diff < min_diff) {
min_diff = diff;
index = iter->first;
}
}
iter++;
}
imshow("【通过验证】", image);
// 绘制最接近的矩形
RotatedRect rect = _map[index];
Point2f vertices[4];
rect.points(vertices);
for (int i = 0; i < 4; i++) {
cout << " asdf" << endl;
line(image2, vertices[i], vertices[(i + 1) % 4], Scalar(0, 255, 0), 10);
}
imshow("【最接近的矩形】", image2);
// 图像切割
Mat image_crop;
// 始终保持宽 > 高
Size rect_size = rect.size;
if (rect_size.width < rect_size.height) {
swap(rect_size.width, rect_size.height); //交换
}
getRectSubPix(image3, rect.size, rect.center, image_crop); //从原图像中提取一个感兴趣的矩形区域图像
imshow("【切割后的车牌】", image_crop);
waitKey(0);
return 0;
}