实现步骤
1. 图像预处理
- 将图像转为灰度图
- 观察原始图像的直方图,确认亮度值分布。
2. 图像增强
- 使用Photoshop调整曲线进一步增强对比度。
3. 噪声添加与去除
- 向图像添加椒盐噪声,模拟实际图像可能遇到的噪声干扰。
- 应用中值滤波器去除椒盐噪声,恢复图像质量。
4. 图像分割
- 通过阈值分割技术,将前景(数字)与背景分离,转为二值图像,同时过滤掉未处理好的阴影部分。
5. 图像平滑与锐化
- 对图像进行平滑处理,以减少噪声。
- 应用拉普拉斯锐化增强图像中的边缘,使数字更加清晰。
6. 图像形态学操作
- 执行开操作(先腐蚀后膨胀),去除数字周围的小噪声。
- 执行闭操作(先膨胀后腐蚀),填充数字内部的小空洞。
7. 图像去噪
- 再次应用中值滤波,进一步去除可能残留的噪声。
8. 图像旋转与对齐
- 使用最邻近插值方法旋转图像,使数字水平对齐。
9. 目标检测
- 应用灰度投影法,识别并定位图像中的数字。
实现代码
- 增加椒盐噪声
std::random_device rd;
std::mt19937 gen(rd());
std::bernoulli_distribution saltDist(0.05);
std::bernoulli_distribution pepperDist(0.05);
std::uniform_int_distribution<> rowDis(0, lHeight - 1);
std::uniform_int_distribution<> colDis(0, lWidth - 1);
for (int i = 0; i < lWidth * lHeight; ++i) {
int row = rowDis(gen);
int col = colDis(gen);
int cur = row * lWidth + col;
// 根据概率生成椒盐噪声
if (saltDist(gen)) {
lpDIBBits[cur] = 0;
}
else if (pepperDist(gen)) {
lpDIBBits[cur] = 0;
}
}
- 中值滤波
int n = 1;//邻域大小
BYTE b[1000];
for (int i = 0; i < lHeight; i++) {
for (int j = 0; j < lWidth; j++) {
int k = 0;
// 田字模板
for (int x = -n; x <= n; x++) {
for (int y = -n; y <= n; y++) {
if (i + x >= 0 && i + x < lHeight && j + y >= 0 && j + y < lWidth) {
b[k++] = lpDIBBits[(i + x) * lWidth + j + y];
}
}
}
// 排序并取中值
std::sort(b, b + k);
int cur = i * lWidth + j;
lpNewDIBBits[cur] = b[k / 2];
}
}
- 阈值分割
LPBYTE lpSrc;//像素指针
LONG lLineBytes;//每行字节数
lLineBytes=WIDTHBYTES(lWidth *8);
long i,j; // 循环变量
float num=170;//分割点
/****************************请在下面编写图像分割的算法*************/
for (int i = 0; i < lHeight; i++) {
for (int j = 0; j < lWidth; j++) {
if (lpDIBBits[i * lLineBytes + j] > num)lpDIBBits[i * lLineBytes + j] = (char)255;
else lpDIBBits[i * lLineBytes + j] = (char)0;
}
}
- 图像平滑
int a[9] = {
1,1,1,
1,2,1,
1,1,1
}; //平滑模板元素数组
LPSTR lpNewDIBBits;
HLOCAL hNewDIBBits;
hNewDIBBits = LocalAlloc(LHND, lWidth * lHeight);
lpNewDIBBits = (char*)LocalLock(hNewDIBBits);
int w = 0;//平滑算子权重
for (int i = 0; i < 9; i++)w += a[i];
for (int i = 0; i < lHeight; i++) {
for (int j = 0; j < lWidth; j++) {
int k = 0;
int sum = 0;
for (int x = -1; x < 2; x++) {
for (int y = -1; y < 2; y++) {
int n = i + x, m = j + y;
n = n < 0 ? 0 : n;
m = m < 0 ? 0 : m;
n = n >= lHeight ? lHeight - 1 : n;
m = m >= lWidth ? lWidth - 1 : m;
int cur = n * lWidth + m;
sum += lpDIBBits[cur] * a[k++];
}
}
lpNewDIBBits[i * lWidth + j] = sum / w;
}
}
- 拉普拉斯锐化
int a[9] = {
0,-1,0,
-1,5,-1,
0,-1,0
};//锐化模板元素数组
/****************************请在下面编写图像锐化的算法*************/
LPSTR lpNewDIBBits;
HLOCAL hNewDIBBits;
hNewDIBBits = LocalAlloc(LHND, lWidth * lHeight);
lpNewDIBBits = (char*)LocalLock(hNewDIBBits);
for (int i = 0; i < lHeight; i++) {
for (int j = 0; j < lWidth; j++) {
int k = 0;
int num = 0;
for (int x = -1; x < 2; x++) {
for (int y = -1; y < 2; y++) {
int n = i + x, m = j + y;
n = n < 0 ? 0 : n;
m = m < 0 ? 0 : m;
n = n >= lHeight ? lHeight - 1 : n;
m = m >= lWidth ? lWidth - 1 : m;//防止访问越界
int cur = n * lWidth + m;
num += lpDIBBits[cur] * a[k++];
}
}
if (num > 255)num = 255;//值域越界判断
if (num < 0)num = 0;
lpNewDIBBits[i * lWidth + j] = num;
}
}
- 开闭操作
①腐蚀
for (i = 0; i < lHeight; i++) {
for (j = 0; j < lWidth; j++) {
int center = i * lWidth + j;
lpNewDIBBits[center] = lpDIBBits[center];
if (lpDIBBits[center] == (char)255) {
int f = 1;//全包含
for (int n = -1; n < 1; n++) {
for (int m = -1; m < 1; m++) {
int cur = (i + n) * lWidth + j + m;
if (cur >= 0 && cur <= (lWidth - 1) * (lHeight - 1) && lpDIBBits[cur]==(char)0) {
f = 0;
break;
}
}
}
if (!f) {
lpNewDIBBits[center] = 0;
}
}
}
}
②膨胀
for (i = 0; i < lHeight; i++) {
for (j = 0; j < lWidth; j++) {
int center = i * lWidth + j;
lpNewDIBBits[center] = lpDIBBits[center];
if (lpDIBBits[center] == (char)255) {
for (int n = -1; n < 1; n++) {
for (int m = -1; m < 1; m++) {
int cur = (i + n) * lWidth + j + m;
if (cur >= 0 && cur <= (lWidth - 1) * (lHeight - 1))
lpNewDIBBits[cur] = 255;
}
}
}
}
}
- 最邻近插值旋转
//旋转角度
FLOAT RotateAngle=(float)-3.14/3.3;
for (i0 = 0; i0 < lNewHeight; i0++) {
for (j0 = 0; j0 < lNewWidth; j0++) {
// 计算对应源图像的坐标 (i, j)
float x = fCosa * j0 + fSina * i0 + f1;
float y = -fSina * j0 + fCosa * i0 + f2;
j = (int)(x + 0.5);
i = (int)(y + 0.5);
// 检查是否在源图像范围内
if (i < 0 || i >= lHeight || j < 0 || j >= lWidth) {
lpNewDIBBits[i0 * lNewLineBytes + j0] = 255;
continue;
}
// 写入新图像
lpNewDIBBits[i0 * lNewLineBytes + j0] = lpDIBBits[i * lLineBytes + j];
}
}
- 目标检测
思路:
遍历图像:如果像素值小于200,增加该像素所在行和列的计数;
确定边界:通过检查 row 数组,找到图像中连续暗区域的起始和结束行,这些行定义了目标区域的垂直边界。padding 是矩形框的内边距,避免检测到的边界过于紧贴目标区域。
绘制矩形框:再次遍历r1到r2行,使用 col 数组来确定目标区域的水平边界。检测到目标后,在确定的边界上绘制矩形框,以框出目标区域。
BOOL CAView::ObDetection(LPBYTE lpDIBBits, LONG lWidth, LONG lHeight, int n)
{
LPBYTE lpSrc;//像素指针
LONG lLineBytes;//每行字节数
lLineBytes = WIDTHBYTES(lWidth * 8);
int row[10005] = { 0 };
int col[10005] = { 0 };
memset(row, 0, sizeof row);
memset(col, 0, sizeof col);
for (int i = 0; i < lHeight; i++) {
for (int j = 0; j < lWidth; j++) {
int cur = i * lLineBytes + j;
if (lpDIBBits[cur] < (unsigned char)200) {
row[i]++;
col[j]++;
}
}
}
int r1, r2;
int padding = 4, noise1 = 15;//框内边距,噪声阈值
for (int i = 1; i < lHeight; i++) {
if (row[i] > noise1) {//找到上边界
r1 = i - padding;
while (i < lHeight && row[i] > 0) i++;
r2 = i + padding;
}
}
int color = 50, noise2 = 3;//噪声阈值
for (int i = r1; i <= r2; i++) {
for (int j = 0; j < lWidth; j++) {
if (row[i] > noise2 && col[j] > noise2) { // 检测到行的开始
int startJ = j - padding;
while (j < lWidth && col[j] > 0) j++;
int endJ = j + padding;
// 绘制矩形框
if (endJ - startJ > 0) {
// 绘制上边框
for (int k = startJ; k <= endJ; k++) {
lpDIBBits[r1 * lLineBytes + k] = color;
}
// 绘制下边框
for (int k = startJ; k <= endJ; k++) {
lpDIBBits[ r2 * lLineBytes + k] = color;
}
// 绘制左边框
for (int k = r1; k <= r2 ; k++) {
lpDIBBits[k * lLineBytes + startJ] = color;
}
// 绘制右边框
for (int k = r1; k <= r2 ; k++) {
lpDIBBits[k * lLineBytes + endJ] = color;
}
}
}
}
}
return TRUE;
}
实验心得
本次大作业基本涵盖了所学知识,实验中,通过非线性变换对原图进行预处理,增强图片对比度,过滤掉不重要的背景信息。然后再通过一系列形态学变化、图像增强的方式,减少噪声,增强目标信息,使得目标能在图片中很好的展示而不受其他信息的影响,便于后续目标检测的任务。在这个过程中也遇到了一些麻烦,比如在中值滤波去噪时,也会导致数字边缘信息的丢失,所以在执行图像增强的这一个过程中,顺序也很关键,不同的执行顺序能显示出不同的效果。此外,在编写目标检测这一算法的过程中,有时因为噪声不完全滤掉,导致噪声信号也被框出。解决方法是增加了一个噪声阈值,避免受到噪声干扰。
总的来说,通过本次实验,让我对图像处理的许多方式有了更深刻的认识,相信这些知识技能将在未来的学习中发挥重要作用。