* change character binaraztion to spatial_ostu.

* improve the judgement of plate color.
* test new method of adaptiveThreshold.
v1.6alpha
liuruoze 9 years ago
parent dafe058d2e
commit 453fb5faf1

@ -1917,3 +1917,48 @@ Recall:66.2867%, Precise:63.4047%, Fscore:64.8137%.
Recall:66.2867%, Precise:63.4047%, Fscore:64.8137%.
0-error:0%, 1-error:0%, Chinese-precise:100%
总时间:260秒, 平均执行时间:5.2秒
2016-06-28 15:02:49
总图片数:50, Plates count:44, 未识出图片:22, 定位率:50%
Recall:44.5799%, Precise:70.0542%, Fscore:54.4866%.
0-error:45.4545%, 1-error:54.5455%, Chinese-precise:59.0909%
总时间:209秒, 平均执行时间:4.18秒
2016-06-28 15:10:13
总图片数:50, Plates count:44, 未识出图片:22, 定位率:50%
Recall:44.5799%, Precise:70.0542%, Fscore:54.4866%.
0-error:45.4545%, 1-error:54.5455%, Chinese-precise:59.0909%
总时间:342秒, 平均执行时间:6.84秒
2016-06-28 15:42:40
总图片数:5, Plates count:0, 未识出图片:0, 定位率:-1.#IND%
Recall:0%, Precise:0%, Fscore:0%.
0-error:0%, 1-error:0%, Chinese-precise:100%
总时间:38秒, 平均执行时间:7.6秒
2016-06-28 15:55:22
总图片数:5, Plates count:0, 未识出图片:0, 定位率:-1.#IND%
Recall:0%, Precise:0%, Fscore:0%.
0-error:0%, 1-error:0%, Chinese-precise:100%
总时间:31秒, 平均执行时间:6.2秒
2016-06-28 16:24:01
总图片数:5, Plates count:0, 未识出图片:0, 定位率:-1.#IND%
Recall:0%, Precise:0%, Fscore:0%.
0-error:0%, 1-error:0%, Chinese-precise:100%
总时间:29秒, 平均执行时间:5.8秒
2016-06-28 16:37:13
总图片数:5, Plates count:0, 未识出图片:0, 定位率:-1.#IND%
Recall:0%, Precise:0%, Fscore:0%.
0-error:0%, 1-error:0%, Chinese-precise:100%
总时间:26秒, 平均执行时间:5.2秒
2016-06-28 16:38:47
总图片数:5, Plates count:5, 未识出图片:0, 定位率:100%
Recall:96.3172%, Precise:96.3172%, Fscore:96.3172%.
0-error:100%, 1-error:100%, Chinese-precise:100%
总时间:26秒, 平均执行时间:5.2秒
2016-06-28 16:43:35
总图片数:50, Plates count:49, 未识出图片:15, 定位率:69.3878%
Recall:62.3538%, Precise:76.3835%, Fscore:68.6593%.
0-error:50%, 1-error:52.9412%, Chinese-precise:55.8824%
总时间:212秒, 平均执行时间:4.24秒
2016-06-28 17:17:39
总图片数:50, Plates count:49, 未识出图片:15, 定位率:69.3878%
Recall:62.3538%, Precise:76.3835%, Fscore:68.6593%.
0-error:44.1176%, 1-error:52.9412%, Chinese-precise:55.8824%
总时间:300秒, 平均执行时间:6秒

@ -37,14 +37,25 @@ class CCharsRecognise {
Color result = getPlateType(input, true);
if (BLUE == result) color = "蓝牌";
if (YELLOW == result) color = "黄牌";
if (WHITE == result) color = "白牌";
#ifdef OS_WINDOWS
color = utils::utf8_to_gbk(color.c_str());
#endif
return color;
}
//! 设置变量
inline std::string getPlateColor(Color in) const {
std::string color = "未知";
if (BLUE == in) color = "蓝牌";
if (YELLOW == in) color = "黄牌";
if (WHITE == in) color = "白牌";
#ifdef OS_WINDOWS
color = utils::utf8_to_gbk(color.c_str());
#endif
return color;
}
//! 设置变量
inline void setLiuDingSize(int param) {
m_charsSegment->setLiuDingSize(param);
}

