车牌定位及分割
2022-5-9 ~ 2022-6-11
(2)
程序简介
车牌定位及分割,涉及到转换灰度,高斯平滑,中值滤波,边缘检测,形态学滤波,轮廓查找。对于原作中存在不能识别某些环境下的车牌;车尾部有一串拼音(有的车有),很容易框住好几个等问题,进行了改进。
原作中的具体步骤:
1、将采集到的彩色车牌图像转换成灰度图。
2、灰度化的图像利用高斯平滑处理后,再对其进行中值滤波。
3、使用 Sobel 算子对图像进行边缘检测。
4、对二值化的图像进行腐蚀,膨胀,开运算,闭运算的形态学组合变换。
5、对形态学变换后的图像进行轮廓查找,根据车牌的长宽比提取车牌。
如果能用彩色车牌进行分析会更好。
他代码翻译到我代码中的中值滤波的核边长是 2。
高斯平滑和中值滤波会使边缘检测的结果不理想(尤其是后者),如果拍到砖面,识别结果不好,程序演示中直接跳过了。
他的边缘检测是仅进行纵向检测,所以识别旋转倾斜后的车牌会出现问题。
他形态学滤波的步骤是膨胀一次,腐蚀一次,膨胀三次,程序演示中删掉了最后两次膨胀(因为删除了平滑和滤波,边缘检测更符合实际尺寸)。
我轮廓查找算法用的是消消乐的判定算法,和他用的轮廓查找算法不一样,所以结果不完全一致。
他提取车牌的方式是面积和长宽比,我只用了面积。用长宽比也就彻底无法识别旋转倾斜后的车牌了。
单步操作可以运行后按各个按钮尝试即可,主要影响结果的是形态学滤波的操作顺序和它的核大小,轮廓检测的面积阈值。
完整操作直接改代码中完整进程函数:completeprocessing(),运行后按完整进程按钮即可。
识别旋转九十度的图片,需要改边缘检测的检测模式为横向,形态学滤波内核的长宽对换。
更新:
- 加载图片按钮更新,调用系统自带的 api,显示一个“打开文件”的对话框。并添加到参考资料里了。
- 将测试图片打包上传,放到代码后面了。
程序执行效果
完整源代码
////////////////////////////////////////
// 程序:车牌定位及分割
// 作者:Gary
// 编译环境:Visual C++ 2010,EasyX_20211109
// 编写日期:2022-4-29
#include <graphics.h>
#include <string>
#include <ShlObj.h>
static HWND hOut; // 画布
// 图像处理
int img_m[1000][1000];
int img_b[1000][1000];
// 定义一个结构体,按钮
struct Node1
{
int posx1, posy1, posx2, posy2; // 坐标
LPCTSTR text; // 文字
};
// 定义一个类
class Gary
{
public:
void carry(); // 主进程
void initialization(); // 初始化
void move(); // 窗口主视角
void draw(); // 绘制函数
void completeprocessing(); // 完整处理函数
IMAGE* gray(IMAGE* img_input); // 灰度化
IMAGE* gaussian_smoothing_filtering(IMAGE* img_input); // 高斯滤波
IMAGE* median_filtering(IMAGE* img_input, int num_range); // 中值滤波
IMAGE* edge_detection(IMAGE* img_input, int num_mode, int num_threshold); // 边缘检测
IMAGE* morphological_filtering(IMAGE* img_input, int num_mode, int a, int b); // 形态学滤波
IMAGE* contours_finding(IMAGE* img_input, int num_threshold); // 轮廓查找
int num_button; // 按钮数量
int num_length; // 图片长度
int num_height; // 图片宽度
int exit_carry; // 进程控制
int exit_move; // 进程控制
IMAGE* img_output; // 输出图像
IMAGE* img_temporary; // 临时图像
TCHAR address_load[30]; // 加载地址
TCHAR address_save[30]; // 保存地址
Node1 boxm[30]; // 按钮
};
// 形态学滤波,输入图像,滤波模式,卷积核长度,卷积核宽度
IMAGE* Gary::morphological_filtering(IMAGE* img_input, int num_mode, int a, int b)
{
// 加载输入图像
putimage(0, 0, img_input);
// 处理
TCHAR s[15];
int i, j, k;
int k1, k2;
// 存储
for(i = 0; i < num_length; i++)
{
for(j = 0; j < num_height; j++)
{
img_b[i][j] = getpixel(i, j) / 256 / 256;
}
}
// 图遍历
for(i = 0; i < num_length; i++)
{
for(j = 0; j < num_height; j++)
{
k = 0;
// 对白点进行腐蚀操作
if(img_b[i][j] > 200 && num_mode == 0)
{
k = 1;
}
// 对黑点进行膨胀操作
else if(img_b[i][j] < 10 && num_mode == 1)
{
k = 1;
}
// 核遍历
for(k1 = i - a / 2; k1 < i + a / 2; k1++)
{
for(k2 = j - b / 2; k2 < j + b / 2; k2++)
{
if(0 <= k1 && k1 < num_length && 0 <= k2 && k2 < num_height)
{
// 核内有零则为零,实现腐蚀
if(img_b[k1][k2] < 10 && num_mode == 0)
{
k = 0;
}
// 核内有一则为一,实现膨胀
else if(img_b[k1][k2] > 200 && num_mode == 1)
{
k = 0;
}
}
if(k == 0)
{
break;
}
}
if(k == 0)
{
break;
}
}
// 二值化
img_m[i][j] = ( num_mode == 0 ? ( ( k == 0 ? 0 : 255 ) ) : ( ( k == 0 ? 255 : 0 ) ) );
}
// 延迟,优化空间,避免太快,更好做演示
// Sleep(1);
// 进度显示
_stprintf_s(s, _T("%0.1d"), i * 100 / num_length);
outtextxy(boxm[6].posx1 + ( boxm[6].posx2 - boxm[6].posx1 ) / 2 - textwidth(boxm[6].text) / 2, boxm[6].posy1 + 30, s);
}
draw();
// 新建输出图像
img_output = new IMAGE(num_length, num_height);
// 初始化处理区域
SetWorkingImage(img_output);
setbkcolor(BLACK);
cleardevice();
// 绘制
for(i = 0; i < num_length; i++)
{
for(j = 0; j < num_height; j++)
{
putpixel(i, j, RGB(img_m[i][j], img_m[i][j], img_m[i][j]));
}
}
SetWorkingImage();
// 绘制
putimage(0, 0, img_output);
// 返回输出图像
return img_output;
}
// 边缘检测,输入图像,检测模式,阈值
IMAGE* Gary::edge_detection(IMAGE* img_input, int num_mode, int num_threshold)
{
// 加载输入图像
putimage(0, 0, img_input);
// 处理
int i, j, k, k1, k2;
TCHAR s[15];
// 存储
for(i = 0; i < num_length; i++)
{
for(j = 0; j < num_height; j++)
{
img_b[i][j] = getpixel(i, j) / 256 / 256;
}
}
// 处理
for(i = 1; i < num_length - 1; i++)
{
for(j = 1; j < num_height - 1; j++)
{
k1 = 0; k2 = 0;
// 横向
k1 = ( -1 * img_b[i - 1][j - 1] +
-2 * img_b[i][j - 1] +
-1 * img_b[i + 1][j - 1] +
0 * img_b[i - 1][j] +
0 * img_b[i][j] +
0 * img_b[i + 1][j] +
1 * img_b[i - 1][j + 1] +
2 * img_b[i][j + 1] +
1 * img_b[i + 1][j + 1] );
// 纵向
k2 = ( -1 * img_b[i - 1][j - 1] +
-2 * img_b[i - 1][j] +
-1 * img_b[i - 1][j + 1] +
0 * img_b[i][j - 1] +
0 * img_b[i][j] +
0 * img_b[i][j + 1] +
1 * img_b[i + 1][j - 1] +
2 * img_b[i + 1][j] +
1 * img_b[i + 1][j + 1] );
k1 = abs(k1) / 2;
k2 = abs(k2) / 2;
switch(num_mode)
{
case 0:k = k1; break;
case 1:k = k2; break;
case 2:k = ( k1 + k2 ) / 2; break;
default:break;
}
// 二值化,与阈值比较
img_m[i][j] = k > num_threshold ? 255 : 0;
}
// 延迟,优化空间,避免太快,更好做演示
// Sleep(1);
// 进度显示
_stprintf_s(s, _T("%0.1d"), i * 100 / num_length);
outtextxy(boxm[5].posx1 + ( boxm[5].posx2 - boxm[5].posx1 ) / 2 - textwidth(boxm[5].text) / 2, boxm[5].posy1 + 30, s);
}
draw();
// 新建输出图像
img_output = new IMAGE(num_length, num_height);
// 初始化处理区域
SetWorkingImage(img_output);
setbkcolor(WHITE);
cleardevice();
// 绘制
for(i = 0; i < num_length; i++)
{
for(j = 0; j < num_height; j++)
{
putpixel(i, j, RGB(img_m[i][j], img_m[i][j], img_m[i][j]));
}
}
SetWorkingImage();
// 绘制
putimage(0, 0, img_output);
// 返回输出图像
return img_output;
}
// 中值滤波函数,卷积核尺度
IMAGE* Gary::median_filtering(IMAGE* img_input, int num_range)
{
// 加载输入图像
putimage(0, 0, img_input);
// 处理
TCHAR s[15];
int i, j, k, t;
int T = num_range;
int a, b;
int g[100];
// 存储
for(i = 0; i < num_length; i++)
{
for(j = 0; j < num_height; j++)
{
img_b[i][j] = getpixel(i, j) / 256 / 256;
}
}
// 滤波
for(i = 0; i < num_length; i++)
{
for(j = 0; j < num_height; j++)
{
t = 0;
// 取 (2*T+1)^2 格的像素值
for(a = -T; a <= T; a++)
{
for(b = -T; b <= T; b++)
{
if(0 <= i + a && i + a < num_length && 0 <= j + b && j + b < num_height)
{
g[t] = getpixel(i + a, j + b) / 256 / 256;
t++;
}
}
}
// 冒泡排序
for(a = 0; a < t - 1; a++)
{
for(b = t - 1; b > a; b--)
{
if(g[b] < g[b - 1])
{
k = g[b];
g[b] = g[b - 1];
g[b - 1] = k;
}
}
}
t = t / 2;
// 取中值作为新像素值并绘制
img_m[i][j] = g[t];
}
// 进度显示
_stprintf_s(s, _T("%0.1d"), i * 100 / num_length);
outtextxy(boxm[4].posx1 + ( boxm[4].posx2 - boxm[4].posx1 ) / 2 - textwidth(boxm[4].text) / 2, boxm[4].posy1 + 30, s);
}
draw();
// 新建输出图像
img_output = new IMAGE(num_length, num_height);
// 初始化处理区域
SetWorkingImage(img_output);
setbkcolor(BLACK);
cleardevice();
// 绘制
for(i = 0; i < num_length; i++)
{
for(j = 0; j < num_height; j++)
{
putpixel(i, j, RGB(img_m[i][j], img_m[i][j], img_m[i][j]));
}
}
SetWorkingImage();
// 绘制
putimage(0, 0, img_output);
// 返回输出图像
return img_output;
}
// 高斯滤波函数
IMAGE* Gary::gaussian_smoothing_filtering(IMAGE* img_input)
{
// 加载输入图像
putimage(0, 0, img_input);
// 处理
TCHAR s[15];
int i, j, k;
// 存储
for(i = 0; i < num_length; i++)
{
for(j = 0; j < num_height; j++)
{
img_b[i][j] = getpixel(i, j) / 256 / 256;
}
}
// 滤波
for(i = 1; i < num_length - 1; i++)
{
for(j = 1; j < num_height - 1; j++)
{
k = ( 1 * img_b[i - 1][j - 1] +
2 * img_b[i][j - 1] +
1 * img_b[i + 1][j - 1] +
2 * img_b[i - 1][j] +
4 * img_b[i][j] +
2 * img_b[i + 1][j] +
1 * img_b[i - 1][j + 1] +
2 * img_b[i][j + 1] +
1 * img_b[i + 1][j + 1] )
/ 16;
img_m[i][j] = k;
}
// 延迟,优化空间,避免太快,更好做演示
// Sleep(1);
// 进度显示
_stprintf_s(s, _T("%0.1d"), i * 100 / num_length);
outtextxy(boxm[3].posx1 + ( boxm[3].posx2 - boxm[3].posx1 ) / 2 - textwidth(boxm[3].text) / 2, boxm[3].posy1 + 30, s);
}
draw();
// 新建输出图像
img_output = new IMAGE(num_length, num_height);
// 初始化处理区域
SetWorkingImage(img_output);
setbkcolor(BLACK);
cleardevice();
// 绘制
for(i = 0; i < num_length; i++)
{
for(j = 0; j < num_height; j++)
{
putpixel(i, j, RGB(img_m[i][j], img_m[i][j], img_m[i][j]));
}
}
SetWorkingImage();
// 显示
putimage(0, 0, img_output);
// 返回输出图像
return img_output;
}
// 灰度化函数
IMAGE* Gary::gray(IMAGE* img_input)
{
// 新建输出图像
img_output = new IMAGE(num_length, num_height);
// 初始化处理区域
SetWorkingImage(img_output);
setbkcolor(WHITE);
cleardevice();
// 加载输入图像
putimage(0, 0, img_input);
// 处理
int i, j;
for(i = 0; i < num_length; i++)
{
for(j = 0; j < num_height; j++)
{
putpixel(i, j, RGBtoGRAY(getpixel(i, j)));
}
}
SetWorkingImage();
// 显示
putimage(0, 0, img_output);
// 返回输出图像
return img_output;
}
// 轮廓查找函数,阈值
IMAGE* Gary::contours_finding(IMAGE* img_input, int num_threshold)
{
// 加载输入图像
putimage(0, 0, img_input);
// 处理
TCHAR s[15];
int i, j, k, flag;
struct Node1
{
int num_box;
int posx, posy;
};
Node1 box[4];
struct Node2
{
int num_box;
};
Node2 eat[10000];
POINT pts[4];
// 存储
for(i = 0; i < num_length; i++)
{
for(j = 0; j < num_height; j++)
{
img_b[i][j] = getpixel(i, j) / 256 / 256;
img_b[i][j] = ( img_b[i][j] > 200 ? 1 : 0 );
}
}
// 新建输出图像
img_output = new IMAGE(num_length, num_height);
// 初始化处理区域
SetWorkingImage(img_output);
setbkcolor(WHITE);
cleardevice();
SetWorkingImage();
// 寻找
// 遍历
for(j = 1; j < num_height - 1; j++)
{
for(i = 1; i < num_length - 1; i++)
{
// 有亮点,通过连通域寻找连续区域
if(img_b[i][j] == 1)
{
// 改点,避免重复
img_b[i][j] = 0;
// 范围参数初始化,左上右下点
box[0].num_box = i + j * num_length;
box[1].num_box = i + j * num_length;
box[2].num_box = i + j * num_length;
box[3].num_box = i + j * num_length;
// 循环参数初始化
k = 0;
flag = 1;
// 三消类算法
eat[0].num_box = i + j * num_height;
while(k != flag)
{
// 上下左右
// 上
if(img_b[eat[k].num_box % num_length][eat[k].num_box / num_length - 1] == 1)
{
img_b[eat[k].num_box % num_length][eat[k].num_box / num_length - 1] = 0;
eat[flag].num_box = eat[k].num_box - num_length;
flag++;
if(flag == 10000)flag = 0;
}
// 下
if(img_b[eat[k].num_box % num_length][eat[k].num_box / num_length + 1] == 1)
{
img_b[eat[k].num_box % num_length][eat[k].num_box / num_length + 1] = 0;
eat[flag].num_box = eat[k].num_box + num_length;
flag++;
if(flag == 10000)flag = 0;
}
// 左
if(img_b[eat[k].num_box % num_length - 1][eat[k].num_box / num_length] == 1)
{
img_b[eat[k].num_box % num_length - 1][eat[k].num_box / num_length] = 0;
eat[flag].num_box = eat[k].num_box - 1;
flag++;
if(flag == 10000)flag = 0;
}
// 右
if(img_b[eat[k].num_box % num_length + 1][eat[k].num_box / num_length] == 1)
{
img_b[eat[k].num_box % num_length + 1][eat[k].num_box / num_length] = 0;
eat[flag].num_box = eat[k].num_box + 1;
flag++;
if(flag == 10000)flag = 0;
}
// 改范围
if(eat[k].num_box > box[1].num_box)
{
box[1].num_box = eat[k].num_box;
}
if(eat[k].num_box % num_length < box[2].num_box % num_length)
{
box[2].num_box = eat[k].num_box;
}
if(eat[k].num_box % num_length > box[3].num_box % num_length)
{
box[3].num_box = eat[k].num_box;
}
k++;
if(k == 10000)k = 0;
}
pts[0].x = box[2].num_box % num_length;
pts[0].y = box[0].num_box / num_length;
pts[2].x = box[3].num_box % num_length;
pts[2].y = box[1].num_box / num_length;
pts[1].x = pts[0].x;
pts[1].y = pts[2].y;
pts[3].x = pts[2].x;
pts[3].y = pts[0].y;
// 算面积,与阈值比较
k = abs(pts[0].x - pts[2].x) * abs(pts[0].y - pts[2].y);
if(k >= num_threshold)
{
SetWorkingImage(img_output);
// 参数
setfillcolor(BLACK);
setlinestyle(PS_SOLID, 3);
setlinecolor(GREEN);
// 绘制
fillpolygon(pts, 4);
setlinestyle(PS_SOLID, 1);
SetWorkingImage();
}
}
}
// 延迟,优化空间,避免太快,更好做演示
// Sleep(1);
// 进度显示
_stprintf_s(s, _T("%0.1d"), j * 100 / num_length);
outtextxy(boxm[7].posx1 + ( boxm[7].posx2 - boxm[7].posx1 ) / 2 - textwidth(boxm[7].text) / 2, boxm[7].posy1 + 30, s);
}
draw();
// 加载原图
SetWorkingImage(img_output);
loadimage(img_temporary, address_load, num_length, num_height);
putimage(0, 0, img_temporary, SRCPAINT);
SetWorkingImage();
// 绘制
putimage(0, 0, img_output);
// 返回
return img_output;
}
// 完整进程函数
void Gary::completeprocessing()
{
// 灰度化
img_temporary = gray(img_temporary);
// 高斯滤波
// img_temporary = gaussian_smoothing_filtering(img_temporary);
// 中值滤波
// img_temporary = median_filtering(img_temporary, 1);
// 边缘检测
img_temporary = edge_detection(img_temporary, 1, 100);
// 形态学滤波
img_temporary = morphological_filtering(img_temporary, 1, 10, 2);
img_temporary = morphological_filtering(img_temporary, 0, 8, 6);
img_temporary = morphological_filtering(img_temporary, 1, 10, 2);
// img_temporary = morphological_filtering(img_temporary, 1, 10, 2);
// img_temporary = morphological_filtering(img_temporary, 1, 10, 2);
// 轮廓检测
img_temporary = contours_finding(img_temporary, 4000);
}
// 主进程函数
void Gary::carry()
{
// 窗口定义
hOut = initgraph(701, 401);
SetWindowText(hOut, _T("车牌定位及分割"));
// 背景绘制
setbkcolor(WHITE);
cleardevice();
// 进程控制
exit_carry = 0;
while(exit_carry == 0)
{
initialization();
move();
}
closegraph();
}
// 绘制函数
void Gary::draw()
{
TCHAR s[15];
int i, j;
// 主界面
setlinecolor(BLACK);
setfillcolor(WHITE);
setlinestyle(PS_SOLID, 1);
fillrectangle(401, 0, 700, 400);
// 根据按钮数量绘制
settextcolor(BLACK);
for(i = 0; i < num_button; i++)
{
// 边框
fillrectangle(boxm[i].posx1, boxm[i].posy1, boxm[i].posx2, boxm[i].posy2);
// 文字
outtextxy(boxm[i].posx1 + ( boxm[i].posx2 - boxm[i].posx1 ) / 2 - textwidth(boxm[i].text) / 2, boxm[i].posy1 + 10, boxm[i].text);
}
// 设置参数
setbkcolor(WHITE);
settextcolor(BLACK);
setlinecolor(BLACK);
// 变量绘制
j = 30;
// 图片长度:num_length
i = 0;
_stprintf_s(s, _T("%0.1d"), num_length);
outtextxy(boxm[i].posx1 + ( boxm[i].posx2 - boxm[i].posx1 ) / 2 - textwidth(boxm[i].text) / 2, boxm[i].posy1 + j, s);
// 图片宽度:num_height
i = 1;
_stprintf_s(s, _T("%0.1d"), num_height);
outtextxy(boxm[i].posx1 + ( boxm[i].posx2 - boxm[i].posx1 ) / 2 - textwidth(boxm[i].text) / 2, boxm[i].posy1 + j, s);
// 加载地址
i = 9;
outtextxy(boxm[i].posx1 + ( boxm[i].posx2 - boxm[i].posx1 ) / 2 - textwidth(boxm[i].text) / 2, boxm[i].posy1 + j, address_load);
// 保存地址
i = 10;
outtextxy(boxm[i].posx1 + ( boxm[i].posx2 - boxm[i].posx1 ) / 2 - textwidth(boxm[i].text) / 2, boxm[i].posy1 + j, address_save);
}
// 初始化函数
void Gary::initialization()
{
int i;
// 参数初始化
num_length = 400;
num_height = 400;
TCHAR s1[30] = _T("t0.png");
for(i = 0; i < 30; i++) { address_load[i] = s1[i]; }
TCHAR s2[30] = _T("image.png");
for(i = 0; i < 30; i++) { address_save[i] = s2[i]; }
// 按钮的初始化
num_button = 12;
// 坐标
for(i = 0; i < num_button; i++)
{
boxm[i].posx1 = 405 + 100 * ( i % 3 );
boxm[i].posy1 = 15 + 100 * ( i / 3 );
boxm[i].posx2 = 495 + 100 * ( i % 3 );
boxm[i].posy2 = 85 + 100 * ( i / 3 );
}
// 内容
boxm[0].text = _T("图像长度"); boxm[1].text = _T("图像宽度"); boxm[2].text = _T("灰度化");
boxm[3].text = _T("高斯滤波"); boxm[4].text = _T("中值滤波"); boxm[5].text = _T("边缘检测");
boxm[6].text = _T("形态学滤波"); boxm[7].text = _T("轮廓检测"); boxm[8].text = _T("完整处理");
boxm[9].text = _T("加载图片"); boxm[10].text = _T("保存图片"); boxm[11].text = _T("退出");
// 第一次加载
img_temporary = new IMAGE(num_length, num_height);
loadimage(img_temporary, address_load, num_length, num_height);
// 图片显示
putimage(0, 0, img_temporary);
// 第一次绘制
draw();
}
// 窗口主视角函数,获取用户操作
void Gary::move()
{
TCHAR ss[15];
// 鼠标定义
ExMessage m;
int i;
int a, b;
exit_move = 0;
while(exit_move == 0)
{
// 鼠标信息
if(peekmessage(&m, EM_MOUSE | EM_KEY))
{
// 左键单击判断
if(m.message == WM_LBUTTONDOWN)
{
// 判断是否点击了有效按钮
for(i = 0; i < num_button; i++)
{
if(m.x > boxm[i].posx1 && m.y > boxm[i].posy1 && m.x < boxm[i].posx2 && m.y < boxm[i].posy2)
{
break;
}
}
// 点击矩形按钮
switch(i)
{
// 图像长度:num_length
case 0:
{
// 输入
InputBox(ss, 10, _T("输入图像长度(20 ~ 400)"));
_stscanf_s(ss, _T("%d"), &i);
if(i >= 20 && i <= 400)
{
// 边长
num_length = i;
}
else { MessageBox(hOut, _T("输入错误,不在范围内"), _T("来自小豆子的提醒"), MB_OK); }
break;
}
// 图像宽度:num_height
case 1:
{
// 输入
InputBox(ss, 10, _T("输入图像宽度(20 ~ 400)"));
_stscanf_s(ss, _T("%d"), &i);
if(i >= 20 && i <= 400)
{
// 边长
num_height = i;
}
else { MessageBox(hOut, _T("输入错误,不在范围内"), _T("来自小豆子的提醒"), MB_OK); }
break;
}
// 灰度化
case 2:
{
img_temporary = gray(img_temporary);
break;
}
// 高斯滤波
case 3:
{
img_temporary = gaussian_smoothing_filtering(img_temporary);
break;
}
// 中值滤波
case 4:
{
// 输入
InputBox(ss, 10, _T("输入卷积核宽度(1 ~ 3)"));
_stscanf_s(ss, _T("%d"), &i);
if(i >= 1 && i <= 3)
{
img_temporary = median_filtering(img_temporary, i);
}
else { MessageBox(hOut, _T("输入错误,不在范围内"), _T("来自小豆子的提醒"), MB_OK); }
break;
}
// 边缘检测
case 5:
{
// 输入
InputBox(ss, 10, _T("输入检测模式 0.横向 1.纵向 2.全检测"));
_stscanf_s(ss, _T("%d"), &i);
if(i >= 0 && i <= 2)
{
InputBox(ss, 10, _T("输入阈值(0 ~ 255)"));
_stscanf_s(ss, _T("%d"), &a);
if(a >= 0 && a <= 255)
{
img_temporary = edge_detection(img_temporary, i, a);
}
else { MessageBox(hOut, _T("输入错误,不在范围内"), _T("来自小豆子的提醒"), MB_OK); }
}
else { MessageBox(hOut, _T("输入错误,不在范围内"), _T("来自小豆子的提醒"), MB_OK); }
break;
}
// 形态学滤波
case 6:
{
// 输入
InputBox(ss, 10, _T("输入滤波模式 0.腐蚀 1.膨胀"));
_stscanf_s(ss, _T("%d"), &i);
if(i >= 0 && i <= 1)
{
InputBox(ss, 10, _T("输入长度(2 ~ 50)"));
_stscanf_s(ss, _T("%d"), &a);
if(a >= 2 && a <= 50)
{
InputBox(ss, 10, _T("输入宽度(2 ~ 50)"));
_stscanf_s(ss, _T("%d"), &b);
if(b >= 2 && b <= 50)
{
img_temporary = morphological_filtering(img_temporary, i, a, b);
}
else { MessageBox(hOut, _T("输入错误,不在范围内"), _T("来自小豆子的提醒"), MB_OK); }
}
else { MessageBox(hOut, _T("输入错误,不在范围内"), _T("来自小豆子的提醒"), MB_OK); }
}
else { MessageBox(hOut, _T("输入错误,不在范围内"), _T("来自小豆子的提醒"), MB_OK); }
break;
}
// 轮廓检测
case 7:
{
// 输入
InputBox(ss, 10, _T("输入面积阈值(1 ~ 100000)"));
_stscanf_s(ss, _T("%d"), &i);
if(i >= 1 && i <= 100000)
{
img_temporary = contours_finding(img_temporary, i);
}
else { MessageBox(hOut, _T("输入错误,不在范围内"), _T("来自小豆子的提醒"), MB_OK); }
break;
}
// 完整处理
case 8:
{
completeprocessing();
break;
}
// 加载图片
case 9:
{
i = MessageBox(hOut, _T("是否默认地址"), _T("来自小豆子的提醒"), MB_YESNO);
if(i == 7)
{
// 打开文件对话框
OPENFILENAME ofn;
// 保存文件完整路径
TCHAR szFileName[MAX_PATH] = { 0 };
// 设置过滤条件
TCHAR szFilter[] = TEXT("PNG Files (*.png)\0*.png\0") \
TEXT("All Files (*.*)\0*.*\0\0");
ZeroMemory(&ofn, sizeof(ofn));
// 保存文件完整路径
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
// 保存文件名
ofn.lpstrFileTitle = address_load;
ofn.nMaxFileTitle = MAX_PATH;
ofn.lpstrFilter = szFilter;
// 默认扩展名
ofn.lpstrDefExt = _T("png");
ofn.lpstrTitle = NULL;
ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
ofn.lStructSize = sizeof(OPENFILENAME);
// 拥有该对话框的窗口句柄
ofn.hwndOwner = hOut;
GetOpenFileName(&ofn);
}
img_temporary = new IMAGE(num_length, num_height);
// 加载
loadimage(img_temporary, address_load, num_length, num_height);
// 主界面
setlinecolor(BLACK);
setfillcolor(WHITE);
setlinestyle(PS_SOLID, 1);
fillrectangle(0, 0, 400, 400);
// 图片显示
putimage(0, 0, img_temporary);
break;
}
// 保存图片
case 10:
{
i = MessageBox(hOut, _T("是否默认地址"), _T("来自小豆子的提醒"), MB_YESNO);
if(i == 7)
{
InputBox(address_save, 10, _T("输入图片相对位置及名称"));
}
// 保存
saveimage(address_save, img_temporary);
break;
}
// 退出
case 11:
{
exit_move = 1;
exit_carry = 1;
break;
}
default:break;
}
draw();
}
}
}
}
// 主函数
int main(void)
{
Gary G;
G.carry();
return 0;
}