基於OpenCV實現Photoshop的17種圖層混合


一、圖層混合模式是什么?

    所謂圖層混合模式就是指一個層與其下圖層的色彩疊加方式,在這之前我們所使用的是正常模式,除了正常以外,還有很多種混合模式,它們都可以產生迥異的合成效果。

二、PhotoShop的27種混合模式

從很久之前的版本開始,PhotoShop就保持了27種圖層混合模式。

並且可以進一步分為:普通模式、變暗模式、變亮模式、飽和度模式、差集模式和顏色模式。

 

 

      1. 正常(Normal)模式
    在“正常”模式下,“混合色”的顯示與不透明度的設置有關。當“不透明度”為100%,也就是說完全不透明時,“結果色”的像素將完全由所用的“混合色”代替;當“不透明度”小於100%時,混合色的像素會透過所用的顏色顯示出來,顯示的程度取決於不透明度的設置與“基色”的顏色
  2. 溶解(Dissolve)模式
  在“溶解”模式中,根據任何像素位置的不透明度,“結果色”由“基色”或“混合色”的像素隨機替換。

  3. 變暗(Darken)模式
  在“變暗”模式中,查看每個通道中的顏色信息,並選擇“基色”或“混合色”中較暗的顏色作為“結果色”。比“混合色”亮的像素被替換,比“混合色”暗的像素保持不變。“變暗”模式將導致比背景顏色更淡的顏色從“結果色”中被去掉了。

  4. 正片疊底(Multiply)模式
  在“正片疊底”模式中,查看每個通道中的顏色信息,並將“基色”與“混合色”復合。“結果色”總是較暗的顏色。任何顏色與黑色復合產生黑色。任何顏色與白色復合保持不變。

  5. 顏色加深(Clolor Burn)模式
  在“顏色加深”模式中,查看每個通道中的顏色信息,並通過增加對比度使基色變暗以反映混合色,如果與白色混合的話將不會產生變化。

  6. 線性加深(Linear Burn)模式
  在“線性加深”模式中,查看每個通道中的顏色信息,並通過減小亮度使“基色”變暗以反映混合色。如果“混合色”與“基色”上的白色混合后將不會產生變化,

  7. 變亮(Lighten)模式
  在“變亮”模式中,查看每個通道中的顏色信息,並選擇“基色”或“混合色”中較亮的顏色作為“結果色”。比“混合色”暗的像素被替換,比“混合色”亮的像素保持不變。 在這種與“變暗”模式相反的模式下,較淡的顏色區域在最終的“合成色”中占主要地位。

  8. 濾色(Screen)模式
  “濾色”模式與“正片疊底”模式正好相反,它將圖像的“基色”顏色與“混合色”顏色結合起來產生比兩種顏色都淺的第三種顏色。
  9. 顏色減淡(Clolor Dodge)模式
  在“顏色減淡”模式中,查看每個通道中的顏色信息,並通過減小對比度使基色變亮以反映混合色。與黑色混合則不發生變化。

  10. 線性減淡(Linear Dodge)模式
  在“線性減淡”模式中,查看每個通道中的顏色信息,並通過增加亮度使基色變亮以反映混合色。

  11. 疊加(Overlay)模式
  “疊加”模式把圖像的“基色”顏色與“混合色”顏色相混合產生一種中間色。“基色”內顏色比“混合色”顏色暗的顏色使“混合色”顏色倍增,比“混合色”顏色亮的顏色將使“混合色”顏色被遮蓋,而圖像內的高亮部分和陰影部分保持不變,因此對黑色或白色像素着色時“疊加”模式不起作用。“疊加”模式以一種非藝術邏輯的方式把放置或應用到一個層上的顏色同背景色進行混合,然而,卻能得到有趣的效果。背景圖像中的純黑色或純白色區域無法在“疊加”模式下顯示層上的“疊加”着色或圖像區域。背景區域上落在黑色和白色之間的亮度值同“疊加”材料的顏色混合在一起,產生最終的合成顏色。為了使背景圖像看上去好像是同設計或文本一起拍攝的。

  12. 柔光(Soft Light)模式
  “柔光”模式會產生一種柔光照射的效果。如果“混合色”顏色比“基色顏色的像素更亮一些,那么“結果色”將更亮;如果“混合色”顏色比“基色”顏色的像素更暗一些,那么“結果色”顏色將更暗,使圖像的亮度反差增大。例如,如果在背景圖像上塗了50%黑色,這是一個從黑色到白色的梯度,那着色時梯度的較暗區域變得更暗,而較亮區域呈現出更亮的色調。 其實使顏色變亮或變暗,具體取決於“混合色”。此效果與發散的聚光燈照在圖像上相似。 如果“混合色”比 50% 灰色亮,則圖像變亮,就像被減淡了一樣。如果“混合色”比 50% 灰色暗,則圖像變暗,就象被加深了一樣。用純黑色或純白色繪畫會產生明顯較暗或較亮的區域,但不會產生純黑色或純白色。
  13. 強光(Hard Light)模式
  “強光”模式將產生一種強光照射的效果。如果“混合色”顏色“基色”顏色的像素更亮一些,那么“結果色”顏色將更亮;如果“混合色”顏色比“基色”顏色的像素更暗一些,那么“結果色”將更暗。除了根據背景中的顏色而使背景色是多重的或屏蔽的之外,這種模式實質上同“柔光”模式是一樣的。它的效果要比“柔光”模式更強烈一些,同“疊加”一樣,這種模式也可以在背景對象的表面模擬圖案或文本,例如,如果混合色比 50% 灰色亮,則圖像變亮,就像過濾后的效果。這對於向圖像中添加高光非常有用。如果混合色比 50%灰色暗,則圖像變暗,就像復合后的效果。這對於向圖像添加暗調非常有用。用純黑色或純白色繪畫會產生純黑色或純白色。
  14. 亮光(Vivid Light)模式
  通過增加或減小對比度來加深或減淡顏色,具體取決於混合色。如果混合色(光源)比 50% 灰色亮,則通過減小對比度使圖像變亮。如果混合色比 50% 灰色暗,則通過增加對比度使圖像變暗,

  15. 線性光(Linear Light)模式
  通過減小或增加亮度來加深或減淡顏色,具體取決於混合色。如果混合色(光源)比 50% 灰色亮,則通過增加亮度使圖像變亮。如果混合色比 50% 灰色暗,則通過減小亮度使圖像變暗。

  16. 點光(Pin Light)模式
  “點光”模式其實就是替換顏色,其具體取決於“混合色”。如果“混合色”比 50% 灰色亮,則替換比“混合色”暗的像素,而不改變比“混合色”亮的像素。如果“混合色”比 50% 灰色暗,則替換比“混合色”亮的像素,而不改變比“混合色”暗的像素。這對於向圖像添加特殊效果非常有用。

  17. 差值(Diference)模式
  在“差值”模式中,查看每個通道中的顏色信息,“差值”模式是將從圖像中“基色”顏色的亮度值減去“混合色”顏色的亮度值,如果結果為負,則取正值,產生反相效果。由於黑色的亮度值為0,白色的亮度值為255,因此用黑色着色不會產生任何影響,用白色着色則產生被着色的原始像素顏色的反相。“差值”模式創建背景顏色的相反色彩,例如,在“差值”模式下,當把藍色應用到綠色背景中時將產生一種青綠組合色。“差值”模式適用於模擬原始設計的底片,而且尤其可用來在其背景顏色從一個區域到另一區域發生變化的圖像中生成突出效果。

  18. 排除(Exclusion)模式
  “排除”模式與“差值”模式相似,但是具有高對比度和低飽和度的特點。比用“差值”模式獲得的顏色要柔和、更明亮一些。建議你在處理圖像時,首先選擇“差值”模式,若效果不夠理想,可以選擇“排除”模式來試試。其中與白色混合將反轉“基色”值,而與黑色混合則不發生變化。其實無論是“差值”模式還是“排除”模式都能使人物或自然景色圖像產生更真實或更吸引人的圖像合成。

  19. 色相(Hue)模式
  “色相”模式只用“混合色”顏色的色相值進行着色,而使飽和度和亮度值保持不變。當“基色”顏色與“混合色”顏色的色相值不同時,才能使用描繪顏色進行着色,如圖30所示。但是要注意的是“色相”模式不能用於灰度模式的圖像。

  20. 飽和度(Saturation)模式
  “飽和度”模式的作用方式與“色相”模式相似,它只用“混合色”顏色的飽和度值進行着色,而使色相值和亮度值保持不變。當“基色”顏色與“混合色”顏色的飽和度值不同時,才能使用描繪顏色進行着色處理,如圖31所示。在無飽和度的區域上(也就是灰色區域中)用“飽和度”模式是不會產生任何效果的。

  21. 顏色(Color)模式
  “顏色”模式能夠使用“混合色”顏色的飽和度值和色相值同時進行着色,而使“基色”顏色的亮度值保持不變。“顏色”模式模式可以看成是“飽合度”模式和“色相”模式的綜合效果。該模式能夠使灰色圖像的陰影或輪廓透過着色的顏色顯示出來,產生某種色彩化的效果。這樣可以保留圖像中的灰階,並且對於給單色圖像上色和給彩色圖像着色都會非常有用。

  22. 亮度(Luminosity)模式
  “亮度”模式能夠使用“混合色”顏色的亮度值進行着色,而保持“基色”顏色的飽和度和色相數值不變。其實就是用“基色”中的“色相”和“飽和度”以及“混合色”的亮度創建“結果色”。

 

