2017-01-17 17:53:19 rongfzh1990 阅读数 33260

一、全局对比度增强1

1. 直方图均衡化 Histogram Equalization

a. 基本思想

对于图像中的灰度点做映射,使得整体图像的灰度大致符合均匀分布。增强图像的对比度。
- 对于灰度值连续的情况,使用灰度的累积分布函数CDF做转换函数,可以使得输出图像的灰度符合均匀分布。
- 对于灰度值不连续的情况,存在舍入误差,得到的灰度分布大致符合均匀分布。
- 直观地理解,如果某一个灰度范围(如200-201)的像素点很少,那么它的概率密度值就会很小,所以CDF在200-201附近的增长变化就会很小;反之,如果某一个灰度范围(如100-101)的像素点很多,CDF在100-101附近的增长变化会很大。总体来看,以灰度为横轴,CDF为纵轴画曲线。这种向上凸的曲线,很像gamma变换: s=crγγ<1的情形。将灰度集中的部分拉伸,而将灰度不集中的部分压缩,达到提高对比度的效果。
- 直方图均衡可以看做自适应的gamma变换或者分段变换。前者的优势在于,不需要指定任何参数,所有运算都是基于图像本身的。

b. 算法
  1. 根据图像灰度计算灰度概率密度函数PDF
  2. 计算累积概率分布函数CDF
  3. CDF归一化到原图灰度取值范围,如[0,255]。
  4. 之后CDF四舍五入取整,得到灰度转换函数sk=T(rk)
  5. CDF作为转换函数,将灰度为rk的点转换为sk灰度
c. matlab实验

代码2

%% 直方图均衡
clear all;clc;close all;
ImgFile='E:\图像处理\冈萨雷斯图片库\DIP3E_Original_Images_CH03\Fig0310(b)(washed_out_pollen_image).tif';
ImgIn=imread(ImgFile);
ImgHistEq=histeq(ImgIn,256);
figure;subplot(121);imshow(uint8(ImgIn));title('原图');
subplot(122);imshow(ImgHistEq);title('全局灰度增强 - 直方图均衡');
figure;subplot(121);imhist(ImgIn,256);
axis([0 255 0 1e5]);title('原图的直方图');
subplot(122);imhist(ImgHistEq,256);axis([0 255 0 1e5]);title('直方图均衡化后的直方图');
% 自定义直方图均衡
[counts,x]=imhist(ImgIn,256);
cum_counts=cumsum(counts);
cum_counts=uint8(cum_counts/max(cum_counts)*255);% 转化函数
figure;plot(x,cum_counts);axis([0 255 0 255]);
xlabel('原图灰度');ylabel('转换后灰度');title('原图CDF转化的灰度映射函数');
ImgOut=nan(size(ImgIn));
for i=1:length(x)
    ImgOut(ImgIn==x(i))=cum_counts(i);
end
ImgOut=uint8(ImgOut);
figure;imshow(uint8(ImgOut));title('自定义直方图均衡')
figure;imhist(ImgOut,256);axis([0 255 0 1e5]);title('自定义直方图均衡的直方图')

输出:
原图和均衡化后的图
图1. 原图和均衡化后的图。均衡后图像对比度明显增强,纹理和边缘更加清晰。

原图和均衡化后的图的直方图
图2. 原图和均衡化后的图的直方图。原图灰度分布在很狭窄的区间约[90,140]内。均衡化后灰度较为均匀的分布在整个[0, 255]区间内。

cdf
图3. 原图CDF归一化到[0, 255]后作为灰度变换函数。为了方便显示,画成连续曲线。

自定义均衡的直方图
图4. 自定义均衡的直方图。和Matlab自带histeq输出基本一致。

2. 直方图匹配 Histogram Matching

a. 基本思想

我的理解:

对于图像中的灰度点做映射,使得整体图像的灰度大致符合给定的分布。
想要图像的直方图Hs和期望直方图Hd相同, 那么二者的CDF也是相同的.
也就是说, 对于某一个灰度x, CDFs(x)=CDFd(x). 所以, 如果CDFs(x)=CDFd(y). 那么我们令x=y.
也就是将原图的x灰度映射为y灰度. 相当于将图像直方图的横轴做了一个点对点的缩放操作.
这个缩放操作的结果就是图像直方图向理想直方图靠近.
在缩放过程中, 如果图像的直方图某一段灰度区间被拉长了, 因为灰度都是整数, 所以直方图的bin会变得稀疏.
所以,
最终的直方图匹配的结果就是直方图的bin变得稀疏了. 有的灰度区间没有灰度点.

  • 对于某些图像,灰度分布过于不均衡,例如背景很大且均匀。直接做直方图均衡会把前景“洗白”,效果很差。
  • 直方图匹配需要设定一个直方图分布。不同的图像,此分布可能不同。
