zoukankan      html  css  js  c++  java
  • 自动曝光修复算法 附完整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');

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

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

    论文链接直达

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

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

    需要进行二次改造,

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

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

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

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

    效果图如下:

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

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

    项目地址:https://github.com/cpuimage/LocalColorCorrection

    再来一个效果前后对比:

    以上,权当抛砖引玉。

    若有其他相关问题或者需求也可以邮件联系俺探讨。

    邮箱地址是: 
    gaozhihan@vip.qq.com

  • 相关阅读:
    原型模式 prototype
    OOAD之单例模式Singleton的6种写法
    OOAD之创建型模式之工厂模式
    OOAD之面向对象设计原则
    第一章 面向对象软件工程与UML
    Oracle数据库之PL/SQL触发器
    Oracle数据库之开发PL/SQL子程序和包
    Oracle数据库中的分页--rownum
    Oracle数据库之FORALL与BULK COLLECT语句
    Oracle数据库 中的基础的一些语法结构
  • 原文地址:https://www.cnblogs.com/cpuimage/p/9125646.html
Copyright © 2011-2022 走看看