三、代碼實現:

其實這里的混合算法大多不復雜,特別是在有算法文檔 http://www.deepskycolors.com/archivo/2010/04/21/formulas-for-Photoshop-blending-modes.html。支持的前提下,這里我們就文檔中提到的16種再加上我之前有研究過的划分算法,整理出表格。

 

Blend mode Formula
Darken min(Target,Blend)         
Multiply Target * Blend         
Color Burn 1 - (1-Target) / Blend         
Linear Burn Target + Blend - 1         
Lighten max(Target,Blend)         
Screen 1 - (1-Target) * (1-Blend)         
Color Dodge Target / (1-Blend)         
Linear Dodge Target + Blend         
Overlay (Target > 0.5) * (1 - (1-2*(Target-0.5)) * (1-Blend)) +
(Target <= 0.5) * ((2*Target) * Blend)
Soft Light (Blend > 0.5) * (1 - (1-Target) * (1-(Blend-0.5))) +
(Blend <= 0.5) * (Target * (Blend+0.5))
Hard Light (Blend > 0.5) * (1 - (1-Target) * (1-2*(Blend-0.5))) +
(Blend <= 0.5) * (Target * (2*Blend))
Vivid Light (Blend > 0.5) * (1 - (1-Target) / (2*(Blend-0.5))) +
(Blend <= 0.5) * (Target / (1-2*Blend))
Linear Light (Blend > 0.5) * (Target + 2*(Blend-0.5)) +
(Blend <= 0.5) * (Target + 2*Blend - 1)
Pin Light (Blend > 0.5) * (max(Target,2*(Blend-0.5))) +
(Blend <= 0.5) * (min(Target,2*Blend)))
Difference | Target - Blend |         
Exclusion 0.5 - 2*(Target-0.5)*(Blend-0.5)

 

