rotation.cpp
上一篇文章整个程序的基本框架已经搭建好,现在完善一下相关的函数。旋转操作在rotation.cpp中完成。包括一下两个函数
计算旋转的角度
void calculationAngle(vector<Point> porker,float &angle) {
//以扑克牌第二个顶点为旋转中心计算旋转的角度
float dx, dy;
dx = porker[0].x - porker[1].x;
dy = porker[0].y - porker[1].y;
float width;
float length;
length = fabs(sqrt((porker[3].x - porker[0].x) * (porker[3].x - porker[0].x) + (porker[3].y - porker[0].y) * (porker[3].y - porker[0].y)));
width = fabs(sqrt((porker[0].x - porker[1].x) * (porker[0].x - porker[1].x) + (porker[0].y - porker[1].y) * (porker[0].y - porker[1].y)));
//根据长短边旋转矩形
//cout << dx << " " << dy << endl;
float tan = dy / dx;
angle=atan(tan);
//cout << angle << endl;
if (width < length) {
angle = (float)atan2(dy,dx) * 180 / PI;
}
else {
angle = (float)atan2(dy,dx) * 180 / PI;
if (angle > 0)
angle = angle - 90;
else
angle = angle + 90;
}
//cout << angle<<endl;
}
旋转图片
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));
}
templateMatching.cpp
对扑克牌识别的区域进行模板匹配,因为数字图像处理的老师不让用机器学习等方法,所以用了这个最基础的方法。
首先先设两个数组
string numID[] ={"10","2","3","4","5","6","7","8","9","A","J","K","Q"};
string suitID[] = { "clubs","diamonds","hearts","spades" };
加载模板图片
void loadImage(vector<Mat> &models,vector<string>files,string path) {
getFiles(path,files);
for (int i = 0; i < files.size(); i++) {
if(path=="num")
files[i][3] = '/';
else if (path == "suits")
files[i][5] = '/';
}
//加载模板图片
for (int i = 0; i < files.size(); i++) {
models.push_back(imread(files[i]));
}
}
模板是我自己做的图片。数字是2020像素大小,花色是3030大小
对要识别的花色和数字区域进行处理,这里以花色为例
Mat boxDetectSuit(Mat m) {
Mat binary;
//腐蚀膨胀
binary = m;
erode(m, binary, 2);
dilate(binary, binary, 2);
bitwise_not(binary, binary);
vector<vector<Point>>contours;
contours.clear();
findContours(m, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
int max = 0;
Rect maxBoundRect;
for (int i = 0; i < contours.size(); i++) {
Rect boundRect = boundingRect(contours[i]);
if (boundRect.area() > max) {
maxBoundRect = boundRect;
max = boundRect.area();
}
}
Mat imageROI = m(maxBoundRect);
resize(imageROI, imageROI, Size(30, 30), 1);
return imageROI;
}
处理后的效果如下
接下来在模板中查找模板与要识别的数字或花色差值最小的模板。
//矩阵内元素求和
int matSum(Mat m1,Mat m2) {
int sum = 0;
for (size_t i = 0; i < m1.rows; i++) {
for (size_t j = 0; j < m1.cols;j++) {
int value1 = (int)*(m1.data+m1.step[0]*i+m1.step[1]*j);
//cout << value1<<endl;
int value2 = (int)*(m2.data + m2.step[0] * i + m2.step[1] * j);
//cout << value2 << endl;
int diff=fabs(value1-value2);
sum += diff;
}
}
return sum;
}
void returnNum(Mat numImage, vector<Mat>& models, string& number) {
resize(numImage, numImage, Size(20, 20), 1);
Mat binarizedImage;//完美切割出来的数字,开心:)
//二值化图像
cvtColor(numImage, binarizedImage,COLOR_RGB2GRAY);
/*string name1 = "binaryg" + to_string(i++);
imshow(name1, binarizedImage);*/
//大津法找阈值
threshold(binarizedImage, binarizedImage,0,255, THRESH_BINARY | THRESH_OTSU);
bitwise_not(binarizedImage,binarizedImage);
binarizedImage=boxDetect(binarizedImage);
//string name = "binary" + to_string(i++);
//imshow(name, binarizedImage);
int minDiff =1000000000;
int index;
for (int i = 0; i < models.size(); i++) {
int diff;
Mat detectModel;
diff=matSum(models[i], binarizedImage);
if (minDiff > diff) {
minDiff = diff;
index = i;
}
}
number = numID[index];
//cout << numID[index]<<endl;
}
void returnSuit(Mat suitImage, vector<Mat>& models, string &suit) {
resize(suitImage, suitImage, Size(30, 30), 1);
Mat binarizedImage;//完美切割出来的数字,开心:)
//二值化图像
cvtColor(suitImage, binarizedImage, COLOR_RGB2GRAY);
/*string name1 = "binaryg" + to_string(i++);
imshow(name1, binarizedImage);*/
//大津法找阈值
threshold(binarizedImage, binarizedImage, 0, 255, THRESH_BINARY | THRESH_OTSU);
//bitwise_not(binarizedImage, binarizedImage);
binarizedImage = boxDetectSuit(binarizedImage);
//string name = "binary2" + to_string(i++);
//imshow(name, binarizedImage);
int minDiff = 1000000000;
int index;
for (int i = 0; i < models.size(); i++) {
int diff;
Mat detectModel;
diff = matSum(models[i], binarizedImage);
if (minDiff > diff) {
minDiff = diff;
index = i;
}
}
suit = suitID[index];
//cout << suitID[index] << endl;
}
此时已经可以得出识别扑克牌的结果。
但是界面不够美观,识别结果是由控制台显示的,于是再给为这个程序写一个QT界面。具体实现在写一个识别扑克牌花色和点数的小程序(三)中。