测试代码来源https://blog.csdn.net/u011808673/article/details/78510830#commentBox
.请认真阅读以上文章
.
.请认真阅读以上文章
.
.请认真阅读以上文章
.
.
。其实不阅读上述文章也可以看懂本文hhhh
目录
改成如下代码测试:
Makefile
CXX ?= g++
CXXFLAGS += -c -std=c++11 -Wall $(shell pkg-config --cflags opencv4)
LDFLAGS += $(shell pkg-config --libs --static opencv4)
all: opencv_example
opencv_example: example.o; $(CXX) $< -o $@ $(LDFLAGS)
%.o: %.cpp; $(CXX) $< -o $@ $(CXXFLAGS)
clean: ; rm -f example.o opencv_example
#https://blog.csdn.net/new_delete_/article/details/84797041这是他写的Makefile,请认真阅读
ubuntu@ubuntu:/$ sudo -s
root@ubuntu:/home/ubuntu/mycpp#/export PKG_CONFIG_PATH=/usr/local/opencv/lib/pkgconfig:$PKG_CONFIG_PATH
root@ubuntu:/home/ubuntu/mycpp#make
root@ubuntu:/home/ubuntu/mycpp#./opencv_example
【吐槽杀伤力专业八级】.........每次都要sudo,还要在命令行打export PKG_CONFIG_PATH=/usr/local/opencv4/lib/pkgconfig:$PKG_CONFIG_PATH
写在配置文件根本不生效。。。。。。。。。
example.cpp
#include "/usr/local/opencv4/include/opencv4/opencv2/opencv.hpp"
#include "/usr/local/opencv4/include/opencv4/opencv2/core/core.hpp"
#include "/usr/local/opencv4/include/opencv4/opencv2/highgui/highgui.hpp"
#include "/usr/local/opencv4/include/opencv4/opencv2/imgproc.hpp"
#include "/usr/local/opencv4/include/opencv4/opencv2/ml/ml.hpp"
#include <iostream>
#include <sstream>
extern "C"
{
#include <stdlib.h>
}
using namespace std;
using namespace cv;
using namespace ml;
int main_licenseprogress(Mat &image);
int main_num_reconginzed(const String photo_path_to_be_reconginzed);
Ptr<StatModel> loadMLPClassifiler();
void calcGradientFeat(Mat &imgSrc, vector<float> &feat);
string car_photo_path = "/home/q/mycpp/license/license";
string xml_path = "abc.xml";
string charSamples_path = "/home/q/opencv/charSamples/";
int main()
{
Mat image;
for (int i = 2; i <= 2; i++)
{
ostringstream oss;
oss << car_photo_path << i << ".jpg";
cout << "car_photo_path = " << car_photo_path << "str = " << oss.str() << endl;
image = imread(oss.str(), 1);
main_licenseprogress(image);
}
return 0;
}
int main_licenseprogress(Mat &image)
{
imshow("test", image);
//waitKey(1000);
//灰度化
Mat gray_image;
cvtColor(image, gray_image, COLOR_RGB2GRAY);
imshow("test", gray_image);
imwrite("license3_gray.jpg", gray_image);
//waitKey(1000);
//平滑处理 中值滤波
Mat blur_image;
medianBlur(gray_image, blur_image, 3);
imwrite("blur_image.jpg", blur_image);
//imshow("test", gray_image);
waitKey(1000);
//自适应二值化处理 由于candy 算子产生的图像已经是二值化图像,所以这里不做处理
//Mat threadhold_image;
//threshold(blur_image, threadhold_image, 200, 255, CV_THRESH_BINARY);
//imshow("test", threadhold_image);
waitKey(1000);
//Candy/sobel 边缘检测:
float mask[3][3] = {{1, 2, 1}, {0, 0, 0}, {-1, -2, -1}};
Mat y_mask = Mat(3, 3, CV_32F, mask) / 8;
Mat x_mask = y_mask.t(); // 转置
Mat sobelX, sobelY;
filter2D(blur_image, sobelX, CV_32F, x_mask);
filter2D(blur_image, sobelY, CV_32F, y_mask);
sobelX = abs(sobelX);
sobelY = abs(sobelY);
Mat candy_image;
Canny(blur_image, candy_image, 500, 250, 3);
//imshow("test", candy_image);
waitKey(1000);
//imshow("test", sobelY);
waitKey(1000);
imwrite("candy_image.jpg", candy_image);
//形态学处理
//图片膨胀处理
Mat dilate_image, erode_image;
//自定义 核进行 x 方向的膨胀腐蚀
Mat elementX = getStructuringElement(MORPH_RECT, Size(25, 1));
Mat elementY = getStructuringElement(MORPH_RECT, Size(1, 19));
Point point(-1, -1);
dilate(candy_image, dilate_image, elementX, point, 2);
imwrite("dilate_image.jpg", dilate_image);
erode(dilate_image, erode_image, elementX, point, 4);
imwrite("erode_image.jpg", erode_image);
dilate(erode_image, dilate_image, elementX, point, 2);
imwrite("dilate_image1.jpg", dilate_image);
//自定义 核进行 Y 方向的膨胀腐蚀
erode(dilate_image, erode_image, elementY, point, 1);
imwrite("erode_image1.jpg", erode_image);
dilate(erode_image, dilate_image, elementY, point, 2);
//imshow("test", dilate_image);
waitKey(1000);
imwrite("dilate_image2.jpg", dilate_image);
//噪声处理
//平滑处理 中值滤波
Mat blurr_image;
medianBlur(dilate_image, blurr_image, 15);
medianBlur(blurr_image, blurr_image, 15);
imshow("test", blurr_image);
//waitKey(1000);
//矩形轮廓查找与筛选:
Mat contour_image;
//深拷贝 查找轮廓会改变源图像信息,需要重新 拷贝 图像
contour_image = blurr_image.clone();
vector<vector<Point>> contours;
findContours(contour_image, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//画出轮廓
drawContours(contour_image, contours, -1, Scalar(255), 1);
//imshow("test", contour_image);
waitKey(1000);
//Mat cannyy_image;
//Canny(contour_image, cannyy_image, 500, 200, 3);
//hough 直线
//vector<Vec4i> lines;
//HoughLinesP(cannyy_image, lines, 1, CV_PI / 180, 20, 10, 0);
//for (size_t i = 0; i < lines.size(); i++) {
// Vec4i l = lines[i];
// line(image, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 0, 255), 1, LINE_AA);
// cout << "直线: "<< i <<endl;
//}
//imshow("test", image);
waitKey(1000);
//轮廓表示为一个矩形 车牌提取
Mat roi_image;
vector<Point> rectPoint;
for (int i = 0; i < contours.size(); i++)
{
Rect r = boundingRect(Mat(contours[i]));
//RotatedRect r = minAreaRect(Mat(contours[i]));
cout << "contours " << i << " height = " << r.height << " width = " << r.width << "rate = " << ((float)r.width / r.height) << endl;
if ((float)r.width / r.height >= 2.2 && (float)r.width / r.height <= 3.6)
{
cout << "r.x = " << r.x << " r.y = " << r.y << endl;
rectangle(contour_image, r, Scalar(0, 0, 255), 2);
imwrite("contour_image.jpg", contour_image);
Point p1, p2, p3, p4;
p1.x = r.x;
p1.y = r.y;
p2.x = r.x + r.width;
p2.x = r.y;
p3.x = r.x + r.width;
p3.y = r.y + r.height;
p4.x = r.x;
p4.y = r.y + r.height;
rectPoint.push_back(p1);
rectPoint.push_back(p2);
rectPoint.push_back(p3);
rectPoint.push_back(p4);
for (int j = 0; j < contours[i].size(); j++)
{
cout << "point = " << contours[i][j] << endl;
}
//rectangle(image, r, Scalar(0, 0, 255), 3);
roi_image = image(r);
}
}
imshow("test", roi_image);
//waitKey(1000);
imwrite("roi_image.jpg", roi_image);
//图片放大
Mat large_image;
int col = roi_image.cols, row = roi_image.rows;
resize(roi_image, large_image, Size(300, 300 * row / col));
imshow("test", large_image);
waitKey(2000);
//车牌分割
//灰度化
Mat roi_gray_image;
cvtColor(large_image, roi_gray_image, COLOR_RGB2GRAY);
imshow("test", roi_gray_image);
//waitKey(1000);
//中值滤波 增强边缘
//Candy 边缘检测
Mat candy_roi_image;
Canny(roi_gray_image, candy_roi_image, 450, 120, 3);
imshow("test", candy_roi_image);
imwrite("candy_roi_image.jpg", candy_roi_image);
//waitKey(1000);
//二值化
Mat roi_threadhold_image;
threshold(candy_roi_image, roi_threadhold_image, 50, 255, THRESH_BINARY);
imshow("test", roi_threadhold_image);
//waitKey(1000);
imwrite("roi_threadhold_image.jpg", roi_threadhold_image);
//平滑处理 中值滤波
//Mat roi_blurr_image;
//medianBlur(roi_threadhold_image, roi_blurr_image, 1);
//imshow("test", roi_blurr_image);
waitKey(1000);
//查找轮廓
Mat roi_contours_image;
vector<vector<Point>> roi_contours;
roi_contours_image = roi_threadhold_image.clone();
findContours(roi_contours_image, roi_contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//画出轮廓
//drawContours(roi_contours_image, roi_contours, -1, Scalar(255), 2);
//imshow("test", roi_contours_image);
waitKey(1000);
//轮廓表示成矩形
//轮廓表示为一个矩形 车牌提取
vector<Point> roi_rectPoint;
for (int i = 0; i < roi_contours.size(); i++)
{
Rect r = boundingRect(Mat(roi_contours[i]));
//RotatedRect r = minAreaRect(Mat(contours[i]));
cout << "contours " << i << " height = " << r.height << " width = " << r.width << "rate = " << ((float)r.width / r.height) << endl;
cout << "r.x = " << r.x << " r.y = " << r.y << endl;
//rectangle(large_image, r, Scalar(0, 0, 255), 1);
Point p1, p2, p3, p4;
p1.x = r.x;
p1.y = r.y;
p2.x = r.x + r.width;
p2.x = r.y;
p3.x = r.x + r.width;
p3.y = r.y + r.height;
p4.x = r.x;
p4.y = r.y + r.height;
roi_rectPoint.push_back(p1);
roi_rectPoint.push_back(p2);
roi_rectPoint.push_back(p3);
roi_rectPoint.push_back(p4);
for (int j = 0; j < roi_contours[i].size(); j++)
{
cout << "point = " << roi_contours[i][j] << endl;
}
}
imshow("test", roi_threadhold_image);
//waitKey(1000);
//矩形轮廓特征提取
int contours_height[30], contours_width[30];
for (int i = 0; i < roi_contours.size(); i++)
{
Rect r = boundingRect(Mat(roi_contours[i]));
contours_height[i] = r.height;
contours_width[i] = r.width;
cout << "contours_height = " << r.height << " contours_width = " << r.width << endl;
}
//判断字符水平位置
int roi_col = roi_threadhold_image.cols, roi_row = roi_threadhold_image.rows, position1[50], position2[50], roi_width[50];
uchar pix;
//cout << roi_threadhold_image << endl;
//确认为1 的像素
int pixrow[1000];
for (int i = 0; i < roi_col - 1; i++)
{
for (int j = 0; j < roi_row - 1; j++)
{
pix = roi_threadhold_image.at<uchar>(j, i);
pixrow[i] = 0;
if (pix > 0)
{
pixrow[i] = 1;
break;
}
}
}
//对数组进行滤波,减少突变概率
for (int i = 2; i < roi_col - 1 - 2; i++)
{
if ((pixrow[i - 1] + pixrow[i - 2] + pixrow[i + 1] + pixrow[i + 2]) >= 3)
{
pixrow[i] = 1;
}
else if ((pixrow[i - 1] + pixrow[i - 2] + pixrow[i + 1] + pixrow[i + 2]) <= 1)
{
pixrow[i] = 0;
}
}
//确认字符位置
int count = 0;
bool flage = false;
for (int i = 0; i < roi_col - 1; i++)
{
pix = pixrow[i];
if (pix == 1 && !flage)
{
flage = true;
position1[count] = i;
continue;
}
if (pix == 0 && flage)
{
flage = false;
position2[count] = i;
count++;
}
if (i == (roi_col - 2) && flage)
{
flage = false;
position2[count] = i;
count++;
}
}
//记录所有字符宽度
for (int n = 0; n < count; n++)
{
cout << " position1 = " << position2[n] << " position2 = " << position2[n] << "distance =" << (position2[n] - position1[n]) << endl;
roi_width[n] = position2[n] - position1[n];
}
// 减去最大值,最小值
int max = roi_width[0], max_index = 0;
int min = roi_width[0], min_index = 0;
for (int n = 1; n < count; n++)
{
if (max < roi_width[n])
{
max = roi_width[n];
max_index = n;
}
if (min > roi_width[n])
{
min = roi_width[n];
min_index = n;
}
}
int index = 0;
int new_roi_width[50];
for (int i = 0; i < count; i++)
{
if (i == min_index || i == max_index)
{
}
else
{
new_roi_width[index] = roi_width[i];
index++;
}
}
cout << "count = " << count << endl;
for (int i = 0; i < count - 2; i++)
{
cout << "new roi width = " << new_roi_width[i] << endl;
}
//取后面三个值的平均值:
int avgre = (int)((new_roi_width[count - 3] + new_roi_width[count - 4] + new_roi_width[count - 5]) / 3.0);
cout << avgre << endl;
//for (int i = count-3; i < count - 6; i--) {
//if (i < 0) {
// break;
//}
//cout << "count = " << new_roi_width[i] << endl;
//}
//字母位置信息确认
int licenseX[10], licenseW[10], licenseNum = 0;
int countX = 0;
for (int i = 0; i < count; i++)
{
if (roi_width[i] > (avgre - 8) && roi_width[i] < (avgre + 8))
{
licenseX[licenseNum] = position1[i];
licenseW[licenseNum] = roi_width[i];
licenseNum++;
cout << "licenseX = " << licenseX[i] << " roi_width =" << roi_width[i] << endl;
continue;
}
if (roi_width[i] > (avgre * 2 - 10) && roi_width[i] < (avgre * 2 + 10))
{
licenseX[licenseNum] = position1[i];
licenseW[licenseNum] = roi_width[i];
licenseNum++;
cout << "licenseX = " << licenseX[i] << " roi_width =" << roi_width[i] << endl;
}
}
//判断字符垂直位置
int licenseY[10], licenseH[10];
int position3[10], position4[10];
//确认为1 的像素
int countYY = 0;
int pixcol[1000], row_height[10];
for (int temp = 0; temp < licenseNum; temp++)
{
for (int i = 0; i < roi_row - 1; i++)
{
for (int j = licenseX[temp]; j < (licenseX[temp] + licenseW[temp]); j++)
{
pix = roi_threadhold_image.at<uchar>(i, j);
pixcol[i] = 0;
if (pix > 0)
{
pixcol[i] = 1;
break;
}
}
}
//对数组进行滤波,减少突变概率
for (int i = 2; i < roi_row - 1 - 2; i++)
{
if ((pixcol[i - 1] + pixcol[i - 2] + pixcol[i + 1] + pixcol[i + 2]) >= 3)
{
pixcol[i] = 1;
}
else if ((pixcol[i - 1] + pixcol[i - 2] + pixcol[i + 1] + pixcol[i + 2]) <= 1)
{
pixcol[i] = 0;
}
}
//确认字符位置
int countY = 0;
bool flage2 = false;
for (int i = 0; i < roi_row - 1; i++)
{
pix = pixcol[i];
if (pix == 1 && !flage2)
{
flage2 = true;
position3[countY] = i;
continue;
}
if (pix == 0 && flage2)
{
flage2 = false;
position4[countY] = i;
countY++;
}
}
//记录所有字符宽度
for (int n = 0; n < countY; n++)
{
cout << " position3 = " << position3[n] << " position4 = " << position4[n] << "distance =" << (position4[n] - position3[n]) << endl;
row_height[countYY] = position4[n] - position3[n];
licenseY[countYY] = position3[n];
licenseH[countYY] = row_height[countYY];
}
countYY++;
}
//截取字符
Mat licenseN = Mat(Scalar(0));
cout << "countYY = " << countYY << endl;
for (int i = 0; i < countYY; i++)
{
Rect rect(licenseX[i], licenseY[i], licenseW[i], licenseH[i]);
cout << "position = " << licenseX[i] << " " << licenseY[i] << " " << licenseW[i] << " " << licenseH[i] << endl;
licenseN = large_image(rect);
imshow("test1" + i, licenseN);
ostringstream oss;
oss << "licenseN" << i << ".jpg";
imwrite(oss.str(), licenseN);
main_num_reconginzed(oss.str());
//waitKey(1000);
}
cout << "license plate process" << endl;
return 0;
}
float sumMatValue(const Mat &image)
{
float sumValue = 0;
int r = image.rows;
int c = image.cols;
if (image.isContinuous())
{
c = r * c;
r = 1;
}
for (int i = 0; i < r; i++)
{
const uchar *linePtr = image.ptr<uchar>(i);
for (int j = 0; j < c; j++)
{
sumValue += linePtr[j];
}
}
return sumValue;
}
void calcGradientFeat(Mat &imgSrc, vector<float> &feat)
{
Mat image;
cvtColor(imgSrc, image, COLOR_BGR2GRAY);
resize(image, image, Size(8, 16));
float mask[3][3] = {{1, 2, 1}, {0, 0, 0}, {-1, -2, -1}};
Mat y_mask = Mat(3, 3, CV_32F, mask) / 8;
Mat x_mask = y_mask.t(); // 转置
Mat sobelX, sobelY;
filter2D(image, sobelX, CV_32F, x_mask);
filter2D(image, sobelY, CV_32F, y_mask);
sobelX = abs(sobelX);
sobelY = abs(sobelY);
float totleValueX = sumMatValue(sobelX);
float totleValueY = sumMatValue(sobelY);
for (int i = 0; i < image.rows; i = i + 4)
{
for (int j = 0; j < image.cols; j = j + 4)
{
Mat subImageX = sobelX(Rect(j, i, 4, 4));
feat.push_back(sumMatValue(subImageX) / totleValueX);
Mat subImageY = sobelY(Rect(j, i, 4, 4));
feat.push_back(sumMatValue(subImageY) / totleValueY);
}
}
Mat img2;
resize(image, img2, Size(4, 8));
int r = img2.rows;
int c = img2.cols;
if (img2.isContinuous())
{
c = r * c;
r = 1;
}
for (int i = 0; i < r; i++)
{
const uchar *linePtr = img2.ptr<uchar>(i);
for (int j = 0; j < c; j++)
{
feat.push_back(linePtr[j]);
}
}
// cout<<sobelX<<endl;
// cout<<sobelY<<endl;
// cout<< x_mask<<endl;
// cout<<img2<<endl;
// for(int i=0; i<feat[num].size(); i++)
// {
// cout<<feat[i]<<endl;
// }
// imshow("cat", img2);
// cout<<"sumValue ="<<sumMatValue(image)<<endl;
}
Ptr<StatModel> buildMLPClassifier(Mat &input, Mat &output)
{
Ptr<ANN_MLP> model;
cout << __LINE__ << endl;
//train classifier;
int layer_sz[] = {input.cols, 100, output.cols};
cout << __LINE__ << endl;
int nlayers = (int)(sizeof(layer_sz) / sizeof(layer_sz[0]));
cout << __LINE__ << endl;
Mat layer_sizes(1, nlayers, CV_32S, layer_sz);
cout << __LINE__ << endl;
int method;
cout << __LINE__ << endl;
double method_param;
int max_iter;
cout << __LINE__ << endl;
if (1)
{
method = ANN_MLP::BACKPROP;
cout << __LINE__ << endl;
method_param = 0.0001;
max_iter = 1000;
}
else
{
method = ANN_MLP::RPROP;
cout << __LINE__ << endl;
method_param = 0.1;
max_iter = 1000;
}
Ptr<TrainData> tData = TrainData::create(input, ROW_SAMPLE, output);
cout << __LINE__ << endl;
model = ANN_MLP::create();
cout << __LINE__ << endl;
model->setLayerSizes(layer_sizes);
cout << __LINE__ << endl;
model->setActivationFunction(ANN_MLP::SIGMOID_SYM, 0, 0);
cout << __LINE__ << endl;
model->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, max_iter, FLT_EPSILON));
cout << __LINE__ << endl;
//setIterCondition(max_iter, 0);
model->setTrainMethod(method, method_param);
cout << __LINE__ << endl;
model->train(tData);
cout << __LINE__ << endl;
model->save(xml_path);
cout << __LINE__ << endl;
return model;
}
Ptr<StatModel> loadMLPClassifiler()
{
Ptr<ANN_MLP> model = Algorithm::load<ANN_MLP>(xml_path);
return model;
}
int main_num_reconginzed(const String photo_path_to_be_reconginzed)
{
string path = charSamples_path;
Mat image;
vector<float> feats;
vector<float> test, test1;
int num = 0;
int classfilternum = 10;
int modlenum = 50;
image = imread(photo_path_to_be_reconginzed.c_str());
image.convertTo(image, CV_32F);
calcGradientFeat(image, test);
/********************************************************************************************************/
/*训练完就不需要这段代码了,我注释掉了*/
// for(int i = 0 ; i < classfilternum ; i++){
// for(int j = 0; j < modlenum; j++){
// ostringstream oss;
// oss<<path<<i<<"/"<<j<<".png";
// image=imread(oss.str());
// image.convertTo(image, CV_32F);
// /*cout<<oss.str()<<endl;*/
// calcGradientFeat(image, feats);
// num++;
// }
// }
// Mat input, output;
// input = Mat(classfilternum*modlenum, 48, CV_32F);
// output = Mat(classfilternum*modlenum, classfilternum, CV_32F, Scalar(0));
// int r = input.rows;
// int c = input.cols;
// if(input.isContinuous()){
// c = r*c;
// r = 1;
// }
// for(int i = 0; i < r; i++){
// float *linePtr = input.ptr<float>(i);
// for (int j = 0; j < c; j++){
// linePtr[j] = feats[c*i + j];
// }
// }
// for(int i = 0; i < output.rows; i++){
// float *lineoutput = output.ptr<float>(i);
// lineoutput[i/modlenum] = 1;
// }
/********************************************************************************************************/
//if(
//Ptr<StatModel> model = buildMLPClassifier(input, output);cout<<__LINE__<<endl;
/*训练完就不需要这 行 代码了,我注释掉了*/
Ptr<StatModel> model = loadMLPClassifiler();
/*训练完就直接用这 行 代码就行*/
float response = model->predict(test, test1);
cout << __LINE__ << endl;
cout << "response = " << response << endl;
for (int i = 0; i < test1.size(); i++)
{
cout << "test1 = " << test1[i] << endl;
}
//cout<<input<<endl;
//cout<<"rows = "<<input.rows<<"col = "<<input.cols<<endl;
//cout<<output<<endl;
// waitKey(); //等待按键
return 0;
}
测试图片(高清大图)
使用example.cpp编译,其他cpp文件请忽略,abc.xml是训练结果,9 7 8三个jpg文件是分割结果
测试结果
9 7 8分别被认成2 7 8
。。。。。。。。。。。。。完。。。。2020/11/28。。。。。。。。。。。。