b. 算法
  1. 根据图像计算概率密度分布pr(r)
  2. 根据pr(r)计算累计分布函数sk=T(rk)
  3. 根据给定的目标分布pz(z)计算累计分布函数G(zq)
  4. 对于每一个k,找到一个q,使得G(zq)约等于sk
  5. 将原图中灰度为k的点变为灰度q
c. Matlab实验
clear all;clc;close all;
ImgFile='E:\图像处理\冈萨雷斯图片库\DIP3E_Original_Images_CH03\Fig0323(a)(mars_moon_phobos).tif';
ImgIn=imread(ImgFile);
figure;subplot(121);imshow(ImgIn);title('原图')
subplot(122);imhist(ImgIn);title('原图Hist')
% histeq
ImgHistEq=histeq(ImgIn);
figure;subplot(121);imshow(ImgHistEq);title('Hist EQ');
subplot(122);imhist(ImgHistEq,256);title('Hist EQ')
% 根据3.35(a)估计的分布
x=[0 8 16 180 200 255];
Y=[0 70000 8000 0 3139 0];
xi=0:255;
yi = interp1q(x',Y',xi');
figure;plot(xi,yi,'.');title('目标 Hist')
ImgHistMatch = histeq(ImgIn, yi);
figure;subplot(121);imshow(ImgHistMatch);title('histeq匹配')
subplot(122);imhist(ImgHistMatch,256);title('histeq匹配直方图')

输出:
这里写图片描述
图5. 原图和直方图

这里写图片描述
图6. 直方图均衡后图像及直方图

这里写图片描述
图7. 目标直方图

这里写图片描述
图8. 直方图匹配图像和直方图

自定义直方图匹配

%% 自定义匹配
clear all;clc;close all;
ImgFile='E:\图像处理\冈萨雷斯图片库\DIP3E_Original_Images_CH03\Fig0323(a)(mars_moon_phobos).tif';
ImgIn=imread(ImgFile);
x=[0 8 16 180 200 255];
Y=[0 70000 8000 0 3139 0];
xi=0:255;
yi = interp1q(x',Y',xi');
[counts, x]=imhist(ImgIn,256);
cum_counts=cumsum(counts);% 累加CDF
% 归一化到[0 255]
cum_counts=cum_counts./numel(ImgIn);
s_k=round(cum_counts*255);
cum_yi=cumsum(yi);% 目标累加CDF
g_z=round(cum_yi./sum(yi)*255); 
figure;plot(s_k,'r');hold on;plot(g_z,'g');grid on;hold off;
legend('s_k','g_z')
axis([0 255 0 255]);title('原图累计CDF 和 目标累计CDF')
map=nan(size(s_k));
for k=1:length(s_k)
    delta=abs(g_z-s_k(k));
    [~,z]=min(delta);
    map(k)=z;
end
figure;plot(0:255,map);axis([0 255 0 255]);grid on;
xlabel('输入灰度');ylabel('输出灰度');title('灰度映射曲线')

ImgOut=zeros(size(ImgIn));
for m=0:length(map)-1
    ImgOut(ImgIn==m)=map(m+1);
end
ImgOut=uint8(ImgOut);
figure;imshow(ImgOut);title('直方图匹配')
figure;imhist(ImgOut,256);title('直方图匹配');

输出:只列出转换函数
这里写图片描述
转换函数映射图

二、局部对比度增强

1. 邻域直方图均衡

将全局直方图均衡的思想应用于邻域直方图处理中。

2. 邻域直方图匹配

将全局直方图匹配的思想应用于邻域直方图处理中。

3. 邻域统计方法

a. 基本思想

对于一幅图像,灰度的均值MG表示平均光照,灰度的方差(或标准差)σG表示对比度的强弱。
对于图像某一个像素,邻域的灰度均值ML表征邻域光照强度,邻域的方差或者标准差σL表示邻域对比度的强弱。
根据MLσL的大小自主选择想要增强的点做增强(例如乘以某一个常数E)。

b. 算法
  1. 初始化:增强常数E,灰度下阈值k0,标准差下阈值 k1,标准差上阈值 k2,窗口半宽s
  2. 计算图像灰度均值MG和灰度标准差σG
  3. 对于每一个像素,计算邻域(大小为2step+1的方块)内灰度均值ML和标准差σL
  4. 如果ML<=k0MG并且k1σG<=σL<=k2σG,将像素灰度乘以E
c. matlab代码

测试脚本LocalEnhancement_test.m

%% 局部对比度增强
clear all;clc;close all;
ImgFile='E:\图像处理\冈萨雷斯图片库\DIP3E_Original_Images_CH03\tungsten_original.tif';
step=1;
para.E=4.0;
para.k0=0.4; % 均值下阈值
para.k1=0.02; % 标准差下阈值
para.k2=0.4; % 标准差上阈值
ImgIn=imread(ImgFile);
ImgHistEq=histeq(ImgIn,256);
ImgIn=double(ImgIn);
% ImgIn=double(rgb2gray(ImgIn));
[ MeanLocal,VarLocal ] = LocalStatistics( ImgIn, step );
[ ImgOut ] = LocalEnhancement( ImgIn, MeanLocal, VarLocal,para );

figure;imshow(uint8(ImgIn));title('原图');
figure;imshow(uint8(ImgOut));title('局部统计增强');
figure;imshow(ImgHistEq);title('全局灰度增强 - 直方图均衡');

函数LocalEnhancement.m

function [ ImgOut ] = LocalEnhancement( ImgIn, MeanLocal, VarLocal,para )
%LOCALENHANCEMENT Summary of this function goes here
% 使用局部均值和局部标准差实现局部图像增强
%   Detailed explanation goes here
[rows, cols]=size(ImgIn);
MeanGlobal=mean(ImgIn(:));
VarGlobal=std(ImgIn(:));
for i=1:rows
    for j=1:cols
        if MeanLocal(i,j)<=para.k0*MeanGlobal...
                &&VarLocal(i,j)>=para.k1*VarGlobal...
                &&VarLocal(i,j)<=para.k2*VarGlobal
            ImgIn(i,j)=para.E*ImgIn(i,j);
        end
    end
end
ImgOut=ImgIn;
end

函数LocalStatistics.m

function [ MeanLocal,VarLocal ] = LocalStatistics( ImgIn, step )
%LOCALSTATISTICS Summary of this function goes here
% 求局部的灰度均值灰度方差
% ImgIn - 输入灰度图,double
% step  - 窗口大小为2*step+1,step越大,细节越少
%   Detailed explanation goes here
% 矩阵边缘补齐
ImgIn=padarray(ImgIn,[step, step],'replicate');
[rows, cols] = size(ImgIn);
MeanLocal=zeros(size(ImgIn));
VarLocal=zeros(size(ImgIn));
for i = step+1:1:rows-step
    for j = step+1:1:cols-step
        LocalNeighbor=ImgIn(i-step:i+step,j-step:j+step);
        MeanLocal(i,j)=mean(LocalNeighbor(:));
        VarLocal(i,j)=std(LocalNeighbor(:));
    end
end
% 切割成和原图相同的大小
MeanLocal=MeanLocal(2:end-1,2:end-1);
VarLocal=VarLocal(2:end-1,2:end-1);

end

结果输出:
目的是想要增强右下方的纹理,而不改变中间较亮的纹理。全局灰度增强不但不能增强右下方的纹理,而且改变了中间较亮的纹理。局部统计增强达到了目的,但是产生了一些不想要的假边缘(如左上方)和奇异点(一些散乱的白点)。
源图像全局HistEQ
图9. 原图和全局灰度增强的图。全局histeq对右下角纹理的增强效果不明显,而且改变了中间较亮区域的灰度。

LocalStatistics
图10. 局部统计增强。右下方纹理得到明显的增强,而且不改变中间较亮区域的纹理。但是产生了一些边缘失真。


2017.4.24补充:


直方图均衡和其他的均衡方法都会把图像的直方图尽量调整的分布均匀一些. 尽量不会出现较高的尖峰和较大的灰度空档.这是因为这些算法不知道要增强哪些部分的灰度, 也不知道要去抑制哪些部分的灰度. 而某一部分灰度的增强必然意味着另外一部分灰度的压缩. 所以这些算法只能”雨露均沾”, 把hist中所有的灰度都调整到较为均匀的分布. 雨露均沾的结果, 可能是细节的增强和提升. 也有可能是背景被增强, 造成背景噪声放大, 还不如不增强. 针对后一种情况, 就有了CLAHE等限制对比度的算法.


  1. 本文主要参考冈萨雷斯《数字图像处理》第三版英文版
  2. 如果matlab读入*.tif图片出错,参见:
    http://jingyan.baidu.com/article/aa6a2c14f9441b0d4c19c4b0.html
2019-02-17 21:15:44 qq_34725005 阅读数 211
import cv2
import numpy as np

def convert(r):
    s = np.empty(r.shape,dtype=np.uint8)
    for j in range(r.shape[0]):
        for i in range(r.shape[1]):
            s[j][i]=(r[j][i]/255)**0.67*255
    return s

im = cv2.imread('tetet.jpg')
im_mat = np.asarray(im)
im_converted_mat = convert(im_mat)
cv2.imwrite('tetet2.jpg',im_converted_mat)
cv2.imshow('original',im)
cv2.imshow('img',im_converted_mat)
cv2.waitKey()

图片对比如下:
原图
在这里插入图片描述
处理后
在这里插入图片描述

2018-07-03 23:16:37 dz4543 阅读数 1953

1. 统计直方图

我的理解,灰度直方图就是统计整张图片的灰度值。比如说对于一张8位图,其灰度级数为0~255,那么灰度直方图便是统计灰度依次区0~255的个数,比如说灰度是127的像素个数,灰度是0的个数等等。

下面是用Python实现的代码:

  • 定义函数
def calGrayHist(image):
    r,c=image.shape#灰度图尺寸

    #创建一个一维数组grayHist,长度为255,用其序列表示灰度值
    grayHist= np.zeros([256],np.uint64)
    for i in range(r):
        for j in range(c):
            #遍历所有元素,把其灰度值所代表的序列指向的数组grayHist累加
            grayHist[image[i][j]]+=1
    return grayHist

import numpy as np
import cv2
import matplotlib.pyplot as plt

#读取图片
image= cv2.imread('D:/codes/shan.jpg',0)
#计算灰度直方图
grayHist= calGrayHist(image)

#画出直方图
x_range = range(256)
plt.plot(x_range,grayHist,'r',linewidth =2,c='black')

#设置坐标轴范围
y_maxValue=np.max(grayHist)
plt.axis([0,255,0,y_maxValue])

#设置坐标标签
plt.xlabel('graylevel')
plt.ylabel('number of pixels')

#显示灰度直方图
plt.show()

其原图如下,图片来源于互联网,若有版权问题请联系我。
这里写图片描述

非常漂亮的一张风景图,它的灰度直方图如下:
这里写图片描述

  • 也可以用matplotlib本身的计算直方图的函数:
import numpy as np
import cv2
import matplotlib.pyplot as plt

#读取图片
image= cv2.imread('D:/codes/shan.jpg',0)

r,c=image.shape#灰度图尺寸

#将二维图像矩阵变为为一维,便于计算
pixelSequence=image.reshape([r*c,])

#组数
numberBins=256

#计算灰度直方图
histogram,bins,patch=plt.hist(pixelSequence,numberBins,facecolor='black',histtype='bar')

#设置坐标轴范围
y_maxValue=np.max(histogram)
plt.axis([0,255,0,y_maxValue])

#设置坐标标签
plt.xlabel('graylevel')
plt.ylabel('number of pixels')

#显示图像
plt.show()

其灰度直方图如下:
这里写图片描述
这里解释下plt.hist()函数,其调用方式为:

n, bins, patches = plt.hist(arr, bins=10, normed=0, facecolor='black', edgecolor='black',alpha=1,histtype='bar')

hist的参数非常多,但常用的就这六个,只有第一个是必须的,后面四个可选:

  1. arr: 需要计算直方图的一维数组

  2. bins: 直方图的柱数,可选项,默认为10

  3. normed: 是否将得到的直方图向量归一化。默认为0

  4. facecolor: 直方图颜色

  5. edgecolor: 直方图边框颜色

  6. alpha: 透明度

  7. histtype: 直方图类型,‘bar’, ‘barstacked’, ‘step’, ‘stepfilled’

返回值 :

  1. n: 直方图向量,是否归一化由参数normed设定

  2. bins: 返回各个bin的区间范围

  3. patches: 返回每个bin里面包含的数据,是一个list

    • 当然也可以用opencv统计直方图函数 cv2.calcHist()来统计一张图的直方图。其函数用法如下:
cv2:calcHist(images; channels; mask; histSize; ranges[; hist[; accumulate]])
  1. images:原图像(图像格式为 uint8 或 float32)。当传入函数时应该用中括号 [] 括起来,例如: [img]。
  2. channels: 同样需要用中括号 [ ] 括起来,它会告诉函数我们要统计那幅图像的直方图。如果输入图像是灰度图,它的值就是 [0];如果是彩色图像的话,传入的参数可以是 [0], [1], [2] 它们分别对应着通道 B, G, R。
  3. mask: 掩模图像。要统计整幅图像的直方图就把它设为 None。但是如果你想统计图像某一部分的直方图的话,你就需要制作一个掩模图像,并使用它。
  4. histSize:BIN 的数目。也应该用中括号括起来,例如: [256]
  5. ranges: 像素值范围,通常为 [0, 256]

那么来简单试下:

import cv2
from matplotlib import pyplot as plt
img= cv2.imread('D:/codes/shan.jpg')#读取图像

color = ('b','g','r')
# 对一个列表或数组既要遍历索引又要遍历元素时
# 使用内置 enumerrate 函数会有更加直接,优美的做法
#enumerate 会将数组或列表组成一个索引序列。
# 使我们再获取索引和索引内容的时候更加方便
for i,col in enumerate(color):
    histr = cv2.calcHist([img],[i],None,[256],[0,256])
    plt.plot(histr,color = col)
    plt.xlimplt.xlim([0,256])#设置x坐标范围
plt.show()

每次循环的histr是一个 256x1 的数组,每一个值代表了与该通道像素值对应的像素点数
目。这里用的是matplotlib来画图,用OpenCV会比较麻烦。
其结果如下:这里写图片描述

  • 当然也可以用Numpy的中函数np.histogram() 帮我们统计直方图,例如:
#img.ravel() 将图像转成一维数组,这里没有中括号。
hist,bins = np.histogram(img.ravel(),256,[0,256])

hist 与上面计算的一样。但是这里的 bins 是 257,因为 Numpy 计算bins 的方式为: 0-0.99,1-1.99,2-2.99 等。所以最后一个范围是 255-255.99。为了表示它,所以在 bins 的结尾加上了 256。但是我们不需要 256,到 255就够了。

当然Numpy 还 有 一 个 函 数 np.bincount(), 所 以 对 于 一 维 直 方 图, 我 们 最 好 使 用 这 个函 数。 它 的 运 行 速 度 是np.histgram 的 十 倍。OpenCV 的函数要比np.histgram() 快 40 倍。

- 线性变换

直白来讲,如果一张图灰度级数为[50,100],那么可以通过线性变换将其进行拉伸。
对于一个输入图像I,宽W,高H,输出为O,其线性变换为:
O(r,c)=a*I(r,c)+b,0<=r

import cv2
import numpy as np

image= cv2.imread('D:/codes/shan.jpg',0)

MAX_VALUE = 120
value = 120

#调整对比度后,图像的效果显示窗口
cv2.namedWindow("contrast",cv2.WND_PROP_AUTOSIZE)
#调整系数,观察图像的变化
def callback_contrast(_value):
    #通过线性运算,调整图像对比度
    a = float(_value)/40.0
    #线性变换
    contrastImage = a*image
    #数据截断,大于255的数据截为255
    contrastImage[contrastImage>255]=255
    #数据类型转换
    contrastImage = np.round(contrastImage)#转为整数
    contrastImage = contrastImage.astype(np.uint8)#装维uint8类型的数据
    cv2.imshow("contrast",contrastImage)#显示
    cv2.imwrite("contrast.jpg",contrastImage)#写
callback_contrast(value)
cv2.createTrackbar("value","contrast",value,MAX_VALUE,callback_contrast)#创建一个滑动条
cv2.waitKey(0)
cv2.destroyAllWindows()

其效果如下:
value=30

value=60

3. 直方图正规化

设输入图像为I,高H,宽W。那么可以用I(r,c)表示r行c列的像素值。若将I中最小灰度级记为Imin,最大灰度级记为Imax,则I∈[Imin,Imax]。为使输出图像O灰度级范围为[Omin,Omax],可做如下映射关系:

这里写图片描述

其中r∈[0,H),c∈[0,W)。这个过程称为直方图正规化。由于(I(r,c)-Imin)/(Imax-Imin)∈[Omin,Omax],故而O(r,c)∈[Omin,Omax],一般令Omin=0,Omax=255.所以可以认为直方图正规化是一种自动选取a和b的值得线性变换方法。其中:
这里写图片描述
废话不说,上代码

import numpy as np
import cv2
import matplotlib.pyplot as plt
#直方图正规化
#1、若输入是 8 位图 ,一般设置 O_min = 0,O_max = 255
#2、若输入的是归一化的图像,一般设置 O_min = 0,O_max = 1
def histNormalized(InputImage,O_min = 0,O_max = 255):
    #得到输入图像的最小灰度值
    I_min = np.min(InputImage)

    #得到输入图像的最大灰度值
    I_max = np.max(InputImage)

    #得到输入图像的宽高
    rows,cols = InputImage.shape

    #输出图像
    OutputImage = np.zeros(InputImage.shape,np.float32)
    #输出图像的映射
    cofficient = float(O_max - O_min)/float(I_max - I_min)
    for r in range(rows):
        for c in range(cols):
            OutputImage[r][c] = cofficient*( InputImage[r][c] - I_min) + O_min
    return OutputImage

#读取图像
image= cv2.imread('D:/codes/shan.jpg',0)

#显示原图
cv2.imshow("image",image)
#直方图正规化
histNormResult = histNormalized(image)
#数据类型转换,灰度级显示
histNormResult = np.round(histNormResult)
histNormResult = histNormResult.astype(np.uint8)
#显示直方图正规化的图片
cv2.imshow("histNormlized",histNormResult)
cv2.imwrite("histNormResult.jpg",histNormResult)
'''
#如果输入图像是归一化的图像
image_0_1 = image/255.0
#直方图正规化
histNormResult = histNormalized(image_0_1,0,1)
#保存结果
histNormResult = 255.0*histNormResult
histNormResult = np.round(histNormResult)
histNormResult = histNormResult.astype(np.uint8)
'''
#显示直方图正规化后图片的灰度直方图
#组数
numberBins = 256
#计算灰度直方图
rows,cols = image.shape
histNormResultSeq = histNormResult.reshape([rows*cols,])
histogram,bins,patch_image= plt.hist(histNormResultSeq,numberBins,facecolor='black',histtype='bar')
#设置坐标轴的标签
plt.xlabel(u"gray Level")
plt.ylabel(u"number of pixels")
#设置坐标轴的范围
y_maxValue = np.max(histogram)
plt.axis([0,255,0,y_maxValue])
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()

由于原图采光还可以,灰度图本身效果还可以,做正规划后的灰度图和原图差别不大,就不放出来了。下面是正规划后的灰度直方图:
这里写图片描述
差别确实不大,读者可以尝试用其他图片。

用OpenCV可以这么写:

import cv2 as cv
import numpy as np

img= cv.imread('D:/codes/111.png',cv.IMREAD_ANYCOLOR)#读取图像
normalizedImg = np.zeros((800, 800))
normalizedImg =cv.normalize(img,  normalizedImg, 0, 255, cv.NORM_MINMAX)
cv.imshow('dst_rt', normalizedImg)
cv.waitKey(0)
cv.destroyAllWindows()

4. 伽马变换

设输入图像为I,高H,宽W。现将灰度值归一化到[0,1]之间。那么可以用I(r,c)表示r行c列的像素值,输出标记为O,伽马变换就是令O(r,c)=I(r,c)^γ,其中r∈[0,H),c∈[0,W)。
当γ=1时,图像不变。如果图像偏暗,可令γ∈(0,1),增强图像对比度,反之令γ>1降低对比度。

#伽马变换
import cv2
import numpy as np


image= cv2.imread('D:/codes/shan.jpg',0)

MAX_VALUE = 200
value = 40
segValue = float(value)

#伽马调整需要先将图像归一化
image_0_1 = image/255.0

#伽马调整后的图像显示窗口
cv2.namedWindow("gamma_contrast",cv2.WND_PROP_AUTOSIZE)

#调整 gamma 值,观察图像的变换
def callback_contrast(_value):
    gamma = float(_value)/segValue
    contrastImage = np.power(image_0_1,gamma)
    cv2.imshow("gamma_contrast",contrastImage)
    #保存伽马调整的结果
    contrastImage*=255
    contrastImage = np.round(contrastImage)
    contrastImage = contrastImage.astype(np.uint8)
    cv2.imwrite("gamma.jpg",contrastImage)
callback_contrast(value)
cv2.createTrackbar("value","gamma_contrast",value,MAX_VALUE,callback_contrast)
cv2.waitKey(0)
cv2.destroyAllWindows()

其效果如下:
value=117

- 5全局直方图均衡化

全局直方图均衡化是对输入图像I进行改变,是的输出图像O的灰度直方图是“平”的,即每一个灰度级的像素点个数是“相等”的。这里的相等并不是严格意义上的等于,而是约等于。对于直方图均衡化的实现主要分为四个步骤:
- 第一步:计算图像的灰度直方图
- 第二步:计算灰度直方图的累加直方图
- 第三部:根据累加直方图和直方图均衡化原理,得到输入灰度级和输出图像的每一个像素的灰度级之间的映射关系
- 第四步:根据第三步得到的灰度映射关系,循环得到输出图像的每一个像素的灰度级。

#全局直方图均衡化
import numpy as np
import cv2
import math
import matplotlib.pyplot as plt
#计算图像灰度直方图
def calcGrayHist(image):
    #灰度图像矩阵的宽高
    rows,cols = image.shape
    #存储灰度直方图
    grayHist = np.zeros([256],np.uint32)
    for r in range(rows):
        for c in range(cols):
            grayHist[image[r][c]] +=1
    return grayHist
#直方图均衡化
def equalHist(image):
    #灰度图像矩阵的宽高
    rows,cols = image.shape
    #计算灰度直方图
    grayHist = calcGrayHist(image)
    #计算累积灰度直方图
    zeroCumuMoment = np.zeros([256],np.uint32)
    for p in range(256):
        if p == 0:
            zeroCumuMoment[p] = grayHist[0]
        else:
            zeroCumuMoment[p] = zeroCumuMoment[p-1] + grayHist[p]
    #根据直方图均衡化得到的输入灰度级和输出灰度级的映射
    outPut_q = np.zeros([256],np.uint8)
    cofficient = 256.0/(rows*cols)
    for p in range(256):
        q = cofficient* float(zeroCumuMoment[p]) -1
        if q >= 0:
            outPut_q[p] = math.floor(q)
        else:
            outPut_q[p] = 0
    #得到直方图均衡化后的图像
    equalHistImage  = np.zeros(image.shape,np.uint8)
    for r in range(rows):
        for c in range(cols):
            equalHistImage[r][c] = outPut_q[image[r][c]]
    return equalHistImage

#读取图像
image= cv2.imread('D:/codes/shan.jpg',0)

#显示原图像
cv2.imshow("image",image)
#直方图均衡化
result = equalHist(image)
cv2.imshow("equalHist",result)
cv2.imwrite("equalHist.jpg",result)
#直方图均衡话后的灰度直方图
#组数
numberBins = 256
#计算灰度直方图
rows,cols = image.shape
histEqualResultSeq = result.reshape([rows*cols,])
histogram,bins,patch_image= plt.hist(histEqualResultSeq,numberBins,facecolor='black',histtype='bar')
#设置坐标轴的标签
plt.xlabel(u"gray Level")
plt.ylabel(u"number of pixels")
#设置坐标轴的范围
y_maxValue = np.max(histogram)
plt.axis([0,255,0,y_maxValue])
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()

全局直方图均衡化

6限制对比度自适应直方图均衡化

自适应直方图均衡化首先将图像划分为不重叠的区域块,然后对每一个块分别进行直方图均衡化。但当有噪声噪声会被放大,故而提出“限制对比度”,如果直方图的bin超过提前预定设好的“限制对比度”,那么会被裁剪,然后将裁减的部分均匀分不给其他bin,这样就重构了直方图。


import cv2

image= cv2.imread('D:/codes/shan.jpg',0)#读取图像


 #创建 CLAHE  对象
clahe = cv2.createCLAHE(clipLimit=1.0,tileGridSize=(28,28))
#限制对比度的自适应阈值均衡化
dst = clahe.apply(image)
#显示
cv2.imshow("src",image)
cv2.imshow("clahe",dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV提供的函数createCLAHE构建指向CLAHE对象的指针,其中默认设置“限制对比度”为40,块的大小为8×8。
效果如下:
这里写图片描述

2019-06-16 16:04:49 qq_38269799 阅读数 255

对比度增强

提前规定:输入图像为?,宽为?,高为?,输出图像?,??,?为输入图像?的 ?行?列像素灰度级,??,?为输出图像?的?行?列像素灰度级,0≤?≤?,0≤?≤?

 

灰度直方图

灰度直方图描述了一幅图像中每个灰度级在图像中占据的像素个数或者是比率,利用灰度直方图,通过数学方法,我们可以很容易的实现图像的处理

1.1线性变换

输入图像为?,宽为?,高为?,输出图像? ,图像的线性变换可以表示为

??,?=?∗??,?+?,0≤?≤?,0≤?≤?

优点:简单易懂

缺点:通过该方法可以调整图像的对比度,但是参数?,?需要根据自己实际图像进行多次测试来确定,比较繁琐

 

1.2直方图正则化

直方图正则化,依据了统计学原理,来进行图像的对比度调整:

一般规定????=255;????=0

 

优点:直方图正规化自动选取?,?的线性变换

缺点:1.变换后图像的灰度级减少,某些细节消失;2.某些图像,如直方图有高峰,经处理后对比度不自然的过分增强

 

 

1.3 伽马变换

 

伽马变换的方法,首先将灰度值诡异到[0.1]范围,??,?表示归一化之后的像素值

??,?=??,??,0≤?≤?,0≤?≤?

输出图像不变:伽马=1

输入图像整体过暗,可以调整令 0<γ<1, 来增加对比度

输入图像整体过暗,  可以调整令   γ>1对比度

优点:提升对比度效果比较好

缺点:需要手动调整γ值

 

1.4 全局直方图均衡

原图 ? 的直方图是各个像素级分布不均匀,导师对比度出现问题,全局直方图均衡使得输出图像?的像素均匀分布在每一像素级别,输出图像?的对比度达到一个比较好的效果。

全局直方图均衡就是在做这个工作,找出亮度级别为p的输入像素到亮度级别为q的输出像素的映射,最终使得输出图像?的像素均匀分布在每一像素级别。

所以推得:

1.5限制对比度的自适应直方图均衡化

将图形划分为不重叠的区域,单独对每个区域做直方图均衡化。为了避免每个区域中的噪声被放大,使用限制对比度的方法,某点像素值超过了该区域的限制对比度,则将起均匀分配给该区域中其他的像素点。

2014-07-28 20:24:50 EbowTang 阅读数 39586

关于图像增强必须清楚的基本概念

1.图像增强的目的:

1)改善图像的视觉效果,
2)转换为更适合于人或机器分析处理的形式
3)突出对人或机器分析有意义的信息
4)抑制无用信息,提高图像的使用价值
5)增强后的图像并不一定保真


