简单二值图降噪
来源:互联网 发布:洛阳平安数据科技招聘 编辑:程序博客网 时间: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;};#endifbmp.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;}
- 简单二值图降噪
- 简单
- 简单
- 简单
- 简单
- 简单
- 简单
- 简单
- 简单
- 简单
- 简单
- 简单
- 简单
- 简单
- 简单
- 简单
- 简单简单
- 简单 简单 简单
- Problem C: Celebrity Split
- Cocoa 框架 For iOS(一) 框架的介绍,Objectivie-C运行时能力的解析等
- c++ builder常用方法
- 25个最好免费下载电子书(Ebooks)的网站
- Android之ScrollLayout左右滑动效果实现
- 简单二值图降噪
- poj 1001 exponent by zhyh2010
- 《Linux内核设计与实现》——内核同步方法
- MySQL创建index提高多表查询效率
- iOS框架介绍(三)---Cocoa Touch 层
- 编程小技巧
- ubuntu linux 触控板失灵的解决方案
- UVA 10673 Play with Floor and Ceil (扩展欧几里德)
- IntelliSense: #error 指令: Please use the /MD switch for _AFXDLL builds