如何合并'这种特殊方式的两个图像

时间:2016-09-07 20:14:34

标签: image image-processing alphablending blending

假设我们有两个任何格式的图像,可能是front.jpg和back.jpg。 然后我将它们加载并转换为灰度色空间。

我的目的是'合并'它们分为灰度+ alpha通道中的一个图像。

这就是问题,如何正确合并它们,以便合并后的图像在给定完全不透明的白色背景时显示front.jpg(在灰度颜色空间中),并在给出完全不透明的白色背景时显示back.jpg(在灰度颜色空间中)不透明的黑色背景。

这是一个预览,以防我没有清楚地解释问题。您可以将此图像另存为merged.png并使用此HTML代码进行查看

<!DOCTYPE html>
<html>
<body>
    <div><img src="merged.png" style="background:black;" height="500"></div>
    <br/>
    <div><img src="merged.png" style="background:white" height="500"></div>
</body>
</html>

enter image description here

wiki page描述了在混合时如何计算alpha和灰度值。

  

formulas 1

     

如果目的地背景不透明,   formulas 2

根据我的要求,我认为我有这些假设和公式,

  1. 两张图片大小相同(即使它们的大小不同,我们也可以通过某种方式调整它们)
  2. 合并后的图片应满足以下公式(wiki page
  3. 中给出的公式

    给定完全不透明的白色背景

        out.alpha = merged.alpha + background.alpha * (1 - merged.alpha)
        out.gray = (merged.gray * merged.alpha + 
                    background.gray * background.alpha * (1 - merged.alpha)) / out.alpha
    
        // since we want front.jpg to be shown
        // then out.alpha should equal to front.alpha
        //      out.gray  should equal to front.gray  (which we already know)
    

    目的地背景是不透明的,我们可以做这样的简化

        out.alpha = 1
        out.gray = merged.gray * merged.alpha + 
                   background.gray * (1 - merged.alpha)
    
        // since we want front.jpg to be shown
        //   out.gray should equal to front.gray (which we already know)
        //   background.gray equals to 255 (white background)
    

    此刻,

        out.gray = merged.gray * merged.alpha + 
                   255 * (1 - merged.alpha)
        // and out.gray equals to front.gray
    

    在给出完全不透明的黑色背景时应用相同的分析,我们得到

        out.gray = merged.gray * merged.alpha + 
                   0 * (1 - merged.alpha)
        // and out.gray equals to back.gray
    

    这里我们有两个公式和两个未知变量, merged.alpha merged.gray

    front.gray = merged.gray * merged.alpha + 255 *(1 - merged.alpha)

    back.gray = merged.gray * merged.alpha

    求解方程式,

    front.gray = back.gray + 255 *(1 - merged.alpha)

    (front.gray - back.gray)/ 255 = 1 - merged.alpha

    merged.alpha = 1 - (front.gray - back.gray)/ 255.0f

    merged.gray = back.gray / merged.alpha

    然而,当给出完全不透明的白色背景时,这种解决方案并不能正常工作。

    这是我得到的,当给出一个完全不透明的白色背景时,back.jpg显示的东西 enter image description here

    这些是我输入的图像, front.jpg back.jpg

    最后,我的程序,

    编译:

    g++ -std=c++14 -fPIC -I/usr/local/include -L/usr/local/lib -I/usr/local/include -L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_imgproc -lpng main.cpp -o hoshizora
    

    执行命令

    ./hoshizora -f front.jpg -b back.jpg -o megered.png
    

    代码:

    #include <getopt.h>
    #include <stdlib.h>
    #include <cmath>
    #include <opencv2/opencv.hpp>
    #include <opencv2/core/version.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    #include <libpng16/png.h>
    
    using namespace cv;
    
    #if (CV_VERSION_MAJOR < 3)
    #define CV_CVTCOLOR_BGR2GREY  CV_BGR2GRAY
    #define CV_CVTCOLOR_GREY2BGRA CV_GRAY2BGRA
    #else
    #define CV_CVTCOLOR_BGR2GREY  COLOR_BGR2GRAY
    #define CV_CVTCOLOR_GREY2BGRA COLOR_GRAY2BGRA
    #endif
    
    #define K_TRANSPARENT_ALPHA 0
    
    #define ENSURE_NOT_NULL(x) (x != nullptr)
    
    char * front_file = NULL;
    char * back_file = NULL;
    char * output = NULL;
    
    static struct option long_options[] = {
        {"help",            no_argument,       0, 'h'},
        {"front",           required_argument, 0, 'f'},
        {"back",            required_argument, 0, 'b'},
        {"ouput",           required_argument, 0, 'o'},
        {0, 0, 0, 0}
    };
    
    /**
     *  @brief Prints the usage message of this program.
     */
    void print_usage() {
        fprintf(stderr, "Usage: hoshizora [-f front layer]\n"
                        "                 [-b back layer]\n"
                        "                 [-o ouput]\n"
                        "\n"
                        "                 -h To print this help\n");
    }
    
    int parse_command_line(int argc, char * const * argv) {
        int c;
        int option_index = 0;
    
        while (1) {
            option_index = 0;
            c = getopt_long (argc, argv, "hf:b:o:", long_options, &option_index);
            if (c == -1)
                break;
            switch (c) {
                case 0:
                    if (long_options[option_index].flag != 0)
                        break;
                    break;
                case 'h':
                    return 0;
                case 'o': {
                    asprintf(&output, "%s", optarg);
                    break;
                }
                case 'f': {
                    asprintf(&front_file, "%s", optarg);
                    break;
                }
                case 'b': {
                    asprintf(&back_file, "%s", optarg);
                    break;
                }
                case '?':
                    return 0;
                default:
                    abort();
            }
        }
    
        return ENSURE_NOT_NULL(front_file)    &&
               ENSURE_NOT_NULL(back_file)     &&
               ENSURE_NOT_NULL(output);
    }
    
    /**
     *  @brief Resize image to fit
     */
    void resize(Mat &src, Mat &dest, int width, int height, int interpolation = INTER_LINEAR) {
        if (width <= 0 && height <= 0) {
            dest = src.clone();
            return;
        }
        int w,h;
        w = src.cols;
        h = src.rows;
    
        if (width == 0) {
            double ratio = height / double(h);
            width = w * ratio;
        } else {
            double ratio = width / double(w);
            height = h * ratio;
        }
        cv::resize(src, dest, Size(width, height), width/w, height/h, interpolation);
    }
    
    int magic(char* filename, Size size, Mat& frontlayer, Mat& backlayer) {
        int code = 0;
        FILE *fp = NULL;
        png_structp png_ptr = NULL;
        png_infop info_ptr = NULL;
        png_bytep row = NULL;
        auto front_pixel = frontlayer.begin<Vec<uchar, 1>>();
        auto back_pixel = backlayer.begin<Vec<uchar, 1>>();
    
        // Open file for writing (binary mode)
        fp = fopen(filename, "wb");
        if (fp == NULL) {
            fprintf(stderr, "Could not open file %s for writing\n", filename);
            code = 1;
            goto finalise;
        }
    
        // Initialize write structure
        png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
        if (png_ptr == NULL) {
            fprintf(stderr, "Could not allocate write struct\n");
            code = 1;
            goto finalise;
        }
    
        // Initialize info structure
        info_ptr = png_create_info_struct(png_ptr);
        if (info_ptr == NULL) {
            fprintf(stderr, "Could not allocate info struct\n");
            code = 1;
            goto finalise;
        }
    
        // Setup Exception handling
        if (setjmp(png_jmpbuf(png_ptr))) {
            fprintf(stderr, "Error during png creation\n");
            code = 1;
            goto finalise;
        }
        png_init_io(png_ptr, fp);
    
        // Write header (8 bit colour depth)
        png_set_IHDR(png_ptr, info_ptr, size.width, size.height,
                     8, PNG_COLOR_TYPE_GRAY_ALPHA, PNG_INTERLACE_NONE,
                     PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
    
        png_write_info(png_ptr, info_ptr);
    
        // Allocate memory for one row (2 bytes per pixel - Gray+Alpha)
        row = (png_bytep)malloc(2 * size.width * sizeof(png_byte));
    
        // Write image data
    
        for (int y = 0; y < size.height; y++) {
            for (int x = 0; x < size.width; x++) {
                uchar _y = (*back_pixel)[0];
                uchar _x = (*front_pixel)[0];
                uchar A = min(_y + 255 - _x, 255);
                uchar G = (_y * 255.0f) / A;
                row[x * 2 + 1] = A;
                row[x * 2]     = G;
                front_pixel++;
                back_pixel++;
            }
            png_write_row(png_ptr, row);
        }
    
        // End write
        png_write_end(png_ptr, NULL);
    finalise:
        if (fp != NULL) fclose(fp);
        if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
        if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
        if (row != NULL) free(row);
    
        return code;
    }
    
    void overlay_center(Mat& bottom, Mat& layer, double alpha = 0) {
        Rect roi = Rect((bottom.cols - layer.cols) / 2, (bottom.rows - layer.rows) / 2, layer.cols, layer.rows);
        layer.copyTo(bottom(roi));
    }
    
    int main(int argc, const char * argv[]) {
        if (!parse_command_line(argc, (char * const *)argv)) {
            print_usage();
            exit(EXIT_FAILURE);
        }
    
        // Load and transform to gray scale
        Mat front = imread(front_file, CV_LOAD_IMAGE_GRAYSCALE);
        Mat back  = imread(back_file,  CV_LOAD_IMAGE_GRAYSCALE);
    
        // resize image to fit
        if (front.cols > back.cols) {
            if (front.rows > back.rows) {
                if (((double)front.cols / (double)front.rows) > ((double)back.cols / (double)back.rows)) {
                    resize(back, back, 0, front.rows);
                } else {
                    resize(back, back, front.cols, 0);
                }
            } else {
                resize(back, back, 0, front.rows);
            }
        } else {
            if (front.rows < back.rows) {
                if (((double)front.cols / (double)front.rows) > ((double)back.cols / (double)back.rows)) {
                    resize(front, front, 0, back.rows);
                } else {
                    resize(front, front, back.cols, 0);
                }
            } else {
                resize(front, front, 0, back.rows);
            }
        }
    
        // new layers, same size
        auto size = Size(max(front.cols, back.cols), max(front.rows, back.rows));
    
        Mat frontlayer(size, CV_8U, Scalar(255));
        Mat backlayer(size, CV_8U, Scalar(0));
    
        overlay_center(frontlayer, front);
        overlay_center(backlayer, back);
    
        magic(output, size, frontlayer, backlayer);
    
        free((void *)front_file);
        free((void *)back_file);
        free((void *)output);
        return 0;
    }
    

0 个答案:

没有答案