2,图像增强的方法分类:

1)从处理对象分类:灰度图像,(伪)彩色图像
2)从处理策略分类:全局处理,局部处理(ROI ROI,Region of Interest Interest)
3)从处理方法分类:空间域(点域运算,即灰度变换;邻域方法,即空域滤波),频域方法
4)从处理目的分类:图像锐化,平滑去噪,灰度调整(对比度增强)


3,图像增强的方法之对比度增强

1)灰度变换法

线性变换(已实现)
对数变换(已实现)
指数变换(已实现)

2)直方图调整法
直方图均衡化(已实现)
直方图匹配(未实现)



一,直方图均衡化 

直方图均衡化的英文名称是Histogram Equalization. 

  图像对比度增强的方法可以分成两类:一类是直接对比度增强方法;另一类是间接对比度增强方法。直方图拉伸和直方图均衡化是两种最常见的间接对比度增强方法。直方图拉伸是通过对比度拉伸对直方图进行调整,从而“扩大”前景和背景灰度的差别,以达到增强对比度的目的,这种方法可以利用线性或非线性的方法来实现;直方图均衡化则通过使用累积函数对灰度值进行“调整”以实现对比度的增强。
  直方图均衡化处理的“中心思想”是把原始图像的灰度直方图从比较集中的某个灰度区间变成在全部灰度范围内的均匀分布。直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致相同。直方图均衡化就是把给定图像的直方图分布改变成“均匀”分布直方图分布。
  缺点: 
  1)变换后图像的灰度级减少,某些细节消失; 
  2)某些图像,如直方图有高峰,经处理后对比度不自然的过分增强。 
  直方图均衡化是图像处理领域中利用图像直方图对对比度进行调整的方法。 
  这种方法通常用来增加许多图像的局部对比度,尤其是当图像的有用数据的对比度相当接近的时候。通过这种方法,亮度可以更好地在直方图上分布。这样就可以用于增强局部的对比度而不影响整体的对比度,直方图均衡化通过有效地扩展常用的亮度来实现这种功能。
  这种方法对于背景和前景都太亮或者太暗的图像非常有用,这种方法尤其是可以带来X光图像中更好的骨骼结构显示以及曝光过度或者曝光不足照片中更好的细节。这种方法的一个主要优势是它是一个相当直观的技术并且是可逆操作,如果已知均衡化函数,那么就可以恢复原始的直方图,并且计算量也不大。这种方法的一个缺点是它对处理的数据不加选择,它可能会增加背景杂讯的对比度并且降低有用信号的对比度。
  

