数字图像处理——手机号码识别提取

实现步骤

1. 图像预处理

  1. 将图像转为灰度图
  2. 观察原始图像的直方图,确认亮度值分布。

2. 图像增强

  1. 使用Photoshop调整曲线进一步增强对比度。

3. 噪声添加与去除

  1. 向图像添加椒盐噪声,模拟实际图像可能遇到的噪声干扰。
  2. 应用中值滤波器去除椒盐噪声,恢复图像质量。

4. 图像分割

  1. 通过阈值分割技术,将前景(数字)与背景分离,转为二值图像,同时过滤掉未处理好的阴影部分。

5. 图像平滑与锐化

  1. 对图像进行平滑处理,以减少噪声。
  2. 应用拉普拉斯锐化增强图像中的边缘,使数字更加清晰。

6. 图像形态学操作

  1. 执行开操作(先腐蚀后膨胀),去除数字周围的小噪声。
  2. 执行闭操作(先膨胀后腐蚀),填充数字内部的小空洞。

7. 图像去噪

  1. 再次应用中值滤波,进一步去除可能残留的噪声。

8. 图像旋转与对齐

  1. 使用最邻近插值方法旋转图像,使数字水平对齐。

9. 目标检测

  1. 应用灰度投影法,识别并定位图像中的数字。

实现代码

  1. 增加椒盐噪声
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;

    }

}



  1. 中值滤波
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];

    }

}
  1. 阈值分割
  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;

        }

    }
  1. 图像平滑
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;

    }

}
  1. 拉普拉斯锐化

   

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;

        }

   }
  1. 开闭操作
①腐蚀

    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;

                   }

                }

            }

        }

   }
  1. 最邻近插值旋转
 //旋转角度

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];

            }

    }
  1. 目标检测

思路:

遍历图像:如果像素值小于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;

}

实验心得

本次大作业基本涵盖了所学知识,实验中,通过非线性变换对原图进行预处理,增强图片对比度,过滤掉不重要的背景信息。然后再通过一系列形态学变化、图像增强的方式,减少噪声,增强目标信息,使得目标能在图片中很好的展示而不受其他信息的影响,便于后续目标检测的任务。在这个过程中也遇到了一些麻烦,比如在中值滤波去噪时,也会导致数字边缘信息的丢失,所以在执行图像增强的这一个过程中,顺序也很关键,不同的执行顺序能显示出不同的效果。此外,在编写目标检测这一算法的过程中,有时因为噪声不完全滤掉,导致噪声信号也被框出。解决方法是增加了一个噪声阈值,避免受到噪声干扰。

总的来说,通过本次实验,让我对图像处理的许多方式有了更深刻的认识,相信这些知识技能将在未来的学习中发挥重要作用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值