自动曝光修复算法 附完整C代码
2018-06-02 15:55
567 查看
众所周知,
图像方面的3A算法有:
AF自动对焦(Automatic Focus)
自动对焦即调节摄像头焦距自动得到清晰的图像的过程
AE自动曝光(Automatic Exposure)
自动曝光的是为了使感光器件获得合适的曝光量
AW自动白平衡(Automatic White Balance)
白平衡的本质是使白色物体在任何光源下都显示白色
前面的文章也有提及过,在刚开始做图像算法的时候,我是先攻克的自动白平衡算法。
后来攻克自动曝光的时候,傻啦吧唧的,踩了不少坑。
我相信一定不止我一个,一开始的时候抱着对图像均衡化,
软磨硬泡,想要做出兼顾自动曝光和自动白平衡的算法。
可惜,图像均衡化去做白平衡或者自动曝光,这条路是错的。
严格意义上来说,图像均衡化是拉伸曲线,这种做法有个弊端。
它没有考虑到图像的空间信息,也就是局部信息。
当然如果是处理音频之类的算法,肯定要考虑时间信息,因为数据是时序性为主的。
而图像,明显是空间信息为主的。
所以从理论上来说,用拉伸曲线这种不具备空间信息的操作,来做空间信息处理的事情,是不科学的。
我记得这博客刚开始写的时候,好多网友问我,为什么你要写那么多图像模糊算法,
图像模糊算法好像很鸡肋啊,没什么用的吧。
这就大错特错了,因为模糊算法是图像算法中,典型的包含空间信息的全局算法。
也就是说,如果要玩好图像算法,玩好模糊算法就是标配。
本次分享的算法为《Local Color Correction using Non-Linear Masking》,是ImageShop博主,
彭兄发出来的,安利一下他的博客https://www.cnblogs.com/imageshop 。
这个文章里的算法比较简单,
主要是通过图像模糊获取局域权重信息,然后映射回图片上。
matlab代码如下:
算法步骤很清晰,就不展开了。
有兴趣的同学,品读下论文吧。
论文链接直达
这个算法其实只是简单采用局部信息进行曝光调节,
但是并不能很好的适配很多图片情景。
需要进行二次改造,
例如: 白平衡,纹理处理更加自然诸如此类,之后就能更加美美哒。
师傅领进门,修行在个人。
改进的思路和方法就不展开一一细说了,
有兴趣的同学,可以考虑进一步改进。
效果图如下:
主要的算法函数实现如下:
做了一些算法性能上的优化,720P,1080P下实时没半点问题。
至于进一步优化性能和效果,就留待下回分解,
当然有没有下回,得看心情。
附完整C代码:
项目地址:https://github.com/cpuimage/LocalColorCorrection
再来一个效果前后对比:
以上,权当抛砖引玉。
若有其他相关问题或者需求也可以邮件联系俺探讨。
邮箱地址是:
gaozhihan@vip.qq.com
图像方面的3A算法有:
AF自动对焦(Automatic Focus)
自动对焦即调节摄像头焦距自动得到清晰的图像的过程
AE自动曝光(Automatic Exposure)
自动曝光的是为了使感光器件获得合适的曝光量
AW自动白平衡(Automatic White Balance)
白平衡的本质是使白色物体在任何光源下都显示白色
前面的文章也有提及过,在刚开始做图像算法的时候,我是先攻克的自动白平衡算法。
后来攻克自动曝光的时候,傻啦吧唧的,踩了不少坑。
我相信一定不止我一个,一开始的时候抱着对图像均衡化,
软磨硬泡,想要做出兼顾自动曝光和自动白平衡的算法。
可惜,图像均衡化去做白平衡或者自动曝光,这条路是错的。
严格意义上来说,图像均衡化是拉伸曲线,这种做法有个弊端。
它没有考虑到图像的空间信息,也就是局部信息。
当然如果是处理音频之类的算法,肯定要考虑时间信息,因为数据是时序性为主的。
而图像,明显是空间信息为主的。
所以从理论上来说,用拉伸曲线这种不具备空间信息的操作,来做空间信息处理的事情,是不科学的。
我记得这博客刚开始写的时候,好多网友问我,为什么你要写那么多图像模糊算法,
图像模糊算法好像很鸡肋啊,没什么用的吧。
这就大错特错了,因为模糊算法是图像算法中,典型的包含空间信息的全局算法。
也就是说,如果要玩好图像算法,玩好模糊算法就是标配。
本次分享的算法为《Local Color Correction using Non-Linear Masking》,是ImageShop博主,
彭兄发出来的,安利一下他的博客https://www.cnblogs.com/imageshop 。
这个文章里的算法比较简单,
主要是通过图像模糊获取局域权重信息,然后映射回图片上。
matlab代码如下:
% Read the image A=imread('input.jpg'); % Seperate the Channels R=A(:,:,1); G=A(:,:,2); B=A(:,:,3); % Calculate Intensity Component I=(R+G+B)/3; % Invert the image I_inverted=255-I; % Apply Average Filter to obtain the Mask Image h_average=fspecial('average',15); M=imfilter(I_inverted,h_average); % Color Correction for R channel R_new=zeros(size(R)); [c_y, c_x,~] = size(R); for j = 1:c_x for i = 1:c_y p=double(R(i,j)); q=double(M(i,j)); R_new(i,j,:)=int8(255*((p/255)^(2^((128-q)/128)))); end end % Color Correction for G channel G_new=zeros(size(G)); [c_y, c_x,~] = size(G); for j = 1:c_x for i = 1:c_y p=double(G(i,j)); q=double(M(i,j)); G_new(i,j,:)=int8(255*((p/255)^(2^((128-q)/128)))); end end % Color Correction for B channel B_new=zeros(size(B)); [c_y, c_x,~] = size(B); for j = 1:c_x for i = 1:c_y p=double(B(i,j)); q=double(M(i,j)); B_new(i,j,:)=int8(255*((p/255)^(2^((128-q)/128)))); end end % Output Image O=zeros(size(A)); O(:,:,1)=R_new; O(:,:,2)=G_new; O(:,:,3)=B_new; % Convert the double output image to uint8 O=uint8(O); % Plot the images subplot(1,3,1), imshow(A), title('Original Image'); subplot(1,3,2), imshow(M), title('Mask'); subplot(1,3,3), imshow(O), title('Output Image');
算法步骤很清晰,就不展开了。
有兴趣的同学,品读下论文吧。
论文链接直达
这个算法其实只是简单采用局部信息进行曝光调节,
但是并不能很好的适配很多图片情景。
需要进行二次改造,
例如: 白平衡,纹理处理更加自然诸如此类,之后就能更加美美哒。
师傅领进门,修行在个人。
改进的思路和方法就不展开一一细说了,
有兴趣的同学,可以考虑进一步改进。
效果图如下:
主要的算法函数实现如下:
void LocalColorCorrection(unsigned char *Input, unsigned char *Output, int Width, int Height, int Channels) { unsigned char *Mask = (unsigned char *) malloc(Width * Height * sizeof(unsigned char)); if (Mask == NULL) return; unsigned char LocalLut[256 * 256]; for (int mask = 0; mask < 256; ++mask) { unsigned char *pLocalLut = LocalLut + (mask << 8); for (int pix = 0; pix < 256; ++pix) { pLocalLut[pix] = ClampToByte(255.0f * powf(pix / 255.0f, powf(2.0f, (128.0f - mask) / 128.0f))); } } InvertGrayscale(Input, Output, Width, Height, Channels); int Radius = (MAX(Width, Height) / 512) + 1; BoxBlurGrayscale(Output, Mask, Width, Height, Radius); for (int Y = 0; Y < Height; Y++) { unsigned char *pOutput = Output + (Y * Width * Channels); unsigned char *pInput = Input + (Y * Width * Channels); unsigned char *pMask = Mask + (Y * Width); for (int X = 0; X < Width; X++) { unsigned char *pLocalLut = LocalLut + (pMask[X] << 8); for (int C = 0; C < Channels; C++) { pOutput[C] = pLocalLut[pInput[C]]; } pOutput += Channels; pInput += Channels; } } free(Mask); }
做了一些算法性能上的优化,720P,1080P下实时没半点问题。
至于进一步优化性能和效果,就留待下回分解,
当然有没有下回,得看心情。
附完整C代码:
/**
*implmentation of Local Color Correction using Non-Linear Masking published by Nathan Moroney Hewlett-Packard Laboratories, Palo Alto, California.
**/
#include "browse.h"
#define USE_SHELL_OPEN
#define STB_IMAGE_STATIC
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
/* ref:https://github.com/nothings/stb/blob/master/stb_image.h */
#define TJE_IMPLEMENTATION
#include "tiny_jpeg.h"
/* ref:https://github.com/serge-rgb/TinyJPEG/blob/master/tiny_jpeg.h */
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include "timing.h"
#include <stdint.h>
#include <assert.h>
#ifndef _MAX_DRIVE
#define _MAX_DRIVE 3
#endif
#ifndef _MAX_FNAME
#define _MAX_FNAME 256
#endif
#ifndef _MAX_EXT
#define _MAX_EXT 256
#endif
#ifndef _MAX_DIR
#define _MAX_DIR 256
#endif
#ifndef MIN
#define MIN(a, b) ( (a) > (b) ? (b) : (a) )
#endif
#ifndef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
char saveFile[1024];
unsigned char *loadImage(const char *filename, int *Width, int *Height, int *Channels) {
return (stbi_load(filename, Width, Height, Channels, 0));
}
void saveImage(const char *filename, int Width, int Height, int Channels, unsigned char *Output) {
memcpy(saveFile + strlen(saveFile), filename, strlen(filename));
*(saveFile + strlen(saveFile) + 1) = 0;
if (!tje_encode_to_file(saveFile, Width, Height, Channels, true, Output)) {
fprintf(stderr, "save JPEG fail.\n");
return;
}
#ifdef USE_SHELL_OPEN
browse(saveFile);
#endif
}
void splitpath(const char *path, char *drv, char *dir, char *name, char *ext) {
const char *end;
const char *p;
const char *s;
if (path[0] && path[1] == ':') {
if (drv) {
*drv++ = *path++;
*drv++ = *path++;
*drv = '\0';
}
} else if (drv)
*drv = '\0';
for (end = path; *end && *end != ':';)
end++;
for (p = end; p > path && *--p != '\\' && *p != '/';)
if (*p == '.') {
end = p;
break;
}
if (ext)
for (s = end; (*ext = *s++);)
ext++;
for (p = end; p > path;)
if (*--p == '\\' || *p == '/') {
p++;
break;
}
if (name) {
for (s = p; s < end;)
*name++ = *s++;
*name = '\0';
}
if (dir) {
for (s = path; s < p;)
*dir++ = *s++;
*dir = '\0';
}
}
void getCurrentFilePath(const char *filePath, char *saveFile) {
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
splitpath(filePath, drive, dir, fname, ext);
size_t n = strlen(filePath);
memcpy(saveFile, filePath, n);
char *cur_saveFile = saveFile + (n - strlen(ext));
cur_saveFile[0] = '_';
cur_saveFile[1] = 0;
}
int GetMirrorPos(int Length, int Pos) {
if (Pos < 0)
return -Pos;
else if (Pos >= Length)
return Length + Length - Pos - 2;
else
return Pos;
}
unsigned char ClampToByte(int Value) {
if (Value < 0)
return 0;
else if (Value > 255)
return 255;
else
return (unsigned char) Value;
}
void FillLeftAndRight_Mirror(int *Array, int Length, int Radius) {
for (int X = 0; X < Radius; X++) {
Array[X] = Array[Radius + Radius - X];
Array[Radius + Length + X] = Array[Radius + Length - X - 2];
}
}
int SumOfArray(const int *Array, int Length) {
int Sum = 0;
for (int X = 0; X < Length; X++) {
Sum += Array[X];
}
return Sum;
}
void BoxBlurGrayscale(unsigned char *input, unsigned char *output, int Width, int Height, int Radius) {
if ((input == NULL) || (output == NULL)) return;
if ((Width <= 0) || (Height <= 0) || (Radius <= 0)) return;
if (Radius < 1) return;
Radius = MIN(MIN(Radius, Width - 1), Height - 1);
int SampleAmount = (2 * Radius + 1) * (2 * Radius + 1);
float Inv = 1.0f / SampleAmount;
int *ColValue = (int *) malloc((Width + Radius + Radius) * sizeof(int));
int *ColOffset = (int *) malloc((Height + Radius + Radius) * sizeof(int));
if ((ColValue == NULL) || (ColOffset == NULL)) {
if (ColValue != NULL) free(ColValue);
if (ColOffset != NULL) free(ColOffset);
return;
}
for (int Y = 0; Y < Height + Radius + Radius; Y++)
ColOffset[Y] = GetMirrorPos(Height, Y - Radius);
{
for (int Y = 0; Y < Height; Y++) {
unsigned char *scanLineOut = output + Y * Width;
if (Y == 0) {
memset(ColValue + Radius, 0, Width * sizeof(int));
for (int Z = -Radius; Z <= Radius; Z++) {
unsigned char *scanLineIn = input + ColOffset[Z + Radius] * Width;
for (int X = 0; X < Width; X++) {
ColValue[X + Radius] += scanLineIn[X];
}
}
} else {
unsigned char *RowMoveOut = input + ColOffset[Y - 1] * Width;
unsigned char *RowMoveIn = input + ColOffset[Y + Radius + Radius] * Width;
for (int X = 0; X < Width; X++) {
ColValue[X + Radius] -=
RowMoveOut[X] - RowMoveIn[X];
}
}
FillLeftAndRight_Mirror(ColValue, Width, Radius);
int LastSum = SumOfArray(ColValue, Radius * 2 + 1);
scanLineOut[0] = ClampToByte((int) (LastSum * Inv));
for (int X = 0 + 1; X < Width; X++) {
int NewSum = LastSum - ColValue[X - 1] + ColValue[X + Radius + Radius];
scanLineOut[X] = ClampToByte((int) (NewSum * Inv));
LastSum = NewSum;
}
}
}
free(ColValue);
free(ColOffset);
}
void InvertGrayscale(unsigned char *Input, unsigned char *Output, int Width, int Height, int Channels) {
if (Channels == 1) {
for (unsigned int Y = 0; Y < Height; Y++) {
unsigned char *pOutput = Output + (Y * Width);
unsigned char *pInput = Input + (Y * Width);
for (unsigned int X = 0; X < Width; X++) {
pOutput[X] = (unsigned char) (255 - pInput[X]);
}
}
} else {
for (unsigned int Y = 0; Y < Height; Y++) {
unsigned char *pOutput = Output + (Y * Width);
unsigned char *pInput = Input + (Y * Width * Channels);
for (unsigned int X = 0; X < Width; X++) {
pOutput[X] = (unsigned char) (255 - ClampToByte(
(21842 * pInput[0] + 21842 * pInput[1] + 21842 * pInput[2]) >> 16));
pInput += Channels;
}
}
}
}
void LocalColorCorrection(unsigned char *Input, unsigned char *Output, int Width, int Height, int Channels) { unsigned char *Mask = (unsigned char *) malloc(Width * Height * sizeof(unsigned char)); if (Mask == NULL) return; unsigned char LocalLut[256 * 256]; for (int mask = 0; mask < 256; ++mask) { unsigned char *pLocalLut = LocalLut + (mask << 8); for (int pix = 0; pix < 256; ++pix) { pLocalLut[pix] = ClampToByte(255.0f * powf(pix / 255.0f, powf(2.0f, (128.0f - mask) / 128.0f))); } } InvertGrayscale(Input, Output, Width, Height, Channels); int Radius = (MAX(Width, Height) / 512) + 1; BoxBlurGrayscale(Output, Mask, Width, Height, Radius); for (int Y = 0; Y < Height; Y++) { unsigned char *pOutput = Output + (Y * Width * Channels); unsigned char *pInput = Input + (Y * Width * Channels); unsigned char *pMask = Mask + (Y * Width); for (int X = 0; X < Width; X++) { unsigned char *pLocalLut = LocalLut + (pMask[X] << 8); for (int C = 0; C < Channels; C++) { pOutput[C] = pLocalLut[pInput[C]]; } pOutput += Channels; pInput += Channels; } } free(Mask); }
int main(int argc, char **argv) {
printf("Local Color Correction demo\n ");
printf("blog:http://cpuimage.cnblogs.com/ \n ");
if (argc < 2) {
printf("usage: %s image \n ", argv[0]);
printf("eg: %s d:\\image.jpg \n ", argv[0]);
return (0);
}
char *szfile = argv[1];
getCurrentFilePath(szfile, saveFile);
int Width = 0;
int Height = 0;
int Channels = 0;
unsigned char *inputImage = NULL;
double startTime = now();
inputImage = loadImage(szfile, &Width, &Height, &Channels);
double nLoadTime = calcElapsed(startTime, now());
printf("load time: %d ms.\n ", (int) (nLoadTime * 1000));
if ((Channels != 0) && (Width != 0) && (Height != 0)) {
unsigned char *outputImg = (unsigned char *) stbi__malloc(Width * Channels * Height * sizeof(unsigned char));
if (inputImage) {
memcpy(outputImg, inputImage, (size_t) (Width * Channels * Height));
} else {
printf("load: %s fail!\n ", szfile);
}
startTime = now();
LocalColorCorrection(inputImage, outputImg, Width, Height, Channels);
double nProcessTime = calcElapsed(startTime, now());
printf("process time: %d ms.\n ", (int) (nProcessTime * 1000));
startTime = now();
saveImage("done.jpg", Width, Height, Channels, outputImg);
double nSaveTime = calcElapsed(startTime, now());
printf("save time: %d ms.\n ", (int) (nSaveTime * 1000));
if (outputImg) {
stbi_image_free(outputImg);
}
if (inputImage) {
stbi_image_free(inputImage);
}
} else {
printf("load: %s fail!\n", szfile);
}
getchar();
printf("press any key to exit. \n");
return (EXIT_SUCCESS);
}
项目地址:https://github.com/cpuimage/LocalColorCorrection
再来一个效果前后对比:
以上,权当抛砖引玉。
若有其他相关问题或者需求也可以邮件联系俺探讨。
邮箱地址是:
gaozhihan@vip.qq.com
相关文章推荐
- 自动红眼移除算法 附c++完整代码
- 音频自动增益 与 静音检测 算法 附完整C代码
- 3D Lut 电影级调色算法 附完整C代码
- salt-minion自动修复代码,配合salt-minion监控使用
- 手把手实战:利用LM神经网络算法自动识别窃电用户(附代码)
- linux中用C语言实现的自动在文件末尾不断添加记录的完整代码
- 图像细化算法 opencv实现 完整代码(附实验结果)
- Zend Studio代码不能自动提示的解决修复方法
- 简单的算法题,包括1.打印100——200之间的的素数2.输出乘法口诀表3.判断1000年——2000年之间的闰年,给出完整代码
- 基于RNN的音频降噪算法 (附完整C代码)
- 15道使用频率极高的基础算法题(完整代码实现)
- [置顶]群蚁算法理论与实践全攻略——旅行商等路径优化问题的新方法【附C#群蚁算法完整项目代码】
- 二分图最大匹配的匈牙利算法完整代码
- Android 一个相对完整的自动升级功能实现代码
- 音频算法之小黄人变声 附完整C代码
- WebRTC 音频采样算法 附完整C++示例代码
- WebRTC 音频算法 附完整C代码
- Python用户推荐系统曼哈顿算法实现完整代码
- c语言同名标靶点自动匹配算法实现实例代码
- 图片文档倾斜矫正算法 附完整c代码