clc;
close all;
clear all;
% -------------Gamma Transformations-----------------
%f = imread('Fig0316(4)(bottom_left).tif');
f = imread('seed.tif');
Gamma = 0.4;
g2 = myExpEnhance(f,Gamma);
figure();
subplot(221); imshow(f); xlabel('a).Original Image');
subplot(222),imhist(f),title('原图像直方图');%显示原始图像直方图
subplot(223); imshow(g2); xlabel('b).Gamma Transformations \gamma = 0.4');
subplot(224),imhist(g2),title('增强图像直方图');%显示原始图像直方图
指数增强核心函数为:
function dst_img=myExpEnhance(src_img,Gamma)
src_img = mat2gray(src_img,[0 255]);%将图像矩阵A中介于amin和amax的数据归一化处理, 其余小于amin的元素都变为0, 大于amax的元素都变为1。
C = 1;
g2 = C*(src_img.^Gamma);
%反归一化
max=255;
min=0;
dst_img=uint8(g2*(max-min)+min);
三,对数变换
对数变换主要用于将图像的低灰度值部分扩展,将其高灰度值部分压缩,以达到强调图像低灰度部分的目的。变换方法由下式给出。
这里的对数变换,底数为(v+1),实际计算的时候,需要用换底公式。其输入范围为归一化的【0-1】,其输出也为【0-1】。对于不同的底数,其对应的变换曲线如下图所示。
底数越大,对低灰度部分的强调就越强,对高灰度部分的压缩也就越强。相反的,如果想强调高灰度部分,则用反对数函数就可以了。看下面的实验就可以很直观的理解,下图是某图像的二维傅里叶变换图像,其为了使其灰度部分较为明显,一般都会使用灰度变换处理一下。
效果图:
参考代码:
clc;
close all;
clear all;
%-------------Log Transformations-----------------
f = imread('seed.tif');
g_1 = myLogEnhance(f,10);
g_2 = myLogEnhance(f,100);
g_3 = myLogEnhance(f,200);
figure();
subplot(2,2,1);
imshow(f);xlabel('a).Original Image');
subplot(2,2,2);
imshow(g_1);xlabel('b).Log Transformations v=10');
subplot(2,2,3);
imshow(g_2);xlabel('c).Log Transformations v=100');
subplot(2,2,4);
imshow(g_3);
xlabel('d).Log Transformations v=200');
对数变换核心函数
function dst_img=myLogEnhance(src_img,v)
c=1.0;
src_img = mat2gray(src_img,[0 255]);
g =c*log2(1 + v*src_img)/log2(v+1);
%反归一化
max=255;
min=0;
dst_img=uint8(g*(max-min)+min);
四,灰度拉伸
灰度拉伸也用于强调图像的某个部分,与伽马变换与对数变换不同的是,灰度拉升可以改善图像的动态范围。可以将原来低对比度的图像拉伸为高对比度图像。实现灰度拉升的方法很多,其中最简单的一种就是线性拉伸。而这里介绍的方法稍微复杂一些。灰度拉伸所用数学式如下所示。
同样的,其输入r为【0-1】,其输出s也为【0-1】。这个式子再熟悉不过了,跟巴特沃斯高通滤波器像极了,其输入输出关系也大致能猜到是个什么形状的。但是,这里就出现一个问题了,输入为0时候,式子无意义了。所以,在用Matlab计算的时候,将其变为如下形式。
这里的eps,就是Matlab里面,一个很小数。如此做的话,式子变得有意义了。但是,其输入范围为【0-1】的时候,其输出范围变为了。输出范围大致为【0-1】,为了精确起见,使用mat2gray函数将其归一化到精确的[0-1]。调用格式如下。
五,线性拉伸
为了突出感兴趣的目标或者灰度区间,相对抑制那些不感兴趣的灰度区域,可采用分段线性法,常用的是三段线性变换
参考程序:
clc;
close all;
clear all;
I=imread('seed.tif');
[m,n,k]=size(I);
figure (1)
imshow('seed.tif');title(' 原图像');
mid=mean(mean(I));
%横轴
fa=20; fb=80;
%纵轴
ga=50; gb=230;
J=myLinearEnhance(I,fa,fb,ga,gb);
figure (2)
imshow(J);title(' 线性拉伸图像');
pixel_f=1:256;
pixel_g=zeros(1,256);
%三段斜率,小于1表示该段将会被收缩
k1=double(ga/fa);
k2=(gb- ga)/(fb- fa);
k3=(256- gb)/(256- fb);
for i=1:256
if i <= fa
pixel_g(i)= k1*i;
elseif fa < i && i <= fb
pixel_g(i)= k2*( i- fa)+ ga;
else
pixel_g(i)= k3*( i - fb)+ gb;
end
end
figure (3)
plot(pixel_f,pixel_g);
核心函数:
function dst_img=myLinearEnhance(src_img,fa,fb,ga,gb)
[height,width] = size(src_img);
dst_img=uint8(zeros(height,width));
src_img=double(src_img);
%三段斜率
k1=ga/fa;
k2=(gb- ga)/(fb- fa);
k3=(255- gb)/(255- fb);
for i=1:height
for j=1:width
if src_img(i,j) <= fa
dst_img(i,j)= k1*src_img(i,j);
elseif fa < src_img(i,j) && src_img(i,j) <= fb
dst_img(i,j)= k2*( src_img(i,j)- fa)+ ga;
else
dst_img(i,j)= k3*( src_img(i,j)- fb)+ gb;
end
end
end
dst_img=uint8(dst_img);
附录:
附录网上的另一份讲解:
直方图均衡化算法分为三个步骤,第一步是统计直方图每个灰度级出现的次数,第二步是累计归一化的直方图,第三步是计算新的像素值。
第一步:
for(i=0;i<height;i++)
for(j=0;j<width;j++)
n[s[i][j]]++;
for(i=0;i<L;i++)
p[i]=n[i]/(width*height);
这里,n[i]表示的是灰度级为i的像素的个数,L表示的是最大灰度级,width和height分别表示的是原始图像的宽度和高度,所以,p[i]表示的就是灰度级为i的像素在整幅图像中出现的概率(其实就是p[]这个数组存储的就是这幅图像的归一化之后的直方图)。
第二步:
for(i=0;i<=L;i++)
for(j=0;j<=i;j++)
c[i]+=p[j];
c[]这个数组存储的就是累计的归一化直方图。
第三步:
max=min=s[0][0];
for(i=0;i<height;i++)
for(j=0;j<width;j++)
if(max<s[i][j]){
max=s[i][j];
}else if(min>s[i][j]){
min=s[i][j];
}
找出像素的最大值和最小值。
for(i=0;i<height;i++)
for(j=0;j<width;j++)
t[i][j]=c[s[i][j]]*(max-min)+min;
t[][]就是最终直方图均衡化之后的结果。
收录优秀代码:
这份代码写得不错,学习了,原博客地址见参考资源【3】!
#include <stdio.h>
#include <iostream>
#include "fftw3.h"
#include "string"
#include "vector"
#include <windows.h>
#include <opencv2/legacy/legacy.hpp>
#include <opencv2/nonfree/nonfree.hpp>//opencv_nonfree模块:包含一些拥有专利的算法,如SIFT、SURF函数源码。
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <opencv2/nonfree/features2d.hpp>
using namespace cv;
using namespace std;
class hisEqt
{
public:
hisEqt::hisEqt();
hisEqt::~hisEqt();
public:
int w;
int h;
int nlen;
int *pHis;
float *pdf;
//=====求像素分布概率密度====
void getPdf();
//======统计像素个数=======
void getHis(unsigned char*imgdata);
//==========画统计分布直方图===============
void drawHistogram(const float*pdf,Mat &hist1);
//===========直方图均衡化==========
void hisBal();
//====直方图均衡化后的图像===
void imgBal(unsigned char* img);
};
hisEqt::hisEqt() :nlen(0){
pHis = new int[256 * sizeof(int)];
memset(pHis, 0, 256 * sizeof(int));
pdf = new float[255 * sizeof(float)];
memset(pdf, 0, 255 * sizeof(float));
}
hisEqt::~hisEqt(){
delete[]pHis;
delete[]pdf;
}
//======统计像素个数=======
void hisEqt::getHis(unsigned char*imgdata){
for (int i = 0; i<nlen; i++)
{
pHis[imgdata[i]]++;
}
}
//=====求像素分布概率密度====
void hisEqt::getPdf(){
for (int k = 0; k<256; k++)
{
pdf[k] = pHis[k] / float(nlen);
}
}
//===========直方图均衡化==========
void hisEqt::hisBal(){
for (int k = 1; k<256; k++)
{
pdf[k] += pdf[k - 1];
}
for (int k = 0; k<256; k++)
{
pHis[k] = 255 * pdf[k];
}
}
//====直方图均衡化
void hisEqt::imgBal(unsigned char* img){
for (int i = 0; i<nlen; i++)
{
img[i] = pHis[img[i]];
}
}
void hisEqt::drawHistogram(const float *pdf, Mat& hist1){
for (int k = 0; k<256; k++)
{
if (k % 2 == 0)
{
Point a(k, 255), b(k, 255 - pdf[k] * 2550);
line(hist1,
a,
b,
Scalar(0, 0, 255),
1);
}
else
{
Point a(k, 255), b(k, 255 - pdf[k] * 2550);
line(hist1,
a,
b,
Scalar(0, 255, 0),
1);
}
}
}
int main()
{
Mat image = imread("Fig0651(a)(flower_no_compression).tif");
if (!image.data)
return -1;
Mat hist2(256, 256, CV_8UC3, Scalar(0, 0, 0));
Mat hist1(256, 256, CV_8UC3, Scalar(0, 0, 0));
Mat imgOut = Mat(image.rows, image.cols, CV_8UC3, Scalar(0, 0, 0));
vector<Mat> planes;
int chn = image.channels();
if (chn == 3)
{
split(image, planes);
}
while (chn)
{
chn--;
unsigned char* imageData = new unsigned char[sizeof(unsigned char)*(image.cols*image.rows)];
memcpy(imageData, planes[chn].data, planes[chn].cols*planes[chn].rows);
hisEqt his;//自定义的类
his.nlen = image.rows*image.cols;
his.getHis(imageData);
his.getPdf();
// //======画原图直方图并保存============
his.drawHistogram(his.pdf, hist1);
string pic_name = "hisline";
pic_name = pic_name + to_string(chn);
pic_name=pic_name+ ".jpg";
imwrite(pic_name, hist1);
his.hisBal();
his.getPdf();
// //======画均衡化后直方图并保存============
his.drawHistogram(his.pdf, hist2);
string pic_name0 = "his_balanceline";
pic_name0 = pic_name0 + to_string(chn);
pic_name0 = pic_name0 + ".jpg";
imwrite(pic_name0, hist2);
// //=====图像均衡化===
his.imgBal(imageData);
memcpy(planes[chn].data, imageData, planes[chn].cols*planes[chn].rows);
delete[] imageData;
imageData = NULL;
}
merge(planes, imgOut);//单通道合并
imwrite("result.jpg", imgOut);
return 0;
}
参考资源
【1】http://blog.csdn.net/xiajun07061225/article/details/6910129
【2】数字图像处理,冈萨雷斯著
【3】http://blog.csdn.net/bettyshasha/article/details/46940805
【4】http://blog.csdn.net/terryzero/article/details/6043821
【5】http://www.myexception.cn/image/1450848.html