編寫具有OpenCV風格的代碼:

#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/photo/photo.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
 
using namespace std;
using namespace cv;
 
#define EPSILON      1e-6f
 
#define SAFE_DIV_MIN EPSILON
#define SAFE_DIV_MAX (1.0f / SAFE_DIV_MIN)
 
#define    CLAMP(f,min,max)    ((f)<(min)?(min):(f)>(max)?(max):(f))
//! layerModelBlending algorithm flags
enum
{
    DARKEN = 1,                //min(Target,Blend)
    MULTIPY = 2,               //Target * Blend      
    COLOR_BURN = 3,            //1 - (1-Target) / Blend         
    LINEAR_BRUN = 4,            //Target + Blend - 1         
    LIGHTEN = 5,               //max(Target,Blend)       
    SCREEN = 6,                //1 - (1-Target) * (1-Blend)         
    COLOR_DODGE = 7,           //Target / (1-Blend)         
    LINEAR_DODGE = 8,          //Target + Blend         
    OVERLAY = 9,               //(Target > 0.5) * (1 - (1-2*(Target-0.5)) * (1-Blend)) +(Target <= 0.5) * ((2*Target) * Blend)
    SOFT_LIGHT = 10,           //(Blend > 0.5) * (1 - (1-Target) * (1-(Blend-0.5))) +(Blend <= 0.5) * (Target * (Blend+0.5))
    HARD_LIGHT = 11,           //(Blend > 0.5) * (1 - (1-Target) * (1-2*(Blend-0.5))) +(Blend <= 0.5) * (Target * (2*Blend))
    VIVID_LIGHT = 12,          //(Blend > 0.5) * (1 - (1-Target) / (2*(Blend-0.5))) +(Blend <= 0.5) * (Target / (1-2*Blend))
    LINEAR_LIGHT = 13,         //(Blend > 0.5) * (Target + 2*(Blend-0.5)) +(Blend <= 0.5) * (Target + 2*Blend - 1)
    PIN_LIGHT = 14,            //(Blend > 0.5) * (max(Target,2*(Blend-0.5))) +(Blend <= 0.5) * (min(Target,2*Blend)))
    DIFFERENCE = 15,           //| Target - Blend |         
    EXCLUSION = 16,            //0.5 - 2*(Target-0.5)*(Blend-0.5)
    DIVIDE = 17                //Target/Blend
 

};

/*  local function prototypes  */
static inline float    safe_div(float afloat b);
/* returns a / b, clamped to [-SAFE_DIV_MAX, SAFE_DIV_MAX].
 * if -SAFE_DIV_MIN <= a <= SAFE_DIV_MIN, returns 0.
 */
static inline float safe_div(float afloat b)
{
    float result = 0.0f;
 
    if (fabsf(a) > SAFE_DIV_MIN)
    {
        result = a / b;
        result = CLAMP(result, -SAFE_DIV_MAXSAFE_DIV_MAX);
    }
 
    return result;
}
//TODO target和blend應該是同樣大小

CV_EXPORTS_W void layerModelBlending(Mat targetMat blendMat dstint flag);

