自动曝光修复算法附完整C代码

众所周知,


图像方面的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');


复制代码


算法步骤很清晰,就不展开了。


有兴趣的同学,品读下论文吧。


论文链接直达


这个算法其实只是简单采用局部信息进行曝光调节,


但是并不能很好的适配很多图片情景。


需要进行二次改造,


例如: 白平衡,纹理处理更加自然诸如此类,之后就能更加美美哒。


师傅领进门,修行在个人。


改进的思路和方法就不展开一一细说了,


有兴趣的同学,可以考虑进一步改进。


效果图如下:自动曝光修复算法附完整C代码_第1张图片自动曝光修复算法附完整C代码_第2张图片

主要的算法函数实现如下:


复制代码


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


#include


#include


#include "timing.h"


#include


#include


#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. ");


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 = '';


}


} else if (drv)


*drv = '';


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 = '';


}


if (dir) {


for (s = path; s < p;)


*dir++ = *s++;


*dir = '';


}


}


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


printf("blog:http://cpuimage.cnblogs.com/ ");


if (argc < 2) {


printf("usage: %s image ", argv[0]);


printf("eg: %s d:\image.jpg ", 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. ", (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! ", szfile);


}


startTime = now();


LocalColorCorrection(inputImage, outputImg, Width, Height, Channels);


double nProcessTime = calcElapsed(startTime, now());


printf("process time: %d ms. ", (int) (nProcessTime * 1000));


startTime = now();


saveImage("done.jpg", Width, Height, Channels, outputImg);


double nSaveTime = calcElapsed(startTime, now());


printf("save time: %d ms. ", (int) (nSaveTime * 1000));


if (outputImg) {


stbi_image_free(outputImg);


}


if (inputImage) {


stbi_image_free(inputImage);


}


} else {


printf("load: %s fail! ", szfile);


}


getchar();


printf("press any key to exit. ");


return (EXIT_SUCCESS);


}


最后你觉得我们的文章对你有帮助,欢迎关注我,可以私信我:久伴,领取学习资料,在评论下方可以关注我的学习群,你可以随时在上面向我们提问,把你在学习C++过程中所遇到的问题发给我们。我们每天都会按时回复大家的每一个问题,希望久伴可以伴随你从入门到专家。

你可能感兴趣的:(自动曝光修复算法附完整C代码)