关于编程实现,同样是不调用matlab库函数,自己编程实现。这样可以更深刻地理解直方图均衡化技术,提高编程能力。

实现代码(matlab):


clc;
close all;
clear all;
 
src_img = imread('flyman_gray.bmp');  

figure (1) 
subplot(321),imshow(src_img),title('原图像');%显示原始图像  
subplot(322),imhist(src_img),title('原图像直方图');%显示原始图像直方图  

matlab_eq=histeq(src_img);         %利用matlab的函数直方图均衡化
subplot(323),imshow(matlab_eq),title('matlab直方图均衡化原图像');%显示原始图像  
subplot(324),imhist(matlab_eq),title('matlab均衡化后的直方图');%显示原始图像直方图 

dst_img=myHE(src_img);             %利用自己写的函数直方图均衡化
subplot(325),imshow(dst_img),title('手写均衡化效果');%显示原始图像  
subplot(326),imhist(dst_img),title('手写均衡化直方图');%显示原始图像直方图 

直方图均衡化函数的实现:


function dst_img=myHE(src_img)  

[height,width] = size(src_img);
dst_img=uint8(zeros(height,width));
%进行像素灰度统计;    
NumPixel = zeros(1,256);%统计各灰度数目,共256个灰度级    
for i = 1:height    
    for j = 1: width    
        NumPixel(src_img(i,j) + 1) = NumPixel(src_img(i,j) + 1) + 1;%对应灰度值像素点数量增加一    
    end    
