简单二值图降噪

来源:互联网 发布:洛阳平安数据科技招聘 编辑:程序博客网 时间:2024/06/07 22:27


Reference:

 

如何用C++读取bmp图像:

http://bbs.csdn.net/topics/340208546

bmp格式图像结构:

http://www.cnblogs.com/kingmoon/archive/2011/04/18/2020097.html


一、问题描述

给一幅二值图,在其中加入一定比例(这里默认是10%)的噪声,然后使用相应的降噪搜索算法,对噪声图像进行降噪处理,获得噪声图像和降噪后的图像,并对比原始图像和降噪后的图像,评估降噪的效果。

 

 

二、算法与模型描述

在进行搜索的时候,用到了如下评估模型:

其中,xi表示在搜索得到的当前状态,即当前处理的二值图,yi表示噪声图像,由于图像为二值图,我们设每个像素点的取值为 {-1,  1},这里的三个参数都是正数。

则图中第一项指,当前图像像素点值的综合与参数的积,至于这项的物理意义我不是很理解;第二项指,当前图像每个像素点与周围点(例如周围的八个点)的乘积和与参数的积,如果这一项越小说明,当前的像素点与周围的差异越小,也就越不可能是噪点;第三项指,当前图像与噪声图像对应点的乘积和与参数的积,因为噪声图像的噪点一般较少,所以我们搜索得到的图像要和噪声图像尽可能拟合,如果这一项的值越小,说明拟合效果越好。

        

         关于实现的算法:

         这里实现了两种降噪算法:

1.      遍历噪声图像的所有点,对于每个像素点计算周围八个点中,其值与当前像素点值不等的个数count,如果count > 4说明当前像素点为噪点,则改变像素值。

这个算法虽然简单,但是高效而且降噪效果很好,但是容易丢失图像细节。

 

2.      类似于“爬山法“的局部搜索算法,对于当前图像,随机选取一个像素点,根据前面提到的评估模型进行试算,判断改变这个像素点的值,会不会使得评估模型的值变小,如果是则改变它,否则进行下一次选取。对于随机选取策略,首先在全局范围内选取一个像素点进行试算,接下来的200次都在这个像素点周围随机选取另一个点进行试算,每进行200次就在全局范围内再选取一个点重复上面的操作。

这样也有点类似,在宽度优先搜索的基础上进行一定深度的深度优先搜索。

 

三、降噪效果

 

这里我定义了一个降噪结果的评估函数,具体就是比较一下降噪后的图像与原图像像素点匹配的比例,这样就可以初步判断降噪效果。

 

下面是测试样例:

简单遍历算法:

src0.bmp

src1.bmp

src2.bmp

src3.bmp

以上是使用第一种简单遍历算法降噪的效果,从左至右三幅图分别指,原图、降噪后的图、噪声图。

 

搜索算法:

src0.bmp

src1.bmp

src2.bmp

src3.bmp

以上是使用搜索算法降噪的效果,从左至右三幅图分别指,原图、降噪后的图、噪声图。

 

 

从上面的结果我们可以看到,虽然两种降噪算法的在与原图的匹配率上相差无几,但是视觉效果上还是简单遍历算法的降噪效果比较好!


四、实现代码:

程序只能处理bmp格式的24位真彩色图,其他格式图像的处理可以自己再进行设计,且只能在windows下运行。

bmp.h

#ifndef BMP_H#define BMP_H #include <windows.h>class BMP{public:    BMP();    ~BMP();     bool readBmp(char* path);    bool saveBmp(char* path);    void addNoise();    void noSearchRemove();    void searchRemove();    double grade(char* path);    private:    //读入图像数据的指针    unsigned char *pBmpBuf;    //图像的宽    int bmpWidth;    //图像的高    int bmpHeight;    //颜色表指针    RGBQUAD *pColorTable;    int biBitCount;    int lineByte;};#endif
bmp.cpp

