图像处理_图像增强

图像增强前期知识

图像增强是图像模式识别中非常重要的图像预处理过程。
图像增强的目的是通过对图像中的信息进行处理,使得有利于模式识别的信息得到增强,不利于模式识别的信息被抑制,扩大图像中不同物体特征之间的差别,为图像的信息提取及其识别奠定良好的基础。图像增强按实现方法不同可分为点增强、空域增强和频域增强。

1、点增强
点增强主要指图像灰度变换和几何变换。图像的灰度变换也称为点运算、对比度增强或对比度拉伸,它是图像数字化软件和图像显示软件的重要组成部分。灰度变换是一种既简单又重要的技术,它能让用户改变图像数据占据的灰度范围。一幅输入图像经过灰度变换后将产生一幅新的输出图像,由输入像素点的灰度值决定相应的输出像素点的灰度值。灰度变换不会改变图像内的空间关系。图像的几何变换是图像处理中的另一种基本变换。它通常包括图像的平移、图像的镜像变换、图像的缩放和图像的旋转。通过图像的几何变换可以实现图像的最基本的坐标变换及缩放功能。

2、空域增强
图像的空间信息可以反映图像中物体的位置 、形状、大小等特征,而这些特征可以通过一定的物理模式来描述。例如,物体的边缘轮廓由于灰度值变化剧烈一般出现高频率特征,而一个比较平滑的物体内部由于灰度值比较均一则呈现低频率特征。因此,根据需要可以分别增强图像的高频和低频特征。对图像的高频增强可以突出物体的边缘轮廓,从而起到锐化图像的作用。例如,对于人脸的比对查询,就需要通过高频增强技术来突出五宫的轮廓。相应地,对图像的低频部分进行增强可以对图像进行平滑处理,一般用于图像的噪声消除。

3、频域增强
图像的空域增强一般只是对数字图像进行局部增强,而图像的频域增强可以对图像进行全局增强。频域增强技术是在数字图像的频率域空间对图像进行滤波,因此需要将图像从空间域变换到频率域,一般通过傅里叶变换实现。在频率域空间的滤波与空域滤波一样可以通过卷积实现,因此傅里叶变换和和卷积理论是频域滤波技术的基础。

图像增强的方法分类:

|图像增强方法|实现方法|
|-|-|
|处理对象|灰度图|
||(伪)彩色图|
|-|-|
|处理策略|全局处理|
||局部处理(ROI ROI,Region of Interest Interest)|
|-|-|
|处理方法|空间域(点域运算,即灰度变换)|
||空间域(邻域方法,即空域滤波)|
||频域方法|
|-|-|
|处理目的|图像锐化|
||平滑去噪|
||灰度调整(对比度增强)|

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

|图像增强方法|实现方法|
|-|-|
|灰度变换法|线性变换(已实现)|
||对数变换(已实现)|
||指数变换(已实现)|
|-|-|
|直方图调整法|直方图均衡化(已实现)|
||直方图匹配(未实现)|

图像对比度增强

||||
|-|-|-|
|图像对比度增强|直接||
||间接|直方图拉伸|
|||直方图均衡化|
||||

直方图拉伸
    是通过对比度拉伸对直方图进行调整,从而“扩大”前景和背景灰度的差别,以达到增强对比度的目的,这种方法可以利用线性或非线性的方法来实现;直方图均衡化则通过使用累积函数对灰度值进行“调整”以实现对比度的增强。
直方图均衡化处理
    “中心思想”是把原始图像的灰度直方图从比较集中的某个灰度区间变成在全部灰度范围内的均匀分布。直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致相同。直方图均衡化就是把给定图像的直方图分布改变成“均匀”分布直方图分布。

常用图像增强

直方图均衡化

|直方图均衡化||
|-|-|
|优点|处理过亮过暗图像很有效(曝光过度或者曝光不足),刻画更多细节|
||是一个相当直观的技术并且是可逆操作,如果已知均衡化函数,那么就可以恢复原始的直方图,并且计算量也不大|
|-|-|
|缺点|处理数据随机,可能会降低信噪比(会增加背景噪声对比度,降低有用信号对比度)|

c语言代码:

#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;
}

指数变换

先做归一化,再指数变换,最后反归一化

$$S=c*R^r$$
通过合理的选择c和r可以压缩灰度范围,算法以c=1.0/255.0, r=2实现

Opencv代码:

void ExpEnhance(IplImage* img, IplImage* dst)
{
    // 由于oldPixel:[1,256],则可以先保存一个查找表
    uchar lut[256] ={0};
 
    double temp = 1.0/255.0;
 
    for ( int i =0; i<255; i++)
    {
        lut[i] = (uchar)(temp*i*i+0.5);
    }
 
    for( int row =0; row <img->height; row++)
    {
        uchar *data = (uchar*)img->imageData+ row* img->widthStep;
        uchar *dstData = (uchar*)dst->imageData+ row* dst->widthStep;
 
        for ( int col = 0; col<img->width; col++)
        {
            for( int k=0; k<img->nChannels; k++)
            {
                uchar t1 = data[col*img->nChannels+k];                
                dstData[col*img->nChannels+k] = lut[t1];
            }
        }        
    }    
}

对数变换

低灰度值部分扩展,高灰度值部分压缩,来强调图像低灰度部分

$$s=c*log_{v+1}(1+v*r)$$

底数为(v+1),实际输入范围为归一化的【0-1】,其输出也为【0-1】。底数越大,对低灰度部分的强调就越强,对高灰度部分的压缩也就越强

matlab代码

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);

灰度拉伸

灰度拉升可以改善图像的动态范围

$$s=\frac{1}{1+\frac{m}{r+eps}^E}$$
输入r为【0-1】,其输出s也为【0-1】

线性拉伸

三段线性变换

突出感兴趣的目标或者灰度区间,相对抑制那些不感兴趣的灰度区域

范围吧
%横轴
fa=20; fb=80;
%纵轴
ga=50; gb=230;

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); 

频率域图像增强

傅里叶变换提供了一种从空间域到频域的转换手段,且用傅里叶反变换可以实现从频域到空间域的无损转换,不丢失任何信息

|频域图像增强|类型|
|-|-|
|高通滤波器|突出图像的边界|
|低通滤波器|抑制图像噪声,改善图像质量|

分析频谱图

clc;  %清空命令行
clear;%清空变量
 
I1=imread('beauty.jpg');
subplot(1,2,1);
imshow(I1);
title('beauty.jpg');
 
I2=fft2(I1);%计算二维FFT
spectrum =fftshift(I2);%将零点移到中心
temp= log(1+ abs(spectrum) ); %对幅值做 对数变换 以压缩动态范围
subplot(1,2,2);
imshow(temp,[]);
title('FFT');

低频分量:主要对整副图像的强度的综合度量.灰度变化缓慢的特性

高频分量:主要是对图像边缘和轮廓的度量.灰度变化快的特性

幅度图,看图像的频率分布,哪里亮那里暗,低频一般在图像中央
如果只保留图像的中心点,则图像的细节会丢失,大致轮廓还在,不同区域好友不同的灰度
如果保留远离中心的点,而去掉中心的幅度,则保留着图像的细节,而不同区域的灰度一样

频域低通滤波

理想低通滤波器

理想低通滤波器并不能很好的兼顾 滤除噪声 与 保留细节 这两个方面

理想低通滤波器:

% 频域低通滤波器 imidealflpf.m
%{
函数:    function ff=imidealflpf(I,freq)
函数说明:构造理想的频域低通滤波器(即 滤镜)
参数说明:I:为输入原图像
         freq:为截止频率
返回值: 与I等大的频域滤镜
 %}
 
 function ff=imidealflpf(I,freq)
 
 [M,N]=size(I);
 ff=ones(M,N);
 for i=1:M
     for j=1:N
         if (sqrt ((i-M/2)^2+ (j-N/2)^2 ) >freq)  ff(i,j)=0; %高于截止频率 设为0
         end
     end
 end

不同截止频率的滤波结果:

高斯低通滤波器

%高斯低通滤波器滤镜 imgaussflpf.m
%{
函数: function ff=imgaussflpf(I,sigma)
函数说明:构造高斯低通滤镜
参数说明:I:输入图像
         sigma:标准差
返回值:与原图像等大的高斯低通滤镜
 
%}
  
function ff=imgaussflpf(I,sigma)
[M,N]=size(I);
 ff=ones(M,N);
 for i=1:M
     for j=1:N
         ff(i,j)= exp( -((i-M/2)^2+(j-N/2)^2) /2/(sigma^2) ); %高斯函数
        
     end
 end

高斯滤波结果:

高斯相比于低通滤波,在有效抑制噪声的同时,图像的模糊程度更低

cankao

频域高通滤波器

图像锐化可以通过衰减频域中的低频信号来实现

%高斯高通滤波器滤镜 imgaussfhpf.m
%{
函数: function ff=imgaussfhpf(I,sigma)
函数说明:构造高斯高通滤镜
参数说明:I:输入图像
         sigma:标准差
返回值:与原图像等大的高斯高通滤镜
 
%}
 
 
function ff=imgaussfhpf(I,sigma)
[M,N]=size(I);
 ff=ones(M,N);
 for i=1:M
     for j=1:N
         ff(i,j)= 1-exp( -((i-M/2)^2+(j-N/2)^2) /2/(sigma^2) ); %  1-(gauss)
        
     end
 end

1、高斯高通滤波器可以较好的提取边缘信息;