CV_EXPORTS_W void layerModelBlending(Mat targetMat blendMat dstint flag)
{
    for (int index_row = 0; index_row < target.rowsindex_row++)
        for (int index_col = 0; index_col < target.colsindex_col++)
            for (int index_c = 0; index_c < 3; index_c++)
                switch (flag)
                {
                case DARKEN:
                    dst.at<Vec3f>(index_rowindex_col)[index_c] = min(
                        target.at<Vec3f>(index_rowindex_col)[index_c],
                        blend.at<Vec3f>(index_rowindex_col)[index_c]);
                    break;
                case MULTIPY:
                    dst.at<Vec3f>(index_rowindex_col)[index_c] =
                        target.at<Vec3f>(index_rowindex_col)[index_c] *
                        blend.at<Vec3f>(index_rowindex_col)[index_c];
                    break;
                case COLOR_BURN:
                    dst.at<Vec3f>(index_rowindex_col)[index_c] = 1 -
                        safe_div((1 - target.at<Vec3f>(index_rowindex_col)[index_c]),
                            blend.at<Vec3f>(index_rowindex_col)[index_c]);
                    break;
                case LINEAR_BRUN:
                    dst.at<Vec3f>(index_rowindex_col)[index_c] =
                        target.at<Vec3f>(index_rowindex_col)[index_c] +
                        blend.at<Vec3f>(index_rowindex_col)[index_c] - 1;
                    break;
                case LIGHTEN:
                    dst.at<Vec3f>(index_rowindex_col)[index_c] = max(
                        target.at<Vec3f>(index_rowindex_col)[index_c],
                        blend.at<Vec3f>(index_rowindex_col)[index_c]);
                    break;
                case SCREEN:
                    dst.at<Vec3f>(index_rowindex_col)[index_c] = 1 -
                        (1 - target.at<Vec3f>(index_rowindex_col)[index_c]) *
                        (1 - blend.at<Vec3f>(index_rowindex_col)[index_c]);
                    break;
                case COLOR_DODGE:
                    dst.at<Vec3f>(index_rowindex_col)[index_c] = safe_div
                    (target.at<Vec3f>(index_rowindex_col)[index_c],
                        1 - blend.at<Vec3f>(index_rowindex_col)[index_c]);
                    break;
                case LINEAR_DODGE:
                    dst.at<Vec3f>(index_rowindex_col)[index_c] =
                        target.at<Vec3f>(index_rowindex_col)[index_c] +
                        blend.at<Vec3f>(index_rowindex_col)[index_c];
                    break;
                case OVERLAY:
                    if (target.at<Vec3f>(index_rowindex_col)[index_c] > 0.5f)
                        dst.at<Vec3f>(index_rowindex_col)[index_c] = 1 -
                        (1 - 2 * (target.at<Vec3f>(index_rowindex_col)[index_c] - 0.5)) *
                        (1 - blend.at<Vec3f>(index_rowindex_col)[index_c]);
                    else
                        dst.at<Vec3f>(index_rowindex_col)[index_c] = 2 *
                        target.at<Vec3f>(index_rowindex_col)[index_c] *
                        blend.at<Vec3f>(index_rowindex_col)[index_c];
                    break;
                case SOFT_LIGHT:
                    if (target.at<Vec3f>(index_rowindex_col)[index_c] > 0.5f)
                        dst.at<Vec3f>(index_rowindex_col)[index_c] = 1 -
                        (1 - target.at<Vec3f>(index_rowindex_col)[index_c]) *
                        (1 - (blend.at<Vec3f>(index_rowindex_col)[index_c] - 0.5));
                    else
                        dst.at<Vec3f>(index_rowindex_col)[index_c] =
                        target.at<Vec3f>(index_rowindex_col)[index_c] *
                        (blend.at<Vec3f>(index_rowindex_col)[index_c] + 0.5);
                    break;
                case HARD_LIGHT:
                    if (target.at<Vec3f>(index_rowindex_col)[index_c] > 0.5f)
                        dst.at<Vec3f>(index_rowindex_col)[index_c] = 1 -
                        (1 - target.at<Vec3f>(index_rowindex_col)[index_c]) *
                        (1 - 2 * blend.at<Vec3f>(index_rowindex_col)[index_c] - 0.5);
                    else
                        dst.at<Vec3f>(index_rowindex_col)[index_c] =
                        target.at<Vec3f>(index_rowindex_col)[index_c] *
                        (2 * blend.at<Vec3f>(index_rowindex_col)[index_c]);
                    break;
                case VIVID_LIGHT:
                    if (target.at<Vec3f>(index_rowindex_col)[index_c] > 0.5f)
                        dst.at<Vec3f>(index_rowindex_col)[index_c] = 1 -
                        safe_div(1 - target.at<Vec3f>(index_rowindex_col)[index_c],
                        (2 * (blend.at<Vec3f>(index_rowindex_col)[index_c] - 0.5)));
                    else
                        dst.at<Vec3f>(index_rowindex_col)[index_c] =
                        safe_div(target.at<Vec3f>(index_rowindex_col)[index_c],
                        (1 - 2 * blend.at<Vec3f>(index_rowindex_col)[index_c]));
                    break;
                case LINEAR_LIGHT:
                    if (target.at<Vec3f>(index_rowindex_col)[index_c] > 0.5f)
                        dst.at<Vec3f>(index_rowindex_col)[index_c] =
                        target.at<Vec3f>(index_rowindex_col)[index_c] +
                        (2 * (blend.at<Vec3f>(index_rowindex_col)[index_c] - 0.5));
                    else
                        dst.at<Vec3f>(index_rowindex_col)[index_c] =
                        target.at<Vec3f>(index_rowindex_col)[index_c] +
                        2 * blend.at<Vec3f>(index_rowindex_col)[index_c] - 1;
                    break;
                case PIN_LIGHT:
                    if (target.at<Vec3f>(index_rowindex_col)[index_c] > 0.5f)
                        dst.at<Vec3f>(index_rowindex_col)[index_c] =
                        max(target.at<Vec3f>(index_rowindex_col)[index_c],
                        (float)(2 * (blend.at<Vec3f>(index_rowindex_col)[index_c] - 0.5)));
                    else
                        dst.at<Vec3f>(index_rowindex_col)[index_c] =
                        min(target.at<Vec3f>(index_rowindex_col)[index_c],
                            2 * blend.at<Vec3f>(index_rowindex_col)[index_c]);
                    break;
                case DIFFERENCE:
                    dst.at<Vec3f>(index_rowindex_col)[index_c] =
                        abs(target.at<Vec3f>(index_rowindex_col)[index_c] -
                            blend.at<Vec3f>(index_rowindex_col)[index_c]);
                    break;
                case EXCLUSION:
                    dst.at<Vec3f>(index_rowindex_col)[index_c] =
                        target.at<Vec3f>(index_rowindex_col)[index_c] +
                        blend.at<Vec3f>(index_rowindex_col)[index_c] -
                        2 * target.at<Vec3f>(index_rowindex_col)[index_c] * blend.at<Vec3f>(index_rowindex_col)[index_c];
                    break;
                case DIVIDE:
                    dst.at<Vec3f>(index_rowindex_col)[index_c] =
                        safe_div(target.at<Vec3f>(index_rowindex_col)[index_c],
                            blend.at<Vec3f>(index_rowindex_col)[index_c]);
                    break;
                }

}

