对于灰度变换与直方图均衡化都是图像增强经常用到的方法,方法原理与解释在这里就不做过多的叙述,本算法都是正常的传统算法没有进行算法优化过的,之前在说过针对算法优化本人也在学习,最近本人忙着找工作可能更新的少,有时间就会更新吧。废话不多说,直接上代码。
1.第一部分:图像读写
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include<iostream>
#include<assert.h>
#include<vector>
#define WIDTHBYTES(bits) (((bits)+31)/32*4)
using namespace std;
BYTE* Read8BitBmpFile2Img(const char* filename, int* width, int* height)
{
FILE* BinFile;
BITMAPFILEHEADER FileHeader;
BITMAPINFOHEADER BmpHeader;
BYTE* plmg;
unsigned int size;
int Suc = 1, w, h;
//open file
*width = *height = 0;
if ((BinFile = fopen(filename, "rb")) == NULL) return NULL;
//read struct info
if (fread((void*)&FileHeader, 1, sizeof(FileHeader), BinFile) != sizeof(FileHeader)) Suc = -1;
if (fread((void*)&BmpHeader, 1, sizeof(BmpHeader), BinFile) != sizeof(BmpHeader)) Suc = -1;
if ((Suc == -1) || (FileHeader.bfOffBits < sizeof(FileHeader) + sizeof(BmpHeader)))
{
fclose(BinFile);
return NULL;
}
//read Image data
*width = w = BmpHeader.biWidth;
*height = h = BmpHeader.biHeight;
size = w * h;
fseek(BinFile, FileHeader.bfOffBits, SEEK_SET);
if ((plmg = new BYTE[size]) != NULL)
{
for (int i = 0; i < h; i++) //0,1,2,3,4(5);400-499
{
if (fread(plmg + (h - 1 - i) * w, sizeof(BYTE), w, BinFile) != w)
{
fclose(BinFile);
delete plmg;
plmg = NULL;
return NULL;
}
fseek(BinFile, (w + 3) / 4 * 4 - w, SEEK_CUR);
}
}
fclose(BinFile);
return plmg;
}
bool Write8BitImg2BmpFile(BYTE* pImg, int width, int height, const char* filename)
//当宽度不是4的倍数时自动添加成4的倍数
{
FILE* BinFile;
BITMAPFILEHEADER FileHeader;
BITMAPINFOHEADER BmpHeader;
int i, extend;
bool Suc = true;
BYTE p[4], * pCur;
//Open File
if ((BinFile = fopen(filename, "w+b")) == NULL) { return false; }
//Fill the FileHeader
FileHeader.bfType = ((WORD)('M' << 8) | 'B');
FileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256 * 4L;
FileHeader.bfSize = FileHeader.bfOffBits + width * height;
FileHeader.bfReserved1 = 0;
FileHeader.bfReserved2 = 0;
if (fwrite((void*)&FileHeader, 1, sizeof(FileHeader), BinFile) != sizeof(FileHeader)) Suc = false;
//Fill the ImgHeader
BmpHeader.biSize = 40;
BmpHeader.biWidth = width;
BmpHeader.biHeight = height;
BmpHeader.biPlanes = 1;
BmpHeader.biBitCount = 8;
BmpHeader.biCompression = 0;
BmpHeader.biSizeImage = 0;
BmpHeader.biXPelsPerMeter = 0;
BmpHeader.biYPelsPerMeter = 0;
BmpHeader.biClrUsed = 0;
BmpHeader.biClrImportant = 0;
if (fwrite((void*)&BmpHeader, 1, sizeof(BmpHeader), BinFile) != sizeof(BmpHeader)) Suc = false;
//write Pallete
for (i = 0, p[3] = 0; i < 256; i++)
{
p[3] = 0;
p[0] = p[1] = p[2] = i;//blue,green,red
if (fwrite((void*)p, 1, 4, BinFile) != 4) { Suc = false; break; }
}
//write image data
extend = (width + 3) / 4 * 4 - width;
if (extend == 0)
{
for (pCur = pImg + (height - 1) * width; pCur >= pImg; pCur -= width)
{
if (fwrite((void*)pCur, 1, width, BinFile) != (unsigned int)width) Suc = false;//真实的数据
}
}
else
{
for (pCur = pImg + (height - 1) * width; pCur >= pImg; pCur -= width)
{
if (fwrite((void*)pCur, 1, width + 1, BinFile) != (unsigned int)width) Suc = false;//真实的数据
for (i = 0; i < extend; i++)//扩充的数据
if (fwrite((void*)(pCur + width - 1), 1, 1, BinFile) != 1) Suc = false;
}
}
//return
fclose(BinFile);
return Suc;
}
2.第二部分:图像数据处理之灰度变换
2.1取反
void fanzhun(unsigned char* inputImg, unsigned char* outputImg, int width, int height)
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
outputImg[i * width + j] = 255 - inputImg[i * width + j];
}
}
}
2.2(对数变换)s = clog(1 + r) c为常数,本次测试中c取10
void logarithm(unsigned char* inputImg, unsigned char* outputImg, int width, int height)
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
outputImg[i * width + j] = (unsigned char)(10 * log((1 + inputImg[i * width + j])));
}
}
}
2.3(伽马变换)s = crγ 其中 c 和 γ 为正常数.(其中γ<1时,降低对比度,γ>1时,提高对比度)
void gamma(unsigned char* inputImg, unsigned char* outputImg, int width, int height)
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
outputImg[i * width + j] = (unsigned char)pow(inputImg[i * width + j], 1.05);
outputImg[i * width + j] = outputImg[i * width + j] < 255 ? outputImg[i * width + j] : 255;
}
}
}
3.第三部分:直方图均衡化
void EqualizeHist(unsigned char* inputImg, unsigned char* outputImg, int width, int height)
{
int gray[256] = { 0 }; //记录每个灰度级别下的像素个数
double gray_prob[256] = { 0 }; //记录灰度分布密度
double gray_distribution[256] = { 0 }; //记录累计密度
int gray_equal[256] = { 0 }; //均衡化后的灰度值
int gray_sum = height * width; //像素总数
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
gray[(int)inputImg[i*width+j]]++; //记录每个灰度级下的像素个数,一共255个灰度级。开辟了整形一维整形一维数组。
}
}
for (int i = 0; i < 256; i++)
{
gray_prob[i] = ((double)gray[i] / gray_sum); //记录灰度分布密度,每个灰度级的像素数占整个图像的比例。
}
gray_distribution[0] = gray_prob[0];
for (int i = 1; i < 256; i++)
{
gray_distribution[i] = gray_distribution[i - 1] + gray_prob[i];
}
for (int i = 0; i < 256; i++)
{
gray_equal[i] = (unsigned char)(255 * gray_distribution[i] ); //均衡化后的灰度值
}
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
outputImg[i*width+j] = gray_equal[(int)inputImg[i * width + j]]; //灰度映射
}
}
}
4.主函数部分
int main()
{
int width, height, func = 0, threshold = 0;
clock_t startTime, endTime;
startTime = clock(); //计时开始
char readPath[] = "D:\\机器视觉学习工程\\lianxi\\me.bmp";
BYTE* pGryImg = Read8BitBmpFile2Img(readPath, &width, &height); //注意:下面对应位置也要该改名。
BYTE* dstImg = new BYTE[width * height];
memset(dstImg, 0, width * height*sizeof(unsigned char));
printf("%d,%d\n", width, height);
//EqualizeHist(pGryImg, dstImg, width, height);
//guidingHist(pGryImg, dstImg, width, height);
//fanzhun(pGryImg, dstImg, width, height);
//logarithm(pGryImg, dstImg, width, height);
//gamma(pGryImg, dstImg, width, height);
//histogram_equalization(pGryImg, dstImg, width, height);
cout << "操作完成" << endl;
char writePath[] = "D:\\机器视觉学习工程\\lianxi\\mejunzhi.bmp";
Write8BitImg2BmpFile(dstImg, width, height, writePath);
ShellExecuteA(nullptr, "open", writePath, "", "", SW_SHOW); //打开写入的位图结果
endTime = clock(); //计时结束
cout << "The run time is: " << (float)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
delete[] dstImg;
delete[] pGryImg;
}
结束语:本代码写的有点次,都是传统的写法,还需要很多优化。比如遍历图象数据通过遍历首尾地址就可以了,我这样遍历有点慢,而且一些乘除法用移位的方式进行计算也能提高算法速度,还有之前的高斯滤波可以变成两个一维进行计算。嗯,那先这样吧。稍后继续再写一章。