W jaki sposób Photoshop łączy ze sobą dwa obrazy? [Zamknięte]

84

Czy ktoś mógłby wyjaśnić, jak Photoshop łączy ze sobą dwa obrazy, abym mógł odtworzyć te same efekty w mojej aplikacji.

Nathan Moinvaziri
źródło
3
Na pewno nie jest to kwestia ogólnego sprzętu komputerowego lub oprogramowania. Chodzi o algorytmy, C ++ i przetwarzanie obrazu. Tego typu pytania są wyraźnie dozwolone i aktywnie zachęcane
Panagiotis Kanavos
Po 8 latach generowania kliknięć na tę stronę dopiero teraz jest uważana za nie na temat?
Nathan Moinvaziri,
Wystarczy 5 głosów bliskich od osób z co najmniej 3000 rep. To raczej oczywiste, że społeczność SO nie uważa tego pytania za niezwiązane z tematem
Panagiotis Kanavos
PS: 4 głosy przeciw mogą mieć 8 lat. Ktoś mógł napotkać to pytanie w kolejce do zamykania w zeszłym tygodniu i zagłosować za zamknięciem. SO ma teraz tak wiele pytań, że trudno jest już na nie zwracać należytą uwagę. Historia zmian pokazuje, że pierwotne pytanie również wymagało nieco edycji
Panagiotis Kanavos
Być może powrót do wersji 3 lub czegoś podobnego sprawi, że będzie bardziej prawdopodobne, że zostanie ona ponownie otwarta przez recenzentów. W obecnym stanie łatwo jest pomylić pytanie z pytaniem „jak używać Photoshopa”, a także odrzucić je jako nie wykazujące żadnego wysiłku badawczego.
Adam Millerchip

Odpowiedzi:

210

Program Photoshop łączy ze sobą dwa obrazy, wykonując operację mieszania na każdym pikselu obrazu A z odpowiadającym mu pikselem na obrazie B. Każdy piksel to kolor składający się z wielu kanałów. Zakładając, że pracujemy z pikselami RGB, kanały w każdym pikselu byłyby czerwone, zielone i niebieskie. Aby połączyć dwa piksele, łączymy ich odpowiednie kanały.

Operację mieszania wykonywaną dla każdego trybu mieszania w programie Photoshop można podsumować w następujących makrach:

#define ChannelBlend_Normal(A,B)     ((uint8)(A))
#define ChannelBlend_Lighten(A,B)    ((uint8)((B > A) ? B:A))
#define ChannelBlend_Darken(A,B)     ((uint8)((B > A) ? A:B))
#define ChannelBlend_Multiply(A,B)   ((uint8)((A * B) / 255))
#define ChannelBlend_Average(A,B)    ((uint8)((A + B) / 2))
#define ChannelBlend_Add(A,B)        ((uint8)(min(255, (A + B))))
#define ChannelBlend_Subtract(A,B)   ((uint8)((A + B < 255) ? 0:(A + B - 255)))
#define ChannelBlend_Difference(A,B) ((uint8)(abs(A - B)))
#define ChannelBlend_Negation(A,B)   ((uint8)(255 - abs(255 - A - B)))
#define ChannelBlend_Screen(A,B)     ((uint8)(255 - (((255 - A) * (255 - B)) >> 8)))
#define ChannelBlend_Exclusion(A,B)  ((uint8)(A + B - 2 * A * B / 255))
#define ChannelBlend_Overlay(A,B)    ((uint8)((B < 128) ? (2 * A * B / 255):(255 - 2 * (255 - A) * (255 - B) / 255)))
#define ChannelBlend_SoftLight(A,B)  ((uint8)((B < 128)?(2*((A>>1)+64))*((float)B/255):(255-(2*(255-((A>>1)+64))*(float)(255-B)/255))))
#define ChannelBlend_HardLight(A,B)  (ChannelBlend_Overlay(B,A))
#define ChannelBlend_ColorDodge(A,B) ((uint8)((B == 255) ? B:min(255, ((A << 8 ) / (255 - B)))))
#define ChannelBlend_ColorBurn(A,B)  ((uint8)((B == 0) ? B:max(0, (255 - ((255 - A) << 8 ) / B))))
#define ChannelBlend_LinearDodge(A,B)(ChannelBlend_Add(A,B))
#define ChannelBlend_LinearBurn(A,B) (ChannelBlend_Subtract(A,B))
#define ChannelBlend_LinearLight(A,B)((uint8)(B < 128)?ChannelBlend_LinearBurn(A,(2 * B)):ChannelBlend_LinearDodge(A,(2 * (B - 128))))
#define ChannelBlend_VividLight(A,B) ((uint8)(B < 128)?ChannelBlend_ColorBurn(A,(2 * B)):ChannelBlend_ColorDodge(A,(2 * (B - 128))))
#define ChannelBlend_PinLight(A,B)   ((uint8)(B < 128)?ChannelBlend_Darken(A,(2 * B)):ChannelBlend_Lighten(A,(2 * (B - 128))))
#define ChannelBlend_HardMix(A,B)    ((uint8)((ChannelBlend_VividLight(A,B) < 128) ? 0:255))
#define ChannelBlend_Reflect(A,B)    ((uint8)((B == 255) ? B:min(255, (A * A / (255 - B)))))
#define ChannelBlend_Glow(A,B)       (ChannelBlend_Reflect(B,A))
#define ChannelBlend_Phoenix(A,B)    ((uint8)(min(A,B) - max(A,B) + 255))
#define ChannelBlend_Alpha(A,B,O)    ((uint8)(O * A + (1 - O) * B))
#define ChannelBlend_AlphaF(A,B,F,O) (ChannelBlend_Alpha(F(A,B),A,O))

Aby zmieszać pojedynczy piksel RGB, wykonaj następujące czynności:

ImageTColorR = ChannelBlend_Glow(ImageAColorR, ImageBColorR); 
ImageTColorB = ChannelBlend_Glow(ImageAColorB, ImageBColorB);
ImageTColorG = ChannelBlend_Glow(ImageAColorG, ImageBColorG);

ImageTColor = RGB(ImageTColorR, ImageTColorB, ImageTColorG);

Gdybyśmy chcieli wykonać operację mieszania z określonym kryciem, powiedzmy 50%:

ImageTColorR = ChannelBlend_AlphaF(ImageAColorR, ImageBColorR, Blend_Subtract, 0.5F);

Jeśli masz wskaźniki do danych obrazu dla obrazów A, B i T (nasz cel), możemy uprościć mieszanie wszystkich trzech kanałów za pomocą tego makra:

#define ColorBlend_Buffer(T,A,B,M)      (T)[0] = ChannelBlend_##M((A)[0], (B)[0]),
                                        (T)[1] = ChannelBlend_##M((A)[1], (B)[1]),
                                        (T)[2] = ChannelBlend_##M((A)[2], (B)[2])

I może wyprowadzić następujące makra mieszania kolorów RGB:

#define ColorBlend_Normal(T,A,B)        (ColorBlend_Buffer(T,A,B,Normal))
#define ColorBlend_Lighten(T,A,B)       (ColorBlend_Buffer(T,A,B,Lighten))
#define ColorBlend_Darken(T,A,B)        (ColorBlend_Buffer(T,A,B,Darken))
#define ColorBlend_Multiply(T,A,B)      (ColorBlend_Buffer(T,A,B,Multiply))
#define ColorBlend_Average(T,A,B)       (ColorBlend_Buffer(T,A,B,Average))
#define ColorBlend_Add(T,A,B)           (ColorBlend_Buffer(T,A,B,Add))
#define ColorBlend_Subtract(T,A,B)      (ColorBlend_Buffer(T,A,B,Subtract))
#define ColorBlend_Difference(T,A,B)    (ColorBlend_Buffer(T,A,B,Difference))
#define ColorBlend_Negation(T,A,B)      (ColorBlend_Buffer(T,A,B,Negation))
#define ColorBlend_Screen(T,A,B)        (ColorBlend_Buffer(T,A,B,Screen))
#define ColorBlend_Exclusion(T,A,B)     (ColorBlend_Buffer(T,A,B,Exclusion))
#define ColorBlend_Overlay(T,A,B)       (ColorBlend_Buffer(T,A,B,Overlay))
#define ColorBlend_SoftLight(T,A,B)     (ColorBlend_Buffer(T,A,B,SoftLight))
#define ColorBlend_HardLight(T,A,B)     (ColorBlend_Buffer(T,A,B,HardLight))
#define ColorBlend_ColorDodge(T,A,B)    (ColorBlend_Buffer(T,A,B,ColorDodge))
#define ColorBlend_ColorBurn(T,A,B)     (ColorBlend_Buffer(T,A,B,ColorBurn))
#define ColorBlend_LinearDodge(T,A,B)   (ColorBlend_Buffer(T,A,B,LinearDodge))
#define ColorBlend_LinearBurn(T,A,B)    (ColorBlend_Buffer(T,A,B,LinearBurn))
#define ColorBlend_LinearLight(T,A,B)   (ColorBlend_Buffer(T,A,B,LinearLight))
#define ColorBlend_VividLight(T,A,B)    (ColorBlend_Buffer(T,A,B,VividLight))
#define ColorBlend_PinLight(T,A,B)      (ColorBlend_Buffer(T,A,B,PinLight))
#define ColorBlend_HardMix(T,A,B)       (ColorBlend_Buffer(T,A,B,HardMix))
#define ColorBlend_Reflect(T,A,B)       (ColorBlend_Buffer(T,A,B,Reflect))
#define ColorBlend_Glow(T,A,B)          (ColorBlend_Buffer(T,A,B,Glow))
#define ColorBlend_Phoenix(T,A,B)       (ColorBlend_Buffer(T,A,B,Phoenix))

Przykładem może być:

ColorBlend_Glow(TargetPtr, ImageAPtr, ImageBPtr);

Pozostałe tryby mieszania Photoshopa obejmują konwersję RGB na HLS iz powrotem.

#define ColorBlend_Hue(T,A,B)            ColorBlend_Hls(T,A,B,HueB,LuminationA,SaturationA)
#define ColorBlend_Saturation(T,A,B)     ColorBlend_Hls(T,A,B,HueA,LuminationA,SaturationB)
#define ColorBlend_Color(T,A,B)          ColorBlend_Hls(T,A,B,HueB,LuminationA,SaturationB)
#define ColorBlend_Luminosity(T,A,B)     ColorBlend_Hls(T,A,B,HueA,LuminationB,SaturationA)

#define ColorBlend_Hls(T,A,B,O1,O2,O3) {
    float64 HueA, LuminationA, SaturationA;
    float64 HueB, LuminationB, SaturationL;
    Color_RgbToHls((A)[2],(A)[1],(A)[0], &HueA, &LuminationA, &SaturationA);
    Color_RgbToHls((B)[2],(B)[1],(B)[0], &HueB, &LuminationB, &SaturationB);
    Color_HlsToRgb(O1,O2,O3,&(T)[2],&(T)[1],&(T)[0]);
    }

Te funkcje będą pomocne przy konwersji RGB na HLS.

int32 Color_HueToRgb(float64 M1, float64 M2, float64 Hue, float64 *Channel)
{
    if (Hue < 0.0)
        Hue += 1.0;
    else if (Hue > 1.0)
        Hue -= 1.0;

    if ((6.0 * Hue) < 1.0)
        *Channel = (M1 + (M2 - M1) * Hue * 6.0);
    else if ((2.0 * Hue) < 1.0)
        *Channel = (M2);
    else if ((3.0 * Hue) < 2.0)
        *Channel = (M1 + (M2 - M1) * ((2.0F / 3.0F) - Hue) * 6.0);
    else
        *Channel = (M1);

    return TRUE;
}

int32 Color_RgbToHls(uint8 Red, uint8 Green, uint8 Blue, float64 *Hue, float64 *Lumination, float64 *Saturation)
{
    float64 Delta;
    float64 Max, Min;
    float64 Redf, Greenf, Bluef;

    Redf    = ((float64)Red   / 255.0F);
    Greenf  = ((float64)Green / 255.0F);
    Bluef   = ((float64)Blue  / 255.0F); 

    Max     = max(max(Redf, Greenf), Bluef);
    Min     = min(min(Redf, Greenf), Bluef);

    *Hue        = 0;
    *Lumination = (Max + Min) / 2.0F;
    *Saturation = 0;

    if (Max == Min)
        return TRUE;

    Delta = (Max - Min);

    if (*Lumination < 0.5)
        *Saturation = Delta / (Max + Min);
    else
        *Saturation = Delta / (2.0 - Max - Min);

    if (Redf == Max)
        *Hue = (Greenf - Bluef) / Delta;
    else if (Greenf == Max)
        *Hue = 2.0 + (Bluef - Redf) / Delta;
    else
        *Hue = 4.0 + (Redf - Greenf) / Delta;

    *Hue /= 6.0; 

    if (*Hue < 0.0)
        *Hue += 1.0;       

    return TRUE;
}

int32 Color_HlsToRgb(float64 Hue, float64 Lumination, float64 Saturation, uint8 *Red, uint8 *Green, uint8 *Blue)
{
    float64 M1, M2;
    float64 Redf, Greenf, Bluef;

    if (Saturation == 0)
        {
        Redf    = Lumination;
        Greenf  = Lumination;
        Bluef   = Lumination;
        }
    else
        {
        if (Lumination <= 0.5)
            M2 = Lumination * (1.0 + Saturation);
        else
            M2 = Lumination + Saturation - Lumination * Saturation;

        M1 = (2.0 * Lumination - M2);

        Color_HueToRgb(M1, M2, Hue + (1.0F / 3.0F), &Redf);
        Color_HueToRgb(M1, M2, Hue, &Greenf);
        Color_HueToRgb(M1, M2, Hue - (1.0F / 3.0F), &Bluef);
        }

    *Red    = (uint8)(Redf * 255);
    *Blue   = (uint8)(Bluef * 255);
    *Green  = (uint8)(Greenf * 255);

    return TRUE;
}

Zasobów na ten temat jest więcej, głównie:

  1. Tryby mieszania PegTop
  2. Forensic Photoshop
  3. Wgląd w tryby mieszania programu Photoshop 7.0
  4. SF - Podstawy - Tryby mieszania
  5. zakończyć tryby mieszania
  6. Blog Romz
  7. Funkcje konwersji ReactOS RGB-HLS
Nathan Moinvaziri
źródło
1
tak, świetna odpowiedź, dzięki! Zastanawiałem się, czy ktoś wie, jak krycie warstwy jest wykonywane w Photoshopie? to znaczy chcę użyć funkcji mieszanka przyciemnić ale tylko 50% ... Sprawdziłem wartości w photoshopie i nie wydaje się, że jest to wystarczające, aby przyjąć tylko 50% wartości obrazu blend ...
Maecky
2
Podana formuła alfa nie jest kompletna - działa tylko w przypadku, gdy tło jest całkowicie nieprzezroczyste. Jeśli tło jest przezroczyste, wynik po narysowaniu może być przezroczysty. W bardziej ogólnym przypadku musisz zastosować mieszanie alfa, takie jak opisane w Wikipedii .
thenickdude
2
A co z kanałem alfa? Czy powinienem również zastosować do niego funkcje?
akhy
Świetna odpowiedź!! Odbicie i Blask są przełączane. W przeciwnym razie: Świetnie !! (Chociaż brakuje alpha-compositing ..)
TaW
rozwiązanie c # do obliczeń alfa: static Color alphaComposite (Color c1, Color c2, Color cb, float op) {float a1, a2, ab, ar = 1; ar = v [c1.A] + v [c2.A] * op - (v [c1.A] * v [c2.A] * op); float asr = v [c2.A] * op / ar; a1 = 1 - asr; a2 = asr * (1 - v [c1.A]); ab = asr * v [c1.A]; bajt r = (bajt) (c1.R * a1 + c2.R * a2 + cb.R * ab); bajt g = (bajt) (c1.G * a1 + c2.G * a2 + cb.G * ab); bajt b = (bajt) (c1.B * a1 + c2.B * a2 + cb.B * ab); return Color.FromArgb ((bajt) (ar * 255), r, g, b); }
TaW
7

Tryby mieszania Barwa, Kolor, Nasycenie w tej odpowiedzi są nieprawidłowe. Żaden produkt Adobe nie konwertuje do HSB, wykonują operacje bezpośrednio na wartościach RGB.

Oto GLSL do ustawiania jasności, na przykład:

float lum(vec4 color)
{
    return ((0.3 * color.r) + (0.59 * color.g) + (0.11 * color.b));
}

vec4 clipColor(vec4 color)
{
    vec4 newColor=color;
    float l=lum(color);
    float n=min(min(color.r,color.g),color.b);
    float x=max(max(color.r,color.g),color.b);

    newColor.r=(n<0.0) ? l+(((color.r-l)*l)/(l-n)) : color.r;
    newColor.r=(x>1.0) ? l+(((color.r-l)*(1.0-l))/(x-l)) : color.r;

    newColor.g=(n<0.0) ? l+(((color.g-l)*l)/(l-n)) : color.g;
    newColor.g=(x>1.0) ? l+(((color.g-l)*(1.0-l))/(x-l)) : color.g;

    newColor.b=(n<0.0) ? l+(((color.b-l)*l)/(l-n)) : color.b;
    newColor.b=(x>1.0) ? l+(((color.b-l)*(1.0-l))/(x-l)) : color.b;

    return clamp(newColor,0.0,1.0);
}

vec4 setlum(vec4 color, float l)
{
    float d=l-lum(color);
    color.r+=d;
    color.g+=d;
    color.b+=d;

    return clipColor(color);    
}

kernel vec4 blendLuminosity(sampler topimage, sampler bottomimage)
{
    vec4 base=sample(bottomimage, samplerCoord(bottomimage));
    vec4 blend=sample(topimage, samplerCoord(topimage));

    float bl=lum(blend);
    return setlum(base,bl);
}

Brak obsługi instrukcji if .. else w CIKernels, stąd użycie operatorów trójskładnikowych.

Jon Gilkison
źródło
4

Popularna odpowiedź jest w 99,9% poprawna, ale jak powiedział Greyfriars, nie uzyska dokładnego wyniku, ponieważ Adobe nie używa HLS w żadnym momencie podczas mieszania.

Ale nie musisz pracować w Adobe, aby to zrobić ... możesz osiągnąć dokładnie to samo mieszanie, przestrzegając wszystkich zasad tutaj w tym dokumencie od Adobe:

zasadniczo rozdziały 4 i 7: http://partners.adobe.com/public/developer/en/pdf/PDFReference.pdf

Wtedy osiągniesz dokładny wynik, tak jak robi to Adobe! Pixel by Pixel!

Wagner Patriota
źródło
i.imgur.com/G5MbHOH.png mówi, że w linku jest używany HSL (poszedłem tutaj, ponieważ ten link był dla mnie uszkodzony: adobe.com/content/dam/acom/en/devnet/pdf/pdfs/ ... )
eri0o
0

Chociaż popularna odpowiedź jest w większości poprawna, poniższe stwierdzenie jest błędne. „Pozostałe tryby mieszania w Photoshopie obejmują konwersję RGB na HLS iz powrotem”. Nie, Photoshop (i tylko Photoshop) używa Chroma i Luma zamiast HLS.

Dlatego w przypadku trybów Barwa, Kolor, Jasność i Nasycenie nie można używać prostych algorytmów. Aby dopasować metodę Photoshopa w takich przypadkach, musisz pracować dla Adobe.

Greyfriars Bobby
źródło