int main() {
    Mat target = cv::imread("e:/template/lena.jpg");
    Mat blend = cv::imread("e:/template/opencv-logo.png");
    Mat dst(target.size(), CV_32FC3Scalar::all(0));
    Mat dst2(target.size(), CV_8UC3Scalar::all(0));
    if (target.empty()) {
        std::cout << "Unable to load target!\n";
    }
    if (blend.empty()) {
        std::cout << "Unable to load blend!\n";
    }
    resize(blendblendtarget.size());
    target.convertTo(targetCV_32F, 1.0 / 255);
    blend.convertTo(blendCV_32F, 1.0 / 255);
 
    layerModelBlending(targetblenddstDARKEN);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/DARKEN_RESULT.jpg"dst2);
 
    layerModelBlending(targetblenddstMULTIPY);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/MULTIPY_RESULT.jpg"dst2);
 
    layerModelBlending(targetblenddstCOLOR_BURN);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/COLOR_BURN.jpg"dst2);
 
    layerModelBlending(targetblenddstLINEAR_BRUN);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/LINEAR_BRUN.jpg"dst2);
 
    layerModelBlending(targetblenddstLIGHTEN);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/LIGHTEN.jpg"dst2);
 
    layerModelBlending(targetblenddstSCREEN);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/SCREEN_RESULT.jpg"dst2);
 
    layerModelBlending(targetblenddstCOLOR_DODGE);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/COLOR_DODGE_RESULT.jpg"dst2);
 
    layerModelBlending(targetblenddstLINEAR_DODGE);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/LINEAR_DODGE_RESULT.jpg"dst2);
 
    layerModelBlending(targetblenddstOVERLAY);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/OVERLAY_RESULT.jpg"dst2);
 
    layerModelBlending(targetblenddstSOFT_LIGHT);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/SOFT_LIGHT_RESULT.jpg"dst2);
 
    layerModelBlending(targetblenddstHARD_LIGHT);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/HARD_LIGHT_RESULT.jpg"dst2);
 
    layerModelBlending(targetblenddstVIVID_LIGHT);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/VIVID_LIGHT_RESULT.jpg"dst2);
 
    layerModelBlending(targetblenddstLINEAR_LIGHT);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/LINEAR_LIGHT_RESULT.jpg"dst2);
 
    layerModelBlending(targetblenddstPIN_LIGHT);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/PIN_LIGHT_RESULT.jpg"dst2);
 
    layerModelBlending(targetblenddstDIFFERENCE);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/DIFFERENCE_RESULT.jpg"dst2);
 
    layerModelBlending(targetblenddstEXCLUSION);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/EXCLUSION_RESULT.jpg"dst2);
 
    layerModelBlending(targetblenddstDIVIDE);
    dst.convertTo(dst2CV_8UC3, 255);
    imwrite("E:/sandbox/layerBlendingModles/DIVIDE_RESULT.jpg"dst2);
}

17種結果展示如下。

 

四、重要的參考:

在本例代碼實現過程中,主要參考了兩個方面的代碼:

一個是GIMP的代碼:來自gimpoperationlayermode-blend.h。

函數名稱 核心代碼
gimp_operation_layer_mode_blend_addition
for  (c =  0 ; c <  3 ; c++)
            comp[c] = in[c] + layer[c];
gimp_operation_layer_mode_blend_burn
  for  (c =  0 ; c <  3 ; c++)
            comp[c] =  1.0f  - safe_div ( 1.0f  - in[c], layer[c]);
gimp_operation_layer_mode_blend_darken_only
  for  (c =  0 ; c <  3 ; c++)
            comp[c] = MIN (in[c], layer[c]);
gimp_operation_layer_mode_blend_difference
  for  (c =  0 ; c <  3 ; c++)
            comp[c] = fabsf (in[c] - layer[c]);
