You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
EasyPR/src/core/plate_locate.cpp

301 lines
7.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "../include/plate_locate.h"
/*! \namespace easypr
Namespace where all the C++ EasyPR functionality resides
*/
namespace easypr{
const float DEFAULT_ERROR = 0.6;
const float DEFAULT_ASPECT = 3.75;
CPlateLocate::CPlateLocate()
{
//cout << "CPlateLocate" << endl;
m_GaussianBlurSize = DEFAULT_GAUSSIANBLUR_SIZE;
m_MorphSizeWidth = DEFAULT_MORPH_SIZE_WIDTH;
m_MorphSizeHeight = DEFAULT_MORPH_SIZE_HEIGHT;
m_error = DEFAULT_ERROR;
m_aspect = DEFAULT_ASPECT;
m_verifyMin = DEFAULT_VERIFY_MIN;
m_verifyMax = DEFAULT_VERIFY_MAX;
m_angle = DEFAULT_ANGLE;
m_debug = DEFAULT_DEBUG;
}
//! 生活模式与工业模式切换
//! 如果为真,则设置各项参数为定位生活场景照片(如百度图片)的参数,否则恢复默认值。
void CPlateLocate::setLifemode(bool param)
{
if(param == true)
{
setGaussianBlurSize(5);
setMorphSizeWidth(9);
setMorphSizeHeight(3);
setVerifyError(0.9);
setVerifyAspect(4);
setVerifyMin(1);
setVerifyMax(30);
}
else
{
setGaussianBlurSize(DEFAULT_GAUSSIANBLUR_SIZE);
setMorphSizeWidth(DEFAULT_MORPH_SIZE_WIDTH);
setMorphSizeHeight(DEFAULT_MORPH_SIZE_HEIGHT);
setVerifyError(DEFAULT_ERROR);
setVerifyAspect(DEFAULT_ASPECT);
setVerifyMin(DEFAULT_VERIFY_MIN);
setVerifyMax(DEFAULT_VERIFY_MAX);
}
}
//! 对minAreaRect获得的最小外接矩形用纵横比进行判断
bool CPlateLocate::verifySizes(RotatedRect mr)
{
float error = m_error;
//Spain car plate size: 52x11 aspect 4,7272
//China car plate size: 440mm*140mmaspect 3.142857
float aspect = m_aspect;
//Set a min and max area. All other patchs are discarded
//int min= 1*aspect*1; // minimum area
//int max= 2000*aspect*2000; // maximum area
int min= 44*14*m_verifyMin; // minimum area
int max= 44*14*m_verifyMax; // maximum area
//Get only patchs that match to a respect ratio.
float rmin= aspect-aspect*error;
float rmax= aspect+aspect*error;
int area= mr.size.height * mr.size.width;
float r = (float)mr.size.width / (float)mr.size.height;
if(r < 1)
{
r= (float)mr.size.height / (float)mr.size.width;
}
if(( area < min || area > max ) || ( r < rmin || r > rmax ))
{
return false;
}
else
{
return true;
}
}
//! 显示最终生成的车牌图像,便于判断是否成功进行了旋转。
Mat CPlateLocate::showResultMat(Mat src, Size rect_size, Point2f center, int index)
{
Mat img_crop;
getRectSubPix(src, rect_size, center, img_crop);
if(m_debug)
{
stringstream ss(stringstream::in | stringstream::out);
ss << "image/tmp/debug_crop_" << index << ".jpg";
imwrite(ss.str(), img_crop);
}
Mat resultResized;
resultResized.create(HEIGHT, WIDTH, TYPE);
resize(img_crop, resultResized, resultResized.size(), 0, 0, INTER_CUBIC);
if(m_debug)
{
stringstream ss(stringstream::in | stringstream::out);
ss << "image/tmp/debug_resize_" << index << ".jpg";
imwrite(ss.str(), resultResized);
}
return resultResized;
}
//! 定位车牌图像
//! src 原始图像
//! resultVec 一个Mat的向量存储所有抓取到的图像
//! 成功返回0否则返回-1
int CPlateLocate::plateLocate(Mat src, vector<Mat>& resultVec)
{
Mat src_blur, src_gray;
Mat grad;
int scale = SOBEL_SCALE;
int delta = SOBEL_DELTA;
int ddepth = SOBEL_DDEPTH;
if( !src.data )
{ return -1; }
//高斯模糊。Size中的数字影响车牌定位的效果。
GaussianBlur( src, src_blur, Size(m_GaussianBlurSize, m_GaussianBlurSize),
0, 0, BORDER_DEFAULT );
if(m_debug)
{
stringstream ss(stringstream::in | stringstream::out);
ss << "image/tmp/debug_GaussianBlur" << ".jpg";
imwrite(ss.str(), src_blur);
}
/// Convert it to gray
cvtColor( src_blur, src_gray, CV_RGB2GRAY );
if(m_debug)
{
stringstream ss(stringstream::in | stringstream::out);
ss << "image/tmp/debug_gray" << ".jpg";
imwrite(ss.str(), src_gray);
}
/// Generate grad_x and grad_y
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y;
/// Gradient X
//Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );
Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
convertScaleAbs( grad_x, abs_grad_x );
/// Gradient Y
//Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );
Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );
convertScaleAbs( grad_y, abs_grad_y );
/// Total Gradient (approximate)
addWeighted( abs_grad_x, SOBEL_X_WEIGHT, abs_grad_y, SOBEL_Y_WEIGHT, 0, grad );
//Laplacian( src_gray, grad_x, ddepth, 3, scale, delta, BORDER_DEFAULT );
//convertScaleAbs( grad_x, grad );
if(m_debug)
{
stringstream ss(stringstream::in | stringstream::out);
ss << "image/tmp/debug_Sobel" << ".jpg";
imwrite(ss.str(), grad);
}
Mat img_threshold;
threshold(grad, img_threshold, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);
//threshold(grad, img_threshold, 75, 255, CV_THRESH_BINARY);
if(m_debug)
{
stringstream ss(stringstream::in | stringstream::out);
ss << "image/tmp/debug_threshold" << ".jpg";
imwrite(ss.str(), img_threshold);
}
Mat element = getStructuringElement(MORPH_RECT, Size(m_MorphSizeWidth, m_MorphSizeHeight) );
morphologyEx(img_threshold, img_threshold, MORPH_CLOSE, element);
if(m_debug)
{
stringstream ss(stringstream::in | stringstream::out);
ss << "image/tmp/debug_morphology" << ".jpg";
imwrite(ss.str(), img_threshold);
}
//Find 轮廓 of possibles plates
vector< vector< Point> > contours;
findContours(img_threshold,
contours, // a vector of contours
CV_RETR_EXTERNAL, // 提取外部轮廓
CV_CHAIN_APPROX_NONE); // all pixels of each contours
Mat result;
if(m_debug)
{
//// Draw blue contours on a white image
src.copyTo(result);
drawContours(result, contours,
-1, // draw all contours
Scalar(0,0,255), // in blue
1); // with a thickness of 1
stringstream ss(stringstream::in | stringstream::out);
ss << "image/tmp/debug_Contours" << ".jpg";
imwrite(ss.str(), result);
}
//Start to iterate to each contour founded
vector<vector<Point> >::iterator itc = contours.begin();
vector<RotatedRect> rects;
//Remove patch that are no inside limits of aspect ratio and area.
int t = 0;
while (itc != contours.end())
{
//Create bounding rect of object
RotatedRect mr = minAreaRect(Mat(*itc));
//large the rect for more
if( !verifySizes(mr))
{
itc = contours.erase(itc);
}
else
{
++itc;
rects.push_back(mr);
}
}
int k = 1;
for(int i=0; i< rects.size(); i++)
{
RotatedRect minRect = rects[i];
if(verifySizes(minRect))
{
// rotated rectangle drawing
// Get rotation matrix
// 旋转这部分代码确实可以将某些倾斜的车牌调整正,
// 但是它也会误将更多正的车牌搞成倾斜!所以综合考虑,还是不使用这段代码。
// 2014-08-14,由于新到的一批图片中发现有很多车牌是倾斜的,因此决定再次尝试
// 这段代码。
if(m_debug)
{
Point2f rect_points[4];
minRect.points( rect_points );
for( int j = 0; j < 4; j++ )
line( result, rect_points[j], rect_points[(j+1)%4], Scalar(0,255,255), 1, 8 );
}
float r = (float)minRect.size.width / (float)minRect.size.height;
float angle = minRect.angle;
Size rect_size = minRect.size;
if (r < 1)
{
angle = 90 + angle;
swap(rect_size.width, rect_size.height);
}
//如果抓取的方块旋转超过m_angle角度则不是车牌放弃处理
if (angle - m_angle < 0 && angle + m_angle > 0)
{
//Create and rotate image
Mat rotmat = getRotationMatrix2D(minRect.center, angle, 1);
Mat img_rotated;
warpAffine(src, img_rotated, rotmat, src.size(), CV_INTER_CUBIC);
Mat resultMat;
resultMat = showResultMat(img_rotated, rect_size, minRect.center, k++);
resultVec.push_back(resultMat);
}
}
}
if(m_debug)
{
stringstream ss(stringstream::in | stringstream::out);
ss << "image/tmp/debug_result" << ".jpg";
imwrite(ss.str(), result);
}
return 0;
}
} /*! \namespace easypr*/