对“ 待分割识别图像”文件夹中的 ” 所有 整版麻将图片进行 分割 ,并输出 识别 结果 :
源代码:
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
static int issame(Mat out, Mat std);
int minvalue = INT_MAX;
void HorizonProjection(const Mat& src, Mat& dst) //水平投影,const表示常量
{
CV_Assert(src.depth() != sizeof(uchar));
dst.create(src.rows, 1, CV_32F);
int i, j;
const uchar* p;
float* p_dst;
for (i = 0; i < src.rows; i++) {
p = src.ptr<uchar>(i);
p_dst = dst.ptr<float>(i);
p_dst[0] = 0;
for (j = 0; j < src.cols; j++) {
p_dst[0] += p[j];
}
p_dst[0] = p_dst[0] / src.cols;
}
}
void VerticalProjection(const Mat& src, Mat& dst) //垂直投影
{
CV_Assert(src.depth() != sizeof(uchar));
dst.create(1, src.cols, CV_32F);
int i, j;
const uchar* p;
float* p_dst = dst.ptr<float>(0);
for (j = 0; j < src.cols; j++) {
p_dst[j] = 0;
for (i = 0; i < src.rows; i++) {
p = src.ptr<uchar>(i);
p_dst[j] += p[j];
}
p_dst[j] = p_dst[j] / src.rows;
}
}
int main()
{
Mat dstImage,gImage,sgImage,ogImage, srcImage = imread("a.jpg"); //有路径要输入
Mat hImage, vImage;
int x1, x2, y1, y2;
int x_proj_out, y_proj_out;
int width_single, heigth_single;
float *dst;
float *t;
int a[100],b[100],d[100];
int n=0,m=0,g=0;
int w,h;
int maxw=0,maxh=0;
int numx=0, numy=0;
//步骤一
cvtColor(srcImage, gImage, CV_BGR2GRAY); //颜色转换函数
threshold(gImage, dstImage, 0, 255, THRESH_OTSU);
//步骤二
HorizonProjection(dstImage, hImage);
VerticalProjection(dstImage, vImage);
for (int i = 0;;i++) {
dst = hImage.ptr<float>(i);
float t1 = dst[0], t2 = dst[1], t3 = dst[2], t4 = dst[3], t5 = dst[4];
if (t1!= 0 && t2 != 0 && t3!= 0 && t4 != 0 && t5 != 0)
{
y1 = i - 2;
break;
}
}
for (int i = hImage.rows;;i--) {
dst = hImage.ptr<float>(i-5);
float t1 = dst[0], t2 = dst[1], t3 = dst[2], t4 = dst[3], t5 = dst[4];
if (t1 != 0 && t2 != 0 && t3 != 0 && t4 != 0 && t5 != 0)
{
y2 = i + 2;
break;
}
}
for (int i = 0;;i++) {
dst = vImage.ptr<float>(0);
float t1 = dst[i], t2 = dst[i+1], t3 = dst[i+2], t4 = dst[i+3], t5 = dst[i+4];
if (t1 != 0 && t2 != 0 && t3 != 0 && t4 != 0 && t5 != 0)
{
x1 = i - 2;
break;
}
}
for (int i = vImage.cols;;i--) {
dst = vImage.ptr<float>(0);
float t1 = dst[i-4], t2 = dst[i-3], t3 = dst[i-2], t4 = dst[i-1], t5 = dst[i];
if (t1 != 0 && t2 != 0 && t3 != 0 && t4 != 0 && t5 != 0)
{
x2 = i + 2;
break;
}
}
sgImage = gImage(Range(y1, y2), Range(x1, x2));
imshow("截取的灰度图", sgImage);
// waitKey(0);
//步骤三
threshold(sgImage, dstImage, 0, 255, THRESH_OTSU);
imshow("二值图像", dstImage);
Mat element = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
morphologyEx(dstImage, dstImage, CV_MOP_OPEN, element);
imshow("形态学滤波", dstImage);
waitKey(0);
HorizonProjection(dstImage, hImage);
VerticalProjection(dstImage, vImage);
dst = hImage.ptr<float>(0);dst[0] = 255;
dst = hImage.ptr<float>(1);dst[0] = 255;
dst = hImage.ptr<float>(hImage.rows-1);dst[0] = 255;
dst = hImage.ptr<float>(hImage.rows-2);dst[0] = 255;
dst = vImage.ptr<float>(0);dst[0] = 255, dst[1] = 255, dst[vImage.cols - 1] = 255, dst[vImage.cols - 2] = 255;
for (int i = 1;i < vImage.cols;i++) {
dst = vImage.ptr<float>(0);
if (dst[i] > 255 * 0.9)
{
dst[i] = 255;
if (dst[i-1] == 0) {
a[n] = i;
n++;
}
}
else {
dst[i] = 0;
if (dst[i-1] == 255) {
a[n] = i;
n++;
}
}
}
// imshow("111111111", vImage);
for (int i = 0;i <= n-1;i += 1) {
if ((a[i + 1] - a[i]) < 5) {
for (int j = a[i];j <= a[i + 1];j++) {
dst = vImage.ptr<float>(0);
dst[j] = 255;
}
}
}
for (int i = 1;i < vImage.cols;i++) {
dst = vImage.ptr<float>(0);
if (dst[i]!=dst[i - 1] ) {
b[m] = i;
m++;
}
}
// imshow("222222222", vImage);
for (int i = 0;i <= m;i += 2) {
if (maxw < b[i + 1] - b[i]) maxw = b[i + 1] - b[i];//maxw单个麻将最大宽度
}
for (int i = 0;i <= m;i += 2) {
w = b[i + 1] - b[i];
if (w > (maxw / 2)) {
numx++;
d[g] = b[i];
d[g+1] = b[i + 1];
g += 2;
}
}
x_proj_out = (d[0] + d[g - 1]) / 2;
n = 0, m = 0, g = 0;
for (int i = 1;i < hImage.rows;i++) {
t = hImage.ptr<float>(i - 1);
dst = hImage.ptr<float>(i);
if (dst[0] > 255 * 0.9) {
dst[0] = 255;
if (t[0] == 0) {
a[n] = i;
n++;
}
}
else {
dst[0] = 0;
if (t[0] == 255) {
a[n] = i;
n++;
}
}
}
// imshow("111111", hImage);
for (int i = 0;i <= n-1;i += 2) {
if ((a[i + 1] - a[i]) < 10) {
for (int j = a[i];j <= a[i + 1];j++) {
dst = hImage.ptr<float>(j);
dst[0] = 255;
}
}
}
// imshow("22222", hImage);
for (int i = 1;i < hImage.rows;i++) {
t = hImage.ptr<float>(i - 1);
dst = hImage.ptr<float>(i);
if (t[0] != dst[0]) {
b[m] = i;
m++;
}
}
for (int i = 0;i <= m-2;i += 2) {
h = b[i + 1] - b[i];
if (h < 0.95*maxw) {
h = b[i + 3] - b[i];
if (h < 0.95*maxw) {
h = b[i + 5] - b[i];
d[g] = b[i];
d[g + 1] = b[i + 5];
g += 2;
i += 4;
numy++;
}
else {
d[g] = b[i];
d[g + 1] = b[i + 3];
g += 2;
i += 2;
numy++;
}
}
else {
d[g] = b[i];
d[g + 1] = b[i + 1];
g += 2;
numy++;
}
}
for (int i = 0;i <= g;i += 2) {
if (maxh < d[i + 1] - d[i]) maxh = d[i + 1] - d[i];
}
y_proj_out = (d[0] + d[g - 1]) / 2;
heigth_single =(sgImage.rows-10) / numy;
width_single =(sgImage.cols-10) / numx;
int num = numx*numy;
Mat *singleImage = new Mat[num];
int xb, yb;
int xb1,yb1,xe, ye;
int count=0;
xb = x_proj_out - (width_single*((float)numx / 2));
printf("%d %d", xb,x_proj_out);
yb = y_proj_out - (heigth_single*((float)numy / 2));
for (int i=0;i < numy;i++) {
for (int j = 0;j < numx;j++) {
xb1 = xb + width_single*(j);
xe = xb + width_single*(j + 1);
yb1 = yb + heigth_single*(i);
ye = yb + heigth_single*(i + 1);
singleImage[count] = sgImage(Range(yb1,ye), Range(xb1,xe));
count++;
}
}
char filename[30];
char filename2[30];
char filename3[30];
char filename4[30];
char filename5[30];
char filename6[30];
char filename7[30];
Mat *dsImage=new Mat[num], *outImage=new Mat[num];
Mat stdImage[34], grayImage[34], threImage[34], tempImage[34];
//步骤4
//分割出来的单个麻将灰度图片
for (int i = 0; i <num; i++)
{
sprintf_s(filename, "%d.jpg", i + 1);
imshow(filename, singleImage[i]);
}
//缩放尺寸
for (int i = 0; i <num; i++)
{
resize(singleImage[i], dsImage[i], Size(32, 48), INTER_LINEAR);
sprintf_s(filename2, "change(%d).jpg", i + 1);
//imwrite(filename2, dstImage[i]);
//imshow(filename2,dstImage[i]);
}
//阈值化
for (int i = 0; i <num; i++)
{
threshold(dsImage[i], outImage[i], 0, 255, THRESH_OTSU);
sprintf_s(filename3, "threshold(%d).jpg", i + 1);
//imshow(filename3, outImage[i]);
for (int m = 0; m < outImage[i].cols; m++)
{
outImage[i].ptr<uchar>(0)[m] = 255;
}
for (int m = 0; m < outImage[i].cols; m++)
{
outImage[i].ptr<uchar>(47)[m] = 255;
}
for (int m = 0; m < outImage[i].rows; m++)
{
outImage[i].ptr<uchar>(m)[0] = 255;
}
for (int m = 0; m < outImage[i].rows; m++)
{
outImage[i].ptr<uchar>(m)[31] = 255;
}
sprintf_s(filename4, "thresholdchange(%d).jpg", i + 1);
//imshow(filename4, outImage[i]);
}
//步骤5
//阈值化模板麻将
for (int i = 0; i <34; i++)
{
sprintf_s(filename5, "%d.jpg", i + 1);
stdImage[i] = imread(filename5);
//imshow(filename5,stdImage[i]);
}
for (int i = 0; i <34; i++)
{
cvtColor(stdImage[i], grayImage[i], CV_RGB2GRAY);
threshold(grayImage[i], threImage[i], 0, 255, THRESH_OTSU);
for (int m = 0; m < threImage[i].cols; m++)
{
threImage[i].ptr<uchar>(0)[m] = 255;
}
for (int m = 0; m < threImage[i].cols; m++)
{
threImage[i].ptr<uchar>(47)[m] = 255;
}
for (int m = 0; m < threImage[i].rows; m++)
{
threImage[i].ptr<uchar>(m)[0] = 255;
}
for (int m = 0; m < threImage[i].rows; m++)
{
threImage[i].ptr<uchar>(m)[31] = 255;
}
sprintf_s(filename6, "thresholdstandard(%d).jpg", i + 1);
//imshow(filename6, threImage[i]);
}
//扩大成模板麻将
for (int i = 0; i <34; i++)
{
copyMakeBorder(threImage[i], tempImage[i], 9, 9, 6, 6, BORDER_REPLICATE);
sprintf_s(filename7, "temp(%d).jpg", i + 1);
// imshow(filename7, tempImage[i]);
}
//步骤6 待识别outImage[i] 模板tempImage[i]
int nvalue;
int minv = INT_MAX;
int n1;
for (int j = 0; j < num; j++)
{
for (int i = 0; i < 34; i++)
{
nvalue = issame(outImage[j], tempImage[i]);
if (nvalue < minv)
{
minv = nvalue;
n1 = i;
}
}
printf("(%d).jpg = %d.jpg \n", j + 1, n1 + 1);
minv = INT_MAX;
}
waitKey(0);
return 0;
}
static int issame(Mat out, Mat std)
{
Mat temp;
int value = INT_MAX;
int res = 0;
int x, y;
for (int x1 = 0; x1 < 13; x1++)
{
for (int y1 = 0; y1 < 19; y1++)
{
temp = std(Range(y1, y1 + 47), Range(x1, x1 + 31));
for (int j = 0; j < 47; j++)
{
for (int i = 0; i < 31; i++)
{
res = res + abs(out.at<uchar>(j, i) - temp.at<uchar>(j, i));
}
}
if (res < minvalue)
{
minvalue = res;
}
if (minvalue < value)
{
value = minvalue;
}
res = 0;
minvalue = INT_MAX;
}
}
return value;
}
处理的结果:
关闭截取的灰度图、二值图像、形态学滤波窗口后:
程序运行后出现的结果:
即为分割识别后的结果。