gimp_operation_layer_mode_blend_divide ( const  gfloat *in,
for  (c =  0 ; c <  3 ; c++)
            comp[c] = safe_div (in[c], layer[c]);
gimp_operation_layer_mode_blend_dodge
for  (c =  0 ; c <  3 ; c++)
            comp[c] = safe_div (in[c],  1.0f  - layer[c]);
gimp_operation_layer_mode_blend_exclusion
for  (c =  0 ; c <  3 ; c++)
            comp[c] =  0.5f  -  2.0f  * (in[c] -  0.5f ) * (layer[c] -  0.5f );
gimp_operation_layer_mode_blend_grain_extract
for  (c =  0 ; c <  3 ; c++)
            comp[c] = in[c] - layer[c] +  0.5f ;
gimp_operation_layer_mode_blend_grain_merge
for  (c =  0 ; c <  3 ; c++)
            comp[c] = in[c] + layer[c] -  0.5f ;
gimp_operation_layer_mode_blend_hard_mix
for  (c =  0 ; c <  3 ; c++)
            comp[c] = in[c] + layer[c] <  1.0f  ?  0.0f  :  1.0f ;
gimp_operation_layer_mode_blend_hardlight
for  (c =  0 ; c <  3 ; c++)
            {
              gfloat val;

               if  (layer[c] >  0.5f )
                {
                  val = ( 1.0f  - in[c]) * ( 1.0f  - (layer[c] -  0.5f ) *  2.0f );
                  val = MIN ( 1.0f  - val,  1.0f );
                }
               else
                {
                  val = in[c] * (layer[c] *  2.0f );
                  val = MIN (val,  1.0f );
                }

              comp[c] = val;
            }
gimp_operation_layer_mode_blend_hsl_color



未整理



gimp_operation_layer_mode_blend_hsv_hue
gimp_operation_layer_mode_blend_hsv_saturation
gimp_operation_layer_mode_blend_hsv_value
gimp_operation_layer_mode_blend_lch_chroma
gimp_operation_layer_mode_blend_lch_color
gimp_operation_layer_mode_blend_lch_hue
gimp_operation_layer_mode_blend_lch_lightness
if  (in[ALPHA] !=  0.0f  && layer[ALPHA] !=  0.0f )
        {
          comp[ 0 ] = layer[ 0 ];
          comp[ 1 ] = in[ 1 ];
          comp[ 2 ] = in[ 2 ];
        }
gimp_operation_layer_mode_blend_lighten_only
  for  (c =  0 ; c <  3 ; c++)
            comp[c] = MAX (in[c], layer[c]);
gimp_operation_layer_mode_blend_linear_burn
for  (c =  0 ; c <  3 ; c++)
            comp[c] = in[c] + layer[c] -  1.0f ;
gimp_operation_layer_mode_blend_linear_light
for  (c =  0 ; c <  3 ; c++)
            {
              gfloat val;

               if  (layer[c] <=  0.5f )
                val = in[c] +  2.0f  * layer[c] -  1.0f ;
               else
                val = in[c] +  2.0f  * (layer[c] -  0.5f );

              comp[c] = val;
            }
gimp_operation_layer_mode_blend_luma_darken_only
  if  (dest_luminance <= src_luminance)
            {
               for  (c =  0 ; c <  3 ; c++)
                comp[c] = in[c];
            }
           else
            {
               for  (c =  0 ; c <  3 ; c++)
                comp[c] = layer[c];
            }
gimp_operation_layer_mode_blend_luma_lighten_only
  if  (dest_luminance >= src_luminance)
            {
               for  (c =  0 ; c <  3 ; c++)
                comp[c] = in[c];
            }
           else
            {
               for  (c =  0 ; c <  3 ; c++)
                comp[c] = layer[c];
            }
gimp_operation_layer_mode_blend_luminance
  if  (layer[ALPHA] !=  0.0f  && in[ALPHA] !=  0.0f )
        {
          gfloat ratio = safe_div (layer_Y_p[ 0 ], in_Y_p[ 0 ]);
          gint   c;

           for  (c =  0 ; c <  3 ; c ++)
            comp[c] = in[c] * ratio;
        }
gimp_operation_layer_mode_blend_multiply
  for  (c =  0 ; c <  3 ; c++)
            comp[c] = in[c] * layer[c];
gimp_operation_layer_mode_blend_overlay
for  (c =  0 ; c <  3 ; c++)
            {
              gfloat val;

               if  (in[c] <  0.5f )
                val =  2.0f  * in[c] * layer[c];
               else
                val =  1.0f  -  2.0f  * ( 1.0f  - layer[c]) * ( 1.0f  - in[c]);

              comp[c] = val;
            }
gimp_operation_layer_mode_blend_pin_light
未整理

一個是網站的代碼:來自https://blog.csdn.net/matrix_space

 

算法名 實現
溶解
Dissolve

// Dissolve
void Dissolve(Mat& src1, Mat& src2, Mat& dst, double alpha)
{
    dst=src1;
    Mat Rand_mat(src1.size(), CV_32FC1);
    cv::randu(Rand_mat, 0,1);
    float a=0;
    float b=0;
     for(int index_row=0; index_row<src1.rows; index_row++)
    {
        for(int index_col=0; index_col<src1.cols; index_col++)
        {
            b=Rand_mat.at<float>(index_row, index_col);
            if(b<alpha)
            {
                for(int index_c=0; index_c<3; index_c++)
               {
                   a=src2.at<Vec3f>(index_row, index_col)[index_c];
                   dst.at<Vec3f>(index_row, index_col)[index_c]=a;
               }
            }
        }
    }
}
變暗
Darken
C = MIN(A,B)
// Darken
void Darken(Mat& src1, Mat& src2, Mat& dst)
{
     for(int index_row=0; index_row<src1.rows; index_row++)
    {
        for(int index_col=0; index_col<src1.cols; index_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
                dst.at<Vec3f>(index_row, index_col)[index_c]=min(
                         src1.at<Vec3f>(index_row, index_col)[index_c],
                         src2.at<Vec3f>(index_row, index_col)[index_c]);
        }
    }
正片疊底
Multiply
C=A*B/255
// Multiply 正片疊底
void Multiply(Matsrc1Matsrc2Matdst)
{
    for(int index_row=0; index_row<src1.rowsindex_row++)
    {
        for(int index_col=0; index_col<src1.colsindex_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
                dst.at<Vec3f>(index_rowindex_col)[index_c]=
                src1.at<Vec3f>(index_rowindex_col)[index_c]*
                src2.at<Vec3f>(index_rowindex_col)[index_c];
        }
    }
}
顏色加深
Color_Burn
C = A-((255-A)×(255-B))/ B

// Color_Burn 顏色加深
void Color_Burn(Matsrc1Matsrc2Matdst)
{
    for(int index_row=0; index_row<src1.rowsindex_row++)
    {
        for(int index_col=0; index_col<src1.colsindex_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
                dst.at<Vec3f>(index_rowindex_col)[index_c]=1-
                (1-src1.at<Vec3f>(index_rowindex_col)[index_c])/
                src2.at<Vec3f>(index_rowindex_col)[index_c];
        }
    }
}
線性加深
Linear_Burn
C=A+B-255
// 線性增強
void(Matsrc1Matsrc2Matdst)
{
    for(int index_row=0; index_row<src1.rowsindex_row++)
    {
        for(int index_col=0; index_col<src1.colsindex_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
                dst.at<Vec3f>(index_rowindex_col)[index_c]=max(
                src1.at<Vec3f>(index_rowindex_col)[index_c]+
                src2.at<Vec3f>(index_rowindex_col)[index_c]-1, (float)0.0);
        }
    }
}
變亮
C = MAX(A,B)
 
濾色
Screen

// Screen
void Screen(Mat& src1, Mat& src2, Mat& dst)
{
     for(int index_row=0; index_row<src1.rows; index_row++)
    {
        for(int index_col=0; index_col<src1.cols; index_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
                dst.at<Vec3f>(index_row, index_col)[index_c]=1-
                         (1-src1.at<Vec3f>(index_row, index_col)[index_c])*
                         (1-src2.at<Vec3f>(index_row, index_col)[index_c]);
        }
    }
}
顏色減淡
Color_Dodge

// Color_Dodge 顏色減淡
void  Color_Dodge(Mat &  src1, Mat &  src2, Mat &  dst)
{
     for ( int  index_row = 0 ; index_row < src1.rows; index_row ++ )
    {
         for ( int  index_col = 0 ; index_col < src1.cols; index_col ++ )
        {
             for ( int  index_c = 0 ; index_c < 3 ; index_c ++ )
                dst.at < Vec3f > (index_row, index_col)[index_c] =
                          src2.at < Vec3f > (index_row, index_col)[index_c] /
                         ( 1 - src1.at < Vec3f > (index_row, index_col)[index_c]);
        }
    }
}
線性減淡(添加)
C=A+B
// Lighten
void Lighten(Mat& src1, Mat& src2, Mat& dst)
{
    for(int index_row=0; index_row<src1.rows; index_row++)
    {
        for(int index_col=0; index_col<src1.cols; index_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
                dst.at<Vec3f>(index_row, index_col)[index_c]=max(
                         src1.at<Vec3f>(index_row, index_col)[index_c],
                         src2.at<Vec3f>(index_row, index_col)[index_c]);
        }
    }
}
淺色  
疊加
Add_Color
// Add color
void Add_Color(Mat& src1, Mat& src2, Mat& dst)
{
    float a=0;
    float b=0;
     for(int index_row=0; index_row<src1.rows; index_row++)
    {
        for(int index_col=0; index_col<src1.cols; index_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
            {
                a=src1.at<Vec3f>(index_row, index_col)[index_c];
                b=src2.at<Vec3f>(index_row, index_col)[index_c];
                if(b>0.5)
                {
                    dst.at<Vec3f>(index_row, index_col)[index_c]=2*a*b;
                }
                else
                {
                    dst.at<Vec3f>(index_row, index_col)[index_c]=1-2*(1-a)*(1-b);
                }
            }
        }
    }
}
柔光
Soft_Lighten
// Soft Lighten
void Soft_Lighten(Mat& src1, Mat& src2, Mat& dst)
{
    float a=0;
    float b=0;
     for(int index_row=0; index_row<src1.rows; index_row++)
    {
        for(int index_col=0; index_col<src1.cols; index_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
            {
                a=src1.at<Vec3f>(index_row, index_col)[index_c];
                b=src2.at<Vec3f>(index_row, index_col)[index_c];
                if(a<=0.5)
                {
                    dst.at<Vec3f>(index_row, index_col)[index_c]=(2*a-1)*(b-b*b)+b;
                }
                else
                {
                    dst.at<Vec3f>(index_row, index_col)[index_c]=(2*a-1)*(sqrt(b)-b)+b;
                }
            }
        }
    }
}
強光
Strong_Lighten
void Strong_Lighten(Mat& src1, Mat& src2, Mat& dst)
{
    float a=0;
    float b=0;
     for(int index_row=0; index_row<src1.rows; index_row++)
    {
        for(int index_col=0; index_col<src1.cols; index_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
            {
                a=src1.at<Vec3f>(index_row, index_col)[index_c];
                b=src2.at<Vec3f>(index_row, index_col)[index_c];
                if(a<=0.5)
                {
                    dst.at<Vec3f>(index_row, index_col)[index_c]=2*a*b;
                }
                else
                {
                    dst.at<Vec3f>(index_row, index_col)[index_c]=1-2*(1-a)*(1-b);
                }
            }
        }
    }
}
亮光
Vivid_Lighten
//Vivid Lighten
void Vivid_Lighten(Mat& src1, Mat& src2, Mat& dst)
{
    float a=0;
    float b=0;
     for(int index_row=0; index_row<src1.rows; index_row++)
    {
        for(int index_col=0; index_col<src1.cols; index_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
            {
                a=src1.at<Vec3f>(index_row, index_col)[index_c];
                b=src2.at<Vec3f>(index_row, index_col)[index_c];
                if(a<=0.5)
                {
                    dst.at<Vec3f>(index_row, index_col)[index_c]=1-(1-b)/(2*a);
                }
                else
                {
                    dst.at<Vec3f>(index_row, index_col)[index_c]=b/(2*(1-a));
                }
            }
        }
    }
}
線性光
Linear_Lighten
// Linear Lighten
void Linear_Lighten(Mat& src1, Mat& src2, Mat& dst)
{
    dst=src2+2*src1-1;
}
點光
Pin_Lighten
// Pin lighten
void Pin_Lighten(Mat& src1, Mat& src2, Mat& dst)
{
    float a=0;
    float b=0;
     for(int index_row=0; index_row<src1.rows; index_row++)
    {
        for(int index_col=0; index_col<src1.cols; index_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
            {
                a=src1.at<Vec3f>(index_row, index_col)[index_c];
                b=src2.at<Vec3f>(index_row, index_col)[index_c];
                if(b<=2*a-1)
                {
                    dst.at<Vec3f>(index_row, index_col)[index_c]=2*a-1;
                }
                else if(b<=2*a)
                {
                    dst.at<Vec3f>(index_row, index_col)[index_c]=b;
                }
                else
                {
                    dst.at<Vec3f>(index_row, index_col)[index_c]=2*a;
                }
            }
        }
    }
}
實色混合
Hard_mix
// Hard mix
void Hard_mix(Mat& src1, Mat& src2, Mat& dst)
{
    float a=0;
    float b=0;
     for(int index_row=0; index_row<src1.rows; index_row++)
    {
        for(int index_col=0; index_col<src1.cols; index_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
            {
                a=src1.at<Vec3f>(index_row, index_col)[index_c];
                b=src2.at<Vec3f>(index_row, index_col)[index_c];
                if(a<1-b)
                {
                    dst.at<Vec3f>(index_row, index_col)[index_c]=0.0;
                }
                else
                {
                    dst.at<Vec3f>(index_row, index_col)[index_c]=1.0;
                }
            }
        }
    }
}
差值
Difference
// Difference
void Difference(Mat& src1, Mat& src2, Mat& dst)
{
    float a=0;
    float b=0;
     for(int index_row=0; index_row<src1.rows; index_row++)
    {
        for(int index_col=0; index_col<src1.cols; index_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
            {
                a=src1.at<Vec3f>(index_row, index_col)[index_c];
                b=src2.at<Vec3f>(index_row, index_col)[index_c];
                dst.at<Vec3f>(index_row, index_col)[index_c]=abs(a-b);
            }
        }
    }
}
排除
Exclusion
// Exclusion
void Exclusion(Mat& src1, Mat& src2, Mat& dst)
{
    float a=0;
    float b=0;
     for(int index_row=0; index_row<src1.rows; index_row++)
    {
        for(int index_col=0; index_col<src1.cols; index_col++)
        {
            for(int index_c=0; index_c<3; index_c++)
            {
                a=src1.at<Vec3f>(index_row, index_col)[index_c];
                b=src2.at<Vec3f>(index_row, index_col)[index_c];
                dst.at<Vec3f>(index_row, index_col)[index_c]=a+b-2*a*b;
            }
        }
    }
}
減去  
划分
divide
結果色 = (基色 / 混合色) * 255
    Mat src = imread("t1.jpeg");
    src.convertTo(src,CV_32FC3,1.0/255);
    Mat gauss;
    Mat dst = src.clone();
    cv::GaussianBlur(src,gauss,Size(101,101),0);
    dst  =  src / gauss ;

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM