1、其中Retinex算法具有的功能:动态范围压缩(即滤掉了低频部分,提取了高频)、色调再现(即还有图像色彩);具有锐化、颜色恒常性、动态范围压缩大、色彩保真度高等特点。
从算法公式上的个人理解:主要特点是增加对比度、图像直方图均衡化、提高色彩饱和度。其就是多种算法一起使用的融合效果。对三个通道进行了均衡化处理色彩失真,所以引入多尺度,为了保证色彩不失真。
总结:直方图的均衡化的图像一般是灰度图,如果对色彩图的话会失真;如果模式识别中没有使用到色彩匹配之类的算法,就直接转换为灰度图然后均衡化;如果使用到了色彩匹配方法,则尽量不要进行均衡化处理,如果由于过曝、过暗需要均衡化处理,则需要使用多尺度Retinex算法来进行色彩还原。如下:
a、多尺度色彩饱和 b、单尺度失真
直方图均衡化的使用范围:如下背景不能太单调,单调表示其直方图就只有那么几条,然后被拉伸到好远,则其会导致很坏的效果。所以单调的背景不要使用直方图均衡化算法。则可以使用亮度算法,首先计算整体亮度均值,然后决定是增加还是降低亮度。如下:
其中这个算法里使用了log对数域,这个主要原因不是为了扩展低灰度值,压缩高灰度值,而是为了避免负值的灰度值,而不是为了扩展低灰度值。是因为原图像直接减去滤波后的图像,可能会出现负值,处理麻烦,如果是装换到对数域的话,虽然相减也会有负值,但是在进行反对数变换的时候就一定是正值。下面的是多尺度Retinex算法的公式,
注意:其运行时间比较长,其log变换比较耗时,所以我们可以做的是把log的256个值保存起来,在变换的时候进行查表,就会比较快。
2、对数域变换
其是把原灰度值通过对数变换到对数域的灰度值,代替原来的灰度值。这样的优点:扩展了暗区域,压缩了亮区域,但是如果本来的图像就很亮,在进行对数运算的话会导致图像越来越亮,然后失真。如下:
代码如下:
%% -------------Log Transformations-----------------
f = imread('9.jpg');
f = mat2gray(f,[0 255]);
v = 10;
g_1 = log2(1 + v*f)/log2(v+1);
v = 30;
g_2 = log2(1 + v*f)/log2(v+1);
v = 200;
g_3 = log2(1 + v*f)/log2(v+1);
figure();
subplot(1,2,1);
imshow(f,[0 1]);
xlabel('a).Original Image');
subplot(1,2,2);
imshow(g_1,[0 1]);
xlabel('b).Log Transformations v=10');
figure();
subplot(1,2,1);
imshow(g_2,[0 1]);
xlabel('c).Log Transformations v=100');
subplot(1,2,2);
imshow(g_3,[0 1]);
xlabel('d).Log Transformations v=200');
其主要特点是:降低了对比度,因为最亮值与最暗值的差值变小了,其实现的是灰度值非线性拉伸;
对比度概念:
对比度指的是一幅图像中明暗区域最亮的白和最暗的黑之间不同亮度层级的测量,即指一幅图像灰度反差的大小。
图像可视性对数增强
为了增强图像的可视信息,对图像中的像素进行基于对数的操作 公式如下:
Ld是显示亮度(我们所要求的值),Lw是真实世界的亮度(图片的当前值),Lmax是场景中的最亮值(图片的最大值)。
公式很简单 实现起来也比较容易。
这个映射保证了不管场景的动态范围是怎样的,其最大值都能映射到1 其他的值能够比较平滑的递增。其性质:
3、直方图均衡化与大津阈值计算:
其只要是把灰度值进行拉伸,其特点是增加了对比度,使图像亮度适中。其一般比对数变换更广泛,因为其不限制与图像的亮黑,而对数变换主要是针对暗图像,其实现的是灰度值线性拉伸。其实际的目的是把图像平均亮度调节到100上下。如下例子:
这是对图像亮度为198的图像进行的均衡化处理,
这是对图像亮度为248的图像进行的均衡化处理,
从这两个实验可以看到,均衡化主要是把其平均亮度调节到100附近。不过其跟亮度调节算法还是有点不同的,原理不同,亮度调节算法很难实现自动,其要自己设定限制,然后决定对图像是调亮还是调暗。
直方图均衡化注意:如果本来的目标图像本来是比较单一的背景--在单一的背景下进行目标识别,而不是说丰富的背景被蒙上了一层淡黑色-就如那个经典风景图。所以调节亮度不是每个场景都适合的。如下:
单一背景进行均衡化处理,效果差
大津算法阈值选取,进行原图二值化处理,效果好,
蒙上黑色进行直方图均衡化处理 效果好。
单一灰度进行大津算法处理的二值图
有一点很大对比度的区域进行大津算法处理的二值图
大津阈值计算,只要计算的目标与背景的对比度大,则其选取的阈值都可以对其很好的分离,由于我们的标识图案是黑白其对比度很大,则选取的阈值都可以分割他们。
4、亮度调节算法:
亮度评估理论:
Im
,输入图像平均亮度:
Im=e(1M∗N∑Mx=1∑Ny=1log(I(x,y)+1))−1
其亮度评估与亮度调节的程序如下:
#include<opencv2/opencv.hpp>
using namespace cv;
int clamp(int value)
{
return value > 255 ? 255 : (value < 0 ? 0 : value);
}
int bright_estimate(Mat img)
{
double img_b=0, img_g=0, img_r=0;
int pix_total = 0;
double mean_brightness = 0;
pix_total = img.cols*img.rows;
for (size_t i = 0; i < img.rows; i++)
{
for (size_t j = 0; j < img.cols*3; j+=3)
{
img_r += log((double)img.data[i*img.cols * 3 + j]+1);
img_g += log((double)img.data[i*img.cols * 3 + j + 1]+1);
img_b += log((double)img.data[i*img.cols * 3 + j + 2]+1);
}
}
img_b = img_b / pix_total;
img_r = img_r / pix_total;
img_g = img_g / pix_total;
img_b = exp(img_b) - 1;
img_g = exp(img_g) - 1;
img_r = exp(img_r) - 1;
mean_brightness = (double)(img_b + img_g + img_r) / 3;
printf("平均亮度是:%.2f\n",mean_brightness);
return 0;
}
int adjustbright(Mat img,int contrast,int brightness)
{
double redsum = 0, greensum = 0, bluesum = 0;
double redmean = 0, greenmean = 0, bluemean = 0;
int r_data = 0, g_data = 0, b_data = 0;
if (img.empty())
{
return -1;
}
int width = img.cols, height = img.rows, pixtotal = 0;
uchar *img_date;
img_date = img.data;
pixtotal = width*height;
for (size_t i = 0; i < img.rows; i++)
{
for (size_t j = 0; j < img.cols * 3; j += 3)
{
redsum += img_date[i*width + j];
greensum += img_date[i*width + j + 1];
bluesum += img_date[i*width + j + 2];
}
}
redmean = redsum / pixtotal;
greenmean = greensum / pixtotal;
bluemean = bluesum / pixtotal;
for (size_t i = 0; i < height; i++)
{
for (size_t j = 0; j < width * 3; j += 3)
{
r_data = img_date[i*width * 3 + j];
g_data = img_date[i*width * 3 + j + 1];
b_data = img_date[i*width * 3 + j + 2];
img_date[i*width * 3 + j] = clamp((r_data - redmean)*contrast + redmean*brightness);
img_date[i*width * 3 + j + 1] = clamp((g_data - greenmean)*contrast + greenmean*brightness);
img_date[i*width * 3 + j + 2] = clamp((b_data - bluemean)*contrast + bluemean*brightness);
}
}
}
void main()
{
Mat img;
float contrast = 2.5;
float brightness = 17.91;
VideoCapture capture(0);
//img = imread("a.jpg");
capture >> img;
while (true)
{
bright_estimate(img);
imshow("原图", img);
adjustbright(img, contrast, brightness);
bright_estimate(img);
imshow("调节后的图像", img);
waitKey(120);
capture >> img;
}
}
4、边缘增强Retinex算法与拉普拉斯算法联系
Retinex保存了图像的高频部分,即使边缘信息。而拉普拉斯也是增强边缘信息的。所以两者是有相似之处的,实际应用上是两者合着用,即先用拉普拉斯来边缘增强,然后再使用Retinex算法。其中拉普拉斯代码如下:
/*
Author : Ggicci
Date : 2012.07.19
File : sharp.h
*/
#pragma once#include <opencv\cv.h>
using namespace cv;namespace ggicci
{void sharpen(const Mat& img, Mat& result);}/*
Author : Ggicci
Date : 2012.07.19
File : sharp.cpp
*/
#include "sharp.h"void ggicci::sharpen(const Mat& img, Mat& result){result.create(img.size(), img.type());//处理边界内部的像素点, 图像最外围的像素点应该额外处理
for (int row = 1; row < img.rows-1; row++){//前一行像素点
const uchar* previous = img.ptr<const uchar>(row-1);//待处理的当前行
const uchar* current = img.ptr<const uchar>(row);//下一行
const uchar* next = img.ptr<const uchar>(row+1);uchar *output = result.ptr<uchar>(row);int ch = img.channels();
int starts = ch;
int ends = (img.cols - 1) * ch;
for (int col = starts; col < ends; col++){//输出图像的遍历指针与当前行的指针同步递增, 以每行的每一个像素点的每一个通道值为一个递增量, 因为要考虑到图像的通道数
*output++ = saturate_cast<uchar>(5 * current[col] - current[col-ch] - current[col+ch] - previous[col] - next[col]);}} //end loop
//处理边界, 外围像素点设为 0
result.row(0).setTo(Scalar::all(0));result.row(result.rows-1).setTo(Scalar::all(0));result.col(0).setTo(Scalar::all(0));result.col(result.cols-1).setTo(Scalar::all(0));}/*
Author : Ggicci
Date : 2012.07.19
File : main.cpp
*/
#include <opencv\highgui.h>
#pragma comment(lib, "opencv_core231d.lib")#pragma comment(lib, "opencv_highgui231d.lib")#pragma comment(lib, "opencv_imgproc231d.lib")using namespace cv;#include "sharp.h"int main()
{Mat lena = imread("lena.jpg");
Mat sharpenedLena;ggicci::sharpen(lena, sharpenedLena);imshow("lena", lena);
imshow("sharpened lena", sharpenedLena);
cvWaitKey();return 0;
}