众所周知,

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

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代码相关推荐

  1. java 对音频文件降噪_(转)音频降噪算法 附完整C代码

    转:https://www.cnblogs.com/cpuimage/p/8905965.html 降噪是音频图像算法中的必不可少的. 目的肯定是让图片或语音 更加自然平滑,简而言之,美化. 图像算法 ...

  2. 音频降噪算法 附完整C代码

    本文转载自博客:https://cloud.tencent.com/developer/article/1117226 降噪是音频图像算法中的必不可少的. 目的肯定是让图片或语音 更加自然平滑,简而言 ...

  3. 果蝇(FOA)优化算法(附完整Matlab代码,可直接复制)

    果蝇优化算法的核心是利用果蝇搜索实物的机制来对问题进行寻优.果蝇根据气味来确定食物的位置,食物腐烂程度越高,气味越大,果蝇对其越敏感.果蝇的觅食行为如下图: Fig1. 果蝇觅食行为示意图 1.果蝇位 ...

  4. wav文件降噪c语言,音频降噪算法 附完整C代码

    降噪是音频图像算法中的必不可少的. 目的肯定是让图片或语音 更加自然平滑,简而言之,美化. 图像算法和音频算法 都有其共通点. 图像是偏向 空间 处理,例如图片中的某个区域. 图像很多时候是以二维数据 ...

  5. 基于傅里叶变换的音频重采样算法 (附完整c代码)

    前面有提到音频采样算法: WebRTC 音频采样算法 附完整C++示例代码 简洁明了的插值音频重采样算法例子 (附完整C代码) 近段时间有不少朋友给我写过邮件,说了一些他们使用的情况和问题. 坦白讲, ...

  6. 音频自动增益 与 静音检测 算法 附完整C代码

    前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用于评估一定长度音频的音量强度, 而分析之后,很多类似的需求,肯定是做音频增益,提高音量诸如此类做法. ...

  7. java mp3静音检测,音频自动增益 与 静音检测 算法 附完整C代码

    前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用于评估一定长度音频的音量强度, 而分析之后,很多类似的需求,肯定是做音频增益,提高音量诸如此类做法. ...

  8. 13_冒泡算法(附完整java代码)

    13_冒泡算法 一.基本介绍 ​ 冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前 ...

  9. 基于rnn的语音降噪matlab,基于RNN的音频降噪算法 (附完整C代码)

    前几天无意间看到一个项目rnnoise. 基于RNN的音频降噪算法. 采用的是 GRU/LSTM 模型. 阅读下训练代码,可惜的是作者没有提供数据训练集. 不过基本可以断定他采用的数据集里,肯定有ur ...

最新文章

  1. Per Johansson:经济学中自然实验和准实验 | 周日直播·因果科学读书会
  2. C++ Multisets
  3. python运行列表的结果不同_python 3 代码一模一样,出现运行结果不同的情况(只是不以为一样而已)...
  4. (九)HTML5本地存储——本地数据库SQLLite的使用
  5. C指针详解(经典,非常详细)
  6. Android学习笔记进阶十之Matrix错切变换
  7. append是什么意思java_关于append()问题
  8. mvc html的扩展,asp.net mvc - 使用Razor声明性视图中的MVC HtmlHelper扩展
  9. [读书笔记]密码文件总结
  10. 阿里云服务器购买价格,真正实用的阿里云服务器价格表
  11. ttest求pvalue_TTEST 在EXCEL计算出的结果是t还是p值,用哪一个公式在excel中计算出t值和p值是多少...
  12. 手游服务器开发技术详解
  13. 时间标准 GMT, UTC, CST
  14. Python实时获取steam游戏数据
  15. 婚姻中受伤的为什么总是女人
  16. MCAL中PORT配置
  17. 圆面积计算公式,这样理解起来超简单!
  18. C语言 数组中插入一个数
  19. 所有学习资源都给你你汇总好啦!
  20. 【机器学习】贝叶斯分类三大实战项目:高斯模型进行疾病诊断、多项式模型进行文本分类、伯努利模型进行好/差评判别

热门文章

  1. 你知道吗?炒鞋的那帮人,现在去炒数字藏品了
  2. 【STM32H7教程】第90章 STM32H7的CAN FD总线之关键知识点整理
  3. 浅谈 Node.js 热更新
  4. 混合动作空间(Dis_Conti_Hybrid)
  5. 2023-ISCC信息安全与对抗竞赛wp-misc(详解,有脚本( •̀ ω •́ ),脚本解析,有附件)
  6. [导入]n73手机拼音输入法
  7. 通讯录姓氏多音字的问题解决
  8. 心通医疗在港交所上市:商业化仍处于初级阶段,近三年亏损4亿元
  9. android端富文本编辑器HRichEditor
  10. 在k8s上安装Jenkins及常见问题