#include "bmp.h"#include <cmath>#include <iomanip>#include <fstream.h>#include <iostream>#include <stdlib.h>#include <cstdio>#include <ctime>using namespace std;BMP::BMP(){    pBmpBuf = NULL;    bmpWidth = bmpHeight = lineByte = 0;    pColorTable = NULL;    biBitCount = 0;}BMP::~BMP(){    delete []pBmpBuf;    delete []pColorTable;}// 读取 bmp格式图像 参数是图像路径 bool BMP::readBmp(char* path){    FILE *fp = fopen(path, "rb");    if (!fp) return 0;    fseek(fp, sizeof(BITMAPFILEHEADER), 0);    BITMAPINFOHEADER header;      fread(&header, sizeof(BITMAPINFOHEADER), 1,fp);    bmpWidth = header.biWidth;    bmpHeight = header.biHeight;    biBitCount = header.biBitCount;    // 因为lineByte在wins中必须是4的倍数 所以这里为了满足这个要求做了一下处理  lineByte指一行像素的字节数     lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;     //申请颜色表所需要的空间,读颜色表进内存    if (biBitCount == 1)    {        delete []pColorTable;        pColorTable = new RGBQUAD[1];        fread(pColorTable, sizeof(RGBQUAD), 1, fp);    }       else if (biBitCount == 4)    {        delete []pColorTable;        pColorTable = new RGBQUAD[16];        fread(pColorTable, sizeof(RGBQUAD), 16, fp);     }        else if(biBitCount == 8)    {        delete []pColorTable;        pColorTable = new RGBQUAD[256];        fread(pColorTable, sizeof(RGBQUAD), 256, fp);    }    //申请位图数据所需要的空间,读位图数据进内存    delete []pBmpBuf;    pBmpBuf = new unsigned char[lineByte * bmpHeight];    fread(pBmpBuf, 1, lineByte * bmpHeight, fp);        fclose(fp);    return 1;}// 保存得到的bmp图像 默认保存路径是当前 目录下的 autoSave.bmpbool BMP::saveBmp(char* path = "autoSave.bmp"){    if(!pBmpBuf)        return 0;    int colorTablesize=0;    if(biBitCount == 1)        colorTablesize = 4;    else if (biBitCount == 4)        colorTablesize = 64;    else if (biBitCount == 8)        colorTablesize = 1024;    FILE *fp = fopen(path, "wb");    if(!fp) return 0;    BITMAPFILEHEADER fileHead;    // bmp类型    fileHead.bfType = 0x4D42;    // bfSize是图像文件4个组成部分之和    fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTablesize + lineByte * bmpHeight;    fileHead.bfReserved1 = 0;    fileHead.bfReserved2 = 0;    //bfOffBits是图像文件前3个部分所需空间之和    fileHead.bfOffBits = 54 + colorTablesize;    fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);        //申请位图信息头结构变量,填写信息头信息    BITMAPINFOHEADER header;    header.biBitCount = biBitCount;    header.biClrImportant = 0;    header.biClrUsed = 0;    header.biCompression = 0;    header.biHeight = bmpHeight;    header.biPlanes = 1;    header.biSize = 40;    header.biSizeImage = lineByte*bmpHeight;    header.biWidth = bmpWidth;    header.biXPelsPerMeter=0;    header.biYPelsPerMeter=0;    //写位图信息头进内存    fwrite(&header, sizeof(BITMAPINFOHEADER), 1, fp);    //如果灰度图像,有颜色表,写入文件     if (biBitCount == 1)        fwrite(pColorTable, sizeof(RGBQUAD), 1, fp);    else if (biBitCount == 4)        fwrite(pColorTable, sizeof(RGBQUAD), 16, fp);    else if (biBitCount == 8)        fwrite(pColorTable, sizeof(RGBQUAD), 256, fp);    fwrite(pBmpBuf, bmpHeight*lineByte, 1, fp);    fclose(fp);    return 1;}// 给图像添加 10%的噪声 void BMP::addNoise(){    srand(time(NULL));    int times = bmpHeight * bmpWidth / 10, x, y;    for (int i = 0; i < times; ++ i)    {        x = rand() % bmpHeight;        y = rand() % bmpWidth;        if (*(pBmpBuf + x*lineByte + y*3) == 0)        {            for (int j = 0; j < 3; ++ j)                *(pBmpBuf + x*lineByte + y*3 + j) = 255;        }        else        {            for (int j = 0; j < 3; ++ j)                *(pBmpBuf + x*lineByte + y*3 + j) = 0;                    }    }    return; }// 简单遍历 void BMP::noSearchRemove(){    int row[8] = {-1, -1, 0, 1, 1, 1, 0, -1}, col[8] = {0, 1, 1, 1, 0, -1, -1, -1};    int  n, tmpi, tmpj;    for (int i = 0; i < bmpHeight; ++ i)    {        for (int j = 0; j < bmpWidth; ++ j)        {            n = 0;            for (int k = 0; k < 8; ++ k)            {                tmpi = row[k] + i;                tmpj = col[k] + j;                if (tmpi >= 0 && tmpi < bmpHeight && tmpj >= 0 && tmpj < lineByte)                  {                    if (*(pBmpBuf + i*lineByte + j*3) == *(pBmpBuf + tmpi*lineByte + tmpj*3))                    {                        n ++;                    }                }            }            if (n < 4)            {                if (*(pBmpBuf + i*lineByte + j*3) == 0)                 {                    for (int l = 0; l < 3; ++ l)                        *(pBmpBuf + i*lineByte + j*3 + l) = 255;                }                else                 {                    for (int l = 0; l < 3; ++ l)                        *(pBmpBuf + i*lineByte + j*3 + l) = 0;                                    }            }        }    }}// 类爬山法搜索 void BMP::searchRemove(){    int row[8] = {-1, -1, 0, 1, 1, 1, 0, -1}, col[8] = {0, 1, 1, 1, 0, -1, -1, -1}, add;    // t为搜索次数 count为局部搜索次数     int t = bmpHeight * bmpWidth * 8, tmpi, tmpj, i = 0, j = 0, count = 200;    // 评估模型的三个参数     double h = 0.5, B = 1.06, n = 2.15, tmp;    unsigned char* cur = new unsigned char[lineByte * bmpHeight];    for (int i = 0; i < lineByte * bmpHeight; ++ i)        *(cur + i) = *(pBmpBuf + i);    srand(time(NULL));    while (t --)     {          // 随机选取像素点             if (count > 0)        {            add = rand() % 8;            i += row[add];            j += col[add];            count --;        }        else         {            count = 200;            i = rand() % bmpHeight;            j = rand() % bmpWidth;        }        tmp = 0;        // 开始试算         if (i >= 0 && i < bmpHeight && j >= 0 && j < bmpWidth)        {            if (*(cur + i*lineByte + j*3) == *(pBmpBuf + i*lineByte + j*3))                tmp -= 2*n;            else                 tmp += 2*n;                            if (*(cur + i*lineByte + j*3) == 0)                tmp += 2*h;            else                tmp -= 2*h;            for (int k = 0; k < 8; ++ k)            {                tmpi = i + row[k];                tmpj = j + col[k];                if (tmpi >= 0 && tmpi < bmpHeight && tmpj >= 0 && tmpj < bmpWidth)                {                    if (*(cur + i*lineByte + j*3) != *(cur + tmpi*lineByte + tmpj*3))                        tmp += 4*B;                    else                         tmp -= 4*B;                }            }            // 试算结束 判断是否修改像素点             if (tmp > 0)             {                if (*(cur + i*lineByte + j*3) == 0)                {                    for (int l = 0; l < 3; ++ l)                        *(cur + i*lineByte + j*3 + l) = 255;                }                else                {                    for (int l = 0; l < 3; ++ l)                        *(cur + i*lineByte + j*3 + l) = 0;                                    }            }        }    }    // 保存降噪后的图     for (int i = 0; i < lineByte * bmpHeight; ++ i)        *(pBmpBuf + i) = *(cur + i);      delete []cur; }// 评分函数 double BMP::grade(char* path){    long long count = 0;    unsigned char* cur = new unsigned char[lineByte * bmpHeight];    for (int i = 0; i < lineByte * bmpHeight; ++ i)        *(cur + i) = *(pBmpBuf + i);    readBmp(path);    for (int i = 0; i < bmpHeight; ++ i)    {        for (int j = 0; j < bmpWidth; ++ j)        {            if (*(cur + i*lineByte + j*3) == *(pBmpBuf + i*lineByte + j*3))                count ++;        }    }    delete []cur;    return (double)count / (double)(bmpHeight * bmpWidth);}
main.cpp

#include "bmp.cpp"int main(){    BMP a;    char s[100];          bool flag;       cout << "加噪后的图像自动保存在当前路径的 noise.bmp\n降噪后的图像自动保存在当前路径的 reduced.bmp\n";    cout << "按 ctrl + c 退出程序\n";    cout << "请选择降噪算法 1 为搜索算法 0 为简单遍历: ";    cin >> flag;    cout << endl << endl;     while (1)    {        cout << "请输入原图像路径: ";        scanf("%s", s);        a.readBmp(s);                a.addNoise();        a.saveBmp("noised.bmp");        if (flag)            for (int i = 0; i < 1; ++ i)                a.searchRemove();        else            for (int i = 0; i < 2; ++ i)                a.noSearchRemove();                                    a.saveBmp("reduced.bmp");                double t = a.grade(s);        cout << fixed << setprecision(4) << "降噪后的图与原图的匹配率为: " << t*100 << "%" << endl;        cout << endl;     }        system("pause");    return 0;}
0 0
原创粉丝点击