end    
%计算灰度分布密度    
ProbPixel = zeros(1,256);    
for i = 1:256    
    ProbPixel(i) = NumPixel(i) / (height * width * 1.0);    
end    
%计算累计直方图分布    
CumuPixel = zeros(1,256);    
for i = 1:256    
    if i == 1    
        CumuPixel(i) = ProbPixel(i);    
    else    
        CumuPixel(i) = CumuPixel(i - 1) + ProbPixel(i);    
    end    
end    
  
% 指定范围进行均衡化  
% pixel_max=max(max(I));  
% pixel_min=min(min(I));  
pixel_max=255;  
pixel_min=0;  
%对灰度值进行映射(均衡化)    
for i = 1:height    
    for j = 1: width    
        dst_img(i,j) = CumuPixel(src_img(i,j)+1)*(pixel_max-pixel_min)+pixel_min;    
    end    
end    
return;



为什们和matlab的直方图不一样呢???



二,指数变换

指数变换(Power-Law )的公式:S=c*R^r,通过合理的选择c和r可以压缩灰度范围,算法以c=1.0/255.0, r=2实现。
要做该图像增强变换需要先做归一化,再指数变换,最后反归一化
增强效果展示:可以看见,改增强算法并不能很好的将像素尽可能的碾平。
指数增强参考程序为:
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

图像对比度增强

阅读数 592

图像的HSV拉伸增强对比度

博文 来自: u011368821
没有更多推荐了,返回首页