@ -11,7 +11,7 @@ class CCharsSegment {
//! 字符分割
int charsSegment(Mat input, std::vector<Mat>& resultVec);
int charsSegment(Mat input, std::vector<Mat>& resultVec, Color color = BLUE);
//! 字符尺寸验证

@ -63,11 +63,14 @@ bool verifyCharSizes(Rect r);
bool verifyPlateSize(Rect mr);
bool verifyRotatedPlateSizes(RotatedRect mr, bool showDebug = false);
// draw rotatedRectangle
void rotatedRectangle(InputOutputArray img, RotatedRect rect,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
// ostu region
void spatial_ostu(InputArray _src, int grid_x, int grid_y, Color type = BLUE);
// Scale to small image (for the purpose of comput mser in large image)
Mat scaleImage(const Mat& image, const Size& maxSize, double& scale_ratio);

@ -23,7 +23,11 @@ namespace easypr {
class CPlate {
public:
CPlate() { }
CPlate() {
m_score = -1;
m_plateStr = "";
m_plateColor = UNKNOWN;
}
CPlate(const CPlate& other) {
m_plateMat = other.m_plateMat;
@ -31,6 +35,7 @@ namespace easypr {
m_platePos = other.m_platePos;
m_plateStr = other.m_plateStr;
m_locateType = other.m_locateType;
m_plateColor = other.m_plateColor;
m_line = other.m_line;
m_leftPoint = other.m_leftPoint;
m_rightPoint = other.m_rightPoint;
@ -51,6 +56,7 @@ namespace easypr {
m_platePos = other.m_platePos;
m_plateStr = other.m_plateStr;
m_locateType = other.m_locateType;
m_plateColor = other.m_plateColor;
m_line = other.m_line;
m_leftPoint = other.m_leftPoint;
m_rightPoint = other.m_rightPoint;
@ -78,6 +84,9 @@ namespace easypr {
inline void setPlateLocateType(LocateType param) { m_locateType = param; }
inline LocateType getPlateLocateType() const { return m_locateType; }
inline void setPlateColor(Color param) { m_plateColor = param; }
inline Color getPlateColor() const { return m_plateColor; }
inline void setPlateScore(double param) { m_score = param; }
inline double getPlateScore() const { return m_score; }
@ -133,6 +142,9 @@ namespace easypr {
//! plate locate type
LocateType m_locateType;
//! plate color type
Color m_plateColor;
//! plate likely
double m_score;

@ -174,6 +174,36 @@
<taggedRectangle x="840" y="361" width="193" height="116" rotation="0" locateType="0">À¶ÅÆ:ÓåBE7773</taggedRectangle>
</taggedRectangles>
</image>
<image>
<imageName>ÕãA26M71</imageName>
<taggedRectangles>
<taggedRectangle x="1018" y="689" width="123" height="32" rotation="0" locateType="2">À¶ÅÆ:ÕãA26M71</taggedRectangle>
</taggedRectangles>
</image>
<image>
<imageName>ÕãC01701</imageName>
<taggedRectangles>
<taggedRectangle x="418" y="639" width="104" height="27" rotation="0" locateType="2">À¶ÅÆ:ÕãC01701</taggedRectangle>
</taggedRectangles>
</image>
<image>
<imageName>ÔÁA82349</imageName>
<taggedRectangles>
<taggedRectangle x="1056" y="564" width="121" height="30" rotation="0" locateType="2">»ÆÅÆ:ÔÁA82349</taggedRectangle>
</taggedRectangles>
</image>
<image>
<imageName>ÔÁA961F3</imageName>
<taggedRectangles>
<taggedRectangle x="1177" y="683" width="131" height="32" rotation="1" locateType="2">À¶ÅÆ:ÔÁA961F3</taggedRectangle>
</taggedRectangles>
</image>
<image>
<imageName>ÔÁBDB720</imageName>
<taggedRectangles>
<taggedRectangle x="918" y="657" width="120" height="30" rotation="0" locateType="2">À¶ÅÆ:ÔÁBDB720</taggedRectangle>
</taggedRectangles>
</image>
<image>
<imageName>ÔÁA2HQ34</imageName>
<taggedRectangles>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because it is too large Load Diff

@ -44,7 +44,19 @@ int CCharsRecognise::charsRecognise(CPlate& plate, std::string& plateLicense) {
Mat plateMat = plate.getPlateMat();
int result = m_charsSegment->charsSegment(plateMat, matChars);
// 判断车牌颜色以此确认threshold方法
Color color;
if (plate.getPlateLocateType() == CMSER) {
color = plate.getPlateColor();
}
else {
int w = plateMat.cols;
int h = plateMat.rows;
Mat tmpMat = plateMat(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8));
color = getPlateType(tmpMat, true);
}
int result = m_charsSegment->charsSegment(plateMat, matChars, color);
//std::cout << "charsSegment:" << result << std::endl;
if (result == 0) {
//for (auto block : matChars) {
@ -67,12 +79,11 @@ int CCharsRecognise::charsRecognise(CPlate& plate, std::string& plateLicense) {
plate.addReutCharacter(charResult);
}
}
if (plateLicense.size() < 7) {
return -1;
if (plateLicense.size() < 7) {
return -1;
}
}
return result;
}

@ -77,59 +77,66 @@ Mat CCharsSegment::preprocessChar(Mat in) {
//! 字符分割与排序
int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec) {
int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec, Color color) {
if (!input.data) return 0x01;
int w = input.cols;
int h = input.rows;
Mat tmpMat = input(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8));
// 判断车牌颜色以此确认threshold方法
Color plateType = getPlateType(tmpMat, true);
Mat input_grey;
cvtColor(input, input_grey, CV_BGR2GRAY);
if (1) {
imshow("plate", input_grey);
waitKey(0);
destroyWindow("plate");
}
Mat img_threshold;
// 二值化
// 根据车牌的不同颜色使用不同的阈值判断方法
// TODO使用MSER来提取这些轮廓
if (BLUE == plateType) {
// cout << "BLUE" << endl;
img_threshold = input_grey.clone();
int w = input_grey.cols;
int h = input_grey.rows;
Mat tmp = input_grey(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8));
int threadHoldV = ThresholdOtsu(tmp);
threshold(input_grey, img_threshold, threadHoldV, 255, CV_THRESH_BINARY);
} else if (YELLOW == plateType) {
// cout << "YELLOW" << endl;
img_threshold = input_grey.clone();
int w = input_grey.cols;
int h = input_grey.rows;
Mat tmp = input_grey(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8));
int threadHoldV = ThresholdOtsu(tmp);
// utils::imwrite("resources/image/tmp/inputgray2.jpg", input_grey);
threshold(input_grey, img_threshold, threadHoldV, 255,
CV_THRESH_BINARY_INV);
} else if (WHITE == plateType) {
// cout << "WHITE" << endl;
threshold(input_grey, img_threshold, 10, 255,
CV_THRESH_OTSU + CV_THRESH_BINARY_INV);
} else {
// cout << "UNKNOWN" << endl;
threshold(input_grey, img_threshold, 10, 255,
CV_THRESH_OTSU + CV_THRESH_BINARY);
//if (BLUE == plateType) {
// // cout << "BLUE" << endl;
// img_threshold = input_grey.clone();
// int w = input_grey.cols;
// int h = input_grey.rows;
// Mat tmp = input_grey(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8));
// int threadHoldV = ThresholdOtsu(tmp);
// threshold(input_grey, img_threshold, threadHoldV, 255, CV_THRESH_BINARY);
//} else if (YELLOW == plateType) {
// // cout << "YELLOW" << endl;
// img_threshold = input_grey.clone();
// int w = input_grey.cols;
// int h = input_grey.rows;
// Mat tmp = input_grey(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8));
// int threadHoldV = ThresholdOtsu(tmp);
// // utils::imwrite("resources/image/tmp/inputgray2.jpg", input_grey);
// threshold(input_grey, img_threshold, threadHoldV, 255,
// CV_THRESH_BINARY_INV);
//} else if (WHITE == plateType) {
// // cout << "WHITE" << endl;
// threshold(input_grey, img_threshold, 10, 255,
// CV_THRESH_OTSU + CV_THRESH_BINARY_INV);
//} else {
// // cout << "UNKNOWN" << endl;
// threshold(input_grey, img_threshold, 10, 255,
// CV_THRESH_OTSU + CV_THRESH_BINARY);
//}
Color plateType = color;
img_threshold = input_grey.clone();
spatial_ostu(img_threshold, 8, 2, plateType);
if (0) {
imshow("plate", img_threshold);
waitKey(0);
destroyWindow("plate");
}
// 去除车牌上方的柳钉以及下方的横线等干扰
@ -191,6 +198,13 @@ int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec) {
else
return 0x04;
if (1) {
rectangle(img_threshold, chineseRect, Scalar(255));
imshow("plate", img_threshold);
waitKey(0);
destroyWindow("plate");
}
//新建一个全新的排序Rect
//将中文字符Rect第一个加进来因为它肯定是最左边的
//其余的Rect只按照顺序去6个车牌只可能是7个字符这样可以避免阴影导致的“1”字符
@ -214,28 +228,34 @@ int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec) {
Mat newRoi;
if (BLUE == plateType) {
/* img_threshold = auxRoi.clone();
int w = input_grey.cols;
int h = input_grey.rows;
Mat tmp = input_grey(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8));
int threadHoldV = ThresholdOtsu(tmp);*/
threshold(auxRoi, newRoi, 5, 255, CV_THRESH_BINARY + CV_THRESH_OTSU);
//newRoi = auxRoi.clone();
//spatial_ostu(newRoi, 5, 5, plateType);
if (i != 0)
threshold(auxRoi, newRoi, 0, 255, CV_THRESH_BINARY + CV_THRESH_OTSU);
else
adaptiveThreshold(auxRoi, newRoi, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, 0);
} else if (YELLOW == plateType) {
threshold(auxRoi, newRoi, 5, 255, CV_THRESH_BINARY_INV + CV_THRESH_OTSU);
threshold(auxRoi, newRoi, 0, 255, CV_THRESH_BINARY_INV + CV_THRESH_OTSU);
} else if (WHITE == plateType) {
threshold(auxRoi, newRoi, 5, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV);
threshold(auxRoi, newRoi, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV);
} else {
threshold(auxRoi, newRoi, 5, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
threshold(auxRoi, newRoi, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
}
// 归一化大小
newRoi = preprocessChar(newRoi);
// 每个字符图块输入到下面的步骤进行处理
if (1) {
if (i == 0) {
imshow("chinese", newRoi);
waitKey(0);
destroyWindow("chinese");
}
}
// 每个字符图块输入到下面的步骤进行处理
resultVec.push_back(newRoi);
}

@ -360,15 +360,17 @@ Color getPlateType(const Mat &src, const bool adaptive_minsv) {
// cout << "WHITE" << endl;
return WHITE;
} else {
// cout << "OTHER" << endl;
//std::cout << "OTHER" << std::endl;
// 如果任意一者都不大于阈值,则取值最大者
max_percent = blue_percent > yellow_percent ? blue_percent : yellow_percent;
/*max_percent = blue_percent > yellow_percent ? blue_percent : yellow_percent;
max_color = blue_percent > yellow_percent ? BLUE : YELLOW;
max_color = max_percent > white_percent ? max_color : WHITE;*/
max_color = max_percent > white_percent ? max_color : WHITE;
return max_color;
// always return blue
return BLUE;
}
}
@ -1839,6 +1841,30 @@ Mat mserMatch(const Mat &src, Mat &match, const Color r,
}
void spatial_ostu(InputArray _src, int grid_x, int grid_y, Color type) {
Mat src = _src.getMat();
int width = src.cols / grid_x;
int height = src.rows / grid_y;
// iterate through grid
for (int i = 0; i < grid_y; i++) {
for (int j = 0; j < grid_x; j++) {
Mat src_cell = Mat(src, Range(i*height, (i + 1)*height), Range(j*width, (j + 1)*width));
if (type == BLUE) {
threshold(src_cell, src_cell, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
}
else if (type == YELLOW) {
threshold(src_cell, src_cell, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV);
}
else {
threshold(src_cell, src_cell, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
}
}
}
}
bool mat_valid_position(const Mat& mat, int row, int col) {
return row >= 0 && col >= 0 && row < mat.rows && col < mat.cols;
}

@ -84,6 +84,21 @@ void getLBPFeatures(const Mat& image, Mat& features) {
Mat grayImage;
cvtColor(image, grayImage, CV_RGB2GRAY);
//if (1) {
// imshow("grayImage", grayImage);
// waitKey(0);
// destroyWindow("grayImage");
//}
//spatial_ostu(grayImage, 8, 2);
//if (1) {
// imshow("grayImage", grayImage);
// waitKey(0);
// destroyWindow("grayImage");
//}
Mat lbpimage;
lbpimage = libfacerec::olbp(grayImage);
Mat lbp_hist = libfacerec::spatial_histogram(lbpimage, 32, 4, 4);

@ -179,13 +179,13 @@ namespace easypr {
result_resize.cols, result_resize.rows));
addWeighted(imageRoi, 0, result_resize, 1, 0, imageRoi);
if (1) {
if (0) {
imshow("EasyPR", img_window);
waitKey(1000);
destroyWindow("EasyPR");
}
if (1) {
if (0) {
std::stringstream ss(std::stringstream::in | std::stringstream::out);
ss << "resources/image/tmp/Result/plate_" << img_index << ".jpg";
imwrite(ss.str(), img_window);

@ -1115,6 +1115,7 @@ int CPlateLocate::plateMserLocate(Mat src, vector<CPlate> &candPlates, int img_i
resize(src(outputRect), plate_mat, plate_mat.size(), 0, 0, INTER_AREA);
plate.setPlateMat(plate_mat);
plate.setPlateColor(color);
candPlates.push_back(plate);
}

@ -321,20 +321,39 @@ int CPlateRecognize::plateRecognize(Mat src, std::vector<CPlate> &licenseVec, in
//依次识别每个车牌内的符号
for (size_t j = 0; j < num; j++) {
CPlate item = plateVec.at(j);
Mat plate = item.getPlateMat();
Mat plateMat = item.getPlateMat();
//获取车牌颜色
std::string plateType = getPlateColor(plate);
if (0) {
imshow("plate", plateMat);
waitKey(0);
destroyWindow("plate");
}
Color color = item.getPlateColor();
if (color == UNKNOWN) {
color = getPlateType(plateMat, true);
item.setPlateColor(color);
}
std::string plateColor = getPlateColor(color);
if (1) {
std::cout << "plateColor:" << plateColor << std::endl;
}
//获取车牌号
std::string plateIdentify = "";
int resultCR = charsRecognise(item, plateIdentify);
if (resultCR == 0) {
std::string license = plateType + ":" + plateIdentify;
std::string license = plateColor + ":" + plateIdentify;
item.setPlateStr(license);
licenseVec.push_back(item);
}
else {
if (1) {
std::cout << "resultCR:" << resultCR << std::endl;
}
}
}
//完整识别过程到此结束
@ -347,13 +366,13 @@ int CPlateRecognize::plateRecognize(Mat src, std::vector<CPlate> &licenseVec, in
for (size_t j = 0; j < num; j++) {
CPlate item = plateVec[j];
Mat plate = item.getPlateMat();
Mat plateMat = item.getPlateMat();
int height = 36;
int width = 136;
if (height * index + height < result.rows) {
Mat imageRoi = result(Rect(0, 0 + height * index, width, height));
addWeighted(imageRoi, 0, plate, 1, 0, imageRoi);
addWeighted(imageRoi, 0, plateMat, 1, 0, imageRoi);
}
index++;

@ -86,7 +86,7 @@ namespace easypr {
CPlateRecognize pr;
// 设置Debug模式
pr.setResultShow(true);
pr.setResultShow(false);
pr.setLifemode(true);
// 设置要处理的一张图片中最多有多少车牌
pr.setMaxPlates(4);
@ -172,9 +172,8 @@ namespace easypr {
XMLNode rectangleNodes = xNode.addChild("taggedRectangles");
vector<CPlate> plateVec;
//int result = pr.plateRecognize(src, plateVec, i);
int result = pd.plateDetect(src, plateVec, i);
int result = pr.plateRecognize(src, plateVec, i);
//int result = pd.plateDetect(src, plateVec, i);
// get the ground truth and compare it with the detect list;
map<string, vector<CPlate>>::iterator it;
@ -184,6 +183,9 @@ namespace easypr {
//cout << it->first << endl;
plateVecGT = it->second;
}
else {
cout << "No ground truth found!" << endl;
}
// calucate the detect recall
// use icdar 2003 evalution protoocal
@ -232,20 +234,7 @@ namespace easypr {
string matchLicense = spilt_plate.at(1);
cout << matchPlateLicense << " (d)" << endl;
XMLNode rectangleNode = rectangleNodes.addChild("taggedRectangle");
RotatedRect rr = matchPlate->getPlatePos();
LocateType locateType = matchPlate->getPlateLocateType();
rectangleNode.addAttribute("x", to_string((int)rr.center.x).c_str());
rectangleNode.addAttribute("y", to_string((int)rr.center.y).c_str());
rectangleNode.addAttribute("width", to_string((int)rr.size.width).c_str());
rectangleNode.addAttribute("height", to_string((int)rr.size.height).c_str());
rectangleNode.addAttribute("rotation", to_string((int)rr.angle).c_str());
rectangleNode.addAttribute("locateType", to_string(locateType).c_str());
rectangleNode.addText(matchPlate->getPlateStr().c_str());
int diff = utils::levenshtein_distance(license, matchLicense);
if (diff == 0) {
non_error_count++;
@ -306,6 +295,19 @@ namespace easypr {
Rect_<float> plateRect_d;
calcSafeRect(platePos_d, src, plateRect_d);
XMLNode rectangleNode = rectangleNodes.addChild("taggedRectangle");
RotatedRect rr = platePos_d;
LocateType locateType = plate_d.getPlateLocateType();
rectangleNode.addAttribute("x", to_string((int)rr.center.x).c_str());
rectangleNode.addAttribute("y", to_string((int)rr.center.y).c_str());
rectangleNode.addAttribute("width", to_string((int)rr.size.width).c_str());
rectangleNode.addAttribute("height", to_string((int)rr.size.height).c_str());
rectangleNode.addAttribute("rotation", to_string((int)rr.angle).c_str());
rectangleNode.addAttribute("locateType", to_string(locateType).c_str());
rectangleNode.addText(plate_d.getPlateStr().c_str());
for (auto plate_g : plateVecGT) {
RotatedRect platePos_g = plate_g.getPlatePos();
Rect_<float> plateRect_g;

Loading…
Cancel
Save