2、sigma越小,Gauss高通的截止频率越低,通过的低频成分越多,边缘提取越不精确,会包含更多的非边缘信息;(要求太低,多了浑水摸鱼者)
3、sigma越大,边缘提取越精确,但可能包含不完整的边缘信息。(要求太高,有了漏网之鱼)

拉普拉斯滤波器

%laplace滤波器滤镜 imlapf.m
%{
函数: function ff=imlapf(I)
函数说明:构造laplace滤镜
参数说明:I:输入图像
       
返回值:与原图像等大的laplace滤镜
 
%}
 
 
function ff=imlapf(I)
[M,N]=size(I);
 ff=ones(M,N);
 for i=1:M
     for j=1:N
         ff(i,j)= -((i-M/2)^2+(j-N/2)^2)  % 
        
     end
 end

图像处理评价指标

基于误差灵敏度评价算法:

最简单的质量评价算法就是均方差(Mean Squared Error, MSE)和峰值信噪比(Peak Signal- Noise Ratio, PSNR)。MSE 和 PSNR 计算复杂度小,易于实现,在图像处理领域中广泛应用。但缺点是它们给出的数值与图像的感知质量之间没有必然联系。

峰值信噪比-PSNR

图像压缩等领域信号重建质量的评价

MSE为当前图像 X 和参考图像 Y 的均方误差,H、W 分别表示图像的高和宽;n为每像素的比特数,一般取8,即像素灰阶数为256。PSNR的单位是dB,数值越大表示失真越小。

matlab代码:

    function pnsr_result = psnr(img_ref,img_in)       
        %   img_ref is a high reference quality image   
        %   img_in is the denoise image    
        %   pnsr_result is the PSNR of the denoise image    
        width = size(img_ref,2);    
        heigh = size(img_ref,1);    
        if( width ~= size(img_in,2) || heigh ~= size(img_in,1) )    
            disp('Please check whether the input image and reference image have same size');    
            return    
        end    
        [a,b]=size(img_ref);      
        XX=double(img_ref) - double(img_in);      
        mse_value = sum(sum( XX.^2 ))/(a*b);      
        pnsr_result = 10*log10( 255*255 / mse_value );   
    end

SSIM

信噪比(SNR)

信噪比就是有用信号与噪声信号的比值
$$snr=10*log_{10}\frac{sigma(I2)}{sigma(I2-I1)}$$

function snr=SNR2(I,In)
% 计算噪声比
% I :original signal
% In:noisy signal
% snr=10*log10(sigma2(I2)/sigma2(I2-I1))
 
[~,~,nchannel]=size(I);
snr=0;
I=double(I);
In=double(In);
if nchannel==1
    Ps=sum(sum((I-mean(mean(I))).^2));%signal power
    Pn=sum(sum((I-In).^2));%noise power
    snr=10*log10(Ps/Pn);
elseif nchannel==3
    for i=1:3
        Ps=sum(sum((I(:,:,i)-mean(mean(I(:,:,i)))).^2));%signal power
        Pn=sum(sum((I(:,:,i)-In(:,:,i)).^2));%noise power
        snr=snr+10*log10(Ps/Pn);
    end
    snr=snr/3;
end

基于结构相似度评价算法

其他

曝光过度问题处理

计算当前图像的反相(255-image),然后取当前图像和反相图像的较小者为当前像素位置的值。

$$min(image,(255-image))$$

加Masaic算法

原理:用中心像素来表示邻域像素
Opencv代码:

uchar getPixel( IplImage* img, int row, int col, int k)
{
    return ((uchar*)img->imageData + row* img->widthStep)[col*img->nChannels +k];
}
 
void setPixel( IplImage* img, int row, int col, int k, uchar val)
{
    ((uchar*)img->imageData + row* img->widthStep)[col*img->nChannels +k] = val;
}

--

// nSize:为尺寸大小,奇数
// 将邻域的值用中心像素的值替换
void Masic(IplImage* img, IplImage* dst, int nSize)
{
    int offset = (nSize-1)/2;
    for ( int row = offset; row <img->height - offset; row= row+offset)
    {
        for( int col= offset; col<img->width - offset; col = col+offset)
        {
            int val0 = getPixel(img, row, col, 0);
            int val1 = getPixel(img, row, col, 1);
            int val2 = getPixel(img, row, col, 2);
            for ( int m= -offset; m<offset; m++)
            {
                for ( int n=-offset; n<offset; n++)
                {
                    setPixel(dst, row+m, col+n, 0, val0);
                    setPixel(dst, row+m, col+n, 1, val1);
                    setPixel(dst, row+m, col+n, 2, val2);
                }
            }
        }
    }
}
最后修改:2020 年 07 月 07 日 08 : 41 PM
如果觉得我的文章对你有用,请随意赞赏