RGB値の加法混色を行うアルゴリズムを探しています。
RGB値を最大256まで加算するのと同じくらい簡単ですか?
(r1, g1, b1) + (r2, g2, b2) =
(min(r1+r2, 256), min(g1+g2, 256), min(b1+b2, 256))
それはあなたが望むものに依存し、異なる方法の結果が何であるかを見るのに役立ちます。
お望みならば
赤+黒=赤 赤+緑=黄 赤+緑+青=白 赤+白=白 黒+白=白
クランプで追加すると動作します(例:min(r1 + r2, 255)
)これは、参照したライトモデルに似ています。
お望みならば
赤+黒=濃い赤 赤+緑=濃い黄色 赤+緑+青=濃い灰色 赤+白=ピンク 黒+白=グレー
次に、値を平均する必要があります(例:(r1 + r2) / 2
)これは、色を明るくしたり暗くしたり、グラデーションを作成したりするのに適しています。
アルファチャネルを使用してブレンドするには、次の式を使用できます。
r = new Color();
r.A = 1 - (1 - fg.A) * (1 - bg.A);
if (r.A < 1.0e-6) return r; // Fully transparent -- R,G,B not important
r.R = fg.R * fg.A / r.A + bg.R * bg.A * (1 - fg.A) / r.A;
r.G = fg.G * fg.A / r.A + bg.G * bg.A * (1 - fg.A) / r.A;
r.B = fg.B * fg.A / r.A + bg.B * bg.A * (1 - fg.A) / r.A;
fg
はペイントの色です。 bg
は背景です。 r
は結果の色です。 1.0e-6
は、丸め誤差を補正するための非常に小さな数字です。
注:ここで使用されるすべての変数は、範囲[0.0、1.0]にあります。 [0、255]の範囲の値を使用する場合は、255で除算または乗算する必要があります。
たとえば、50%の緑の上に50%の赤:
// background, 50% green
var bg = new Color { R = 0.00, G = 1.00, B = 0.00, A = 0.50 };
// Paint, 50% red
var fg = new Color { R = 1.00, G = 0.00, B = 0.00, A = 0.50 };
// The result
var r = new Color();
r.A = 1 - (1 - fg.A) * (1 - bg.A); // 0.75
r.R = fg.R * fg.A / r.A + bg.R * bg.A * (1 - fg.A) / r.A; // 0.67
r.G = fg.G * fg.A / r.A + bg.G * bg.A * (1 - fg.A) / r.A; // 0.33
r.B = fg.B * fg.A / r.A + bg.B * bg.A * (1 - fg.A) / r.A; // 0.00
結果の色は:(0.67, 0.33, 0.00, 0.75)
、または75%ブラウン(またはダークオレンジ)。
これらの式を逆にすることもできます。
var bg = new Color();
if (1 - fg.A <= 1.0e-6) return null; // No result -- 'fg' is fully opaque
if (r.A - fg.A < -1.0e-6) return null; // No result -- 'fg' can't make the result more transparent
if (r.A - fg.A < 1.0e-6) return bg; // Fully transparent -- R,G,B not important
bg.A = 1 - (1 - r.A) / (1 - fg.A);
bg.R = (r.R * r.A - fg.R * fg.A) / (bg.A * (1 - fg.A));
bg.G = (r.G * r.A - fg.G * fg.A) / (bg.A * (1 - fg.A));
bg.B = (r.B * r.A - fg.B * fg.A) / (bg.A * (1 - fg.A));
または
var fg = new Color();
if (1 - bg.A <= 1.0e-6) return null; // No result -- 'bg' is fully opaque
if (r.A - bg.A < -1.0e-6) return null; // No result -- 'bg' can't make the result more transparent
if (r.A - bg.A < 1.0e-6) return bg; // Fully transparent -- R,G,B not important
fg.A = 1 - (1 - r.A) / (1 - bg.A);
fg.R = (r.R * r.A - bg.R * bg.A * (1 - fg.A)) / fg.A;
fg.G = (r.G * r.A - bg.G * bg.A * (1 - fg.A)) / fg.A;
fg.B = (r.B * r.A - bg.B * bg.A * (1 - fg.A)) / fg.A;
数式は、指定された結果の色を生成するために背景色またはペイント色でなければならないことを計算します。
背景が不透明な場合、結果も不透明になります。前景色は、異なるアルファ値を持つ値の範囲を取ることができます。各チャネル(赤、緑、青)について、有効な値(0-1)になるアルファの範囲を確認する必要があります。
楽しい事実:コンピューターのRGB値は、光子束の平方根から導出されます。したがって、一般的な関数として、数学はそれを考慮に入れる必要があります。特定のチャネルに対するこれの一般的な機能は次のとおりです。
blendColorValue(a, b, t)
return sqrt((1 - t) * a^2 + t * b^2)
ここで、aとbはブレンドする色で、tは0〜1の数値で、aとbの間のブレンドのポイントを表します。
アルファチャネルは異なります。それは光子の強度を表すのではなく、透けて見えるはずの背景の割合だけを表します。したがって、アルファ値をブレンドする場合、線形平均で十分です。
blendAlphaValue(a, b, t)
return (1-t)*a + t*b;
したがって、これらの2つの関数を使用して2つの色のブレンドを処理するには、次の擬似コードが役立ちます。
blendColors(c1, c2, t)
ret
[r, g, b].each n ->
ret[n] = blendColorValue(c1[n], c2[n], t)
ret.alpha = blendAlphaValue(c1.alpha, c2.alpha, t)
return ret
ちなみに、私はプログラミング言語とキーボードの両方を望みます。両方とも(またはそれ以上の)数学をきれいに表現でき(上線付きユニコード文字の組み合わせは上付き文字、記号、および他の膨大な文字の配列では機能しません)、それを正しく解釈します。 sqrt((1-t)* pow(a、2)+ t * pow(b、2))は、単にクリーンとして読み取られません。
いくつかのポイント:
これにより以下が得られます。
(r1、g1、b1)+(r2、g2、b2)=(min(r1 + r2、255)、min(g1 + g2、255)、min(b1 + b2、255))
ただし、色を混合する「自然な」方法は平均を使用することであり、その後、最小値は必要ありません。
(r1、g1、b1)+(r2、g2、b2)=((r1 + r2)/ 2、(g1 + g2)/ 2、(b1 + b2)/ 2)
c1、c2および結果-JSONのようなc1 = {r:0.5、g:1、b:0、a:0.33}
var rgbaSum = function(c1, c2){
var a = c1.a + c2.a*(1-c1.a);
return {
r: (c1.r * c1.a + c2.r * c2.a * (1 - c1.a)) / a,
g: (c1.g * c1.a + c2.g * c2.a * (1 - c1.a)) / a,
b: (c1.b * c1.a + c2.b * c2.a * (1 - c1.a)) / a,
a: a
}
}
[〜#〜] python [〜#〜][〜#〜] colour [〜#〜][〜#〜] mixing [〜#〜][〜#〜 ] through [〜#〜][〜#〜] addition [〜#〜][〜#〜] in [〜#〜][〜#〜] cmyk [〜#〜][〜#〜] space [〜#〜]
これを行う1つの方法は、まず色を [〜#〜] cmyk [〜#〜] 形式に変換し、そこに追加してからRGBに再変換することです。
Pythonのサンプルコードを次に示します。
rgb_scale = 255
cmyk_scale = 100
def rgb_to_cmyk(self,r,g,b):
if (r == 0) and (g == 0) and (b == 0):
# black
return 0, 0, 0, cmyk_scale
# rgb [0,255] -> cmy [0,1]
c = 1 - r / float(rgb_scale)
m = 1 - g / float(rgb_scale)
y = 1 - b / float(rgb_scale)
# extract out k [0,1]
min_cmy = min(c, m, y)
c = (c - min_cmy)
m = (m - min_cmy)
y = (y - min_cmy)
k = min_cmy
# rescale to the range [0,cmyk_scale]
return c*cmyk_scale, m*cmyk_scale, y*cmyk_scale, k*cmyk_scale
def cmyk_to_rgb(self,c,m,y,k):
"""
"""
r = rgb_scale*(1.0-(c+k)/float(cmyk_scale))
g = rgb_scale*(1.0-(m+k)/float(cmyk_scale))
b = rgb_scale*(1.0-(y+k)/float(cmyk_scale))
return r,g,b
def ink_add_for_rgb(self,list_of_colours):
"""input: list of rgb, opacity (r,g,b,o) colours to be added, o acts as weights.
output (r,g,b)
"""
C = 0
M = 0
Y = 0
K = 0
for (r,g,b,o) in list_of_colours:
c,m,y,k = rgb_to_cmyk(r, g, b)
C+= o*c
M+=o*m
Y+=o*y
K+=o*k
return cmyk_to_rgb(C, M, Y, K)
質問の結果は次のようになります(2色の半分の混合を仮定します:
r_mix, g_mix, b_mix = ink_add_for_rgb([(r1,g1,b1,0.5),(r2,g2,b2,0.5)])
ここで0.5は、最初の色の50%と2番目の色の50%を混合するという意味です。
はい、それはそれと同じくらい簡単です。別のオプションは、平均を見つけることです(グラデーションを作成するため)。
それは本当にあなたが達成したい効果に依存します。
ただし、アルファが追加されると、複雑になります。アルファを使用してブレンドするには、さまざまな方法があります。
単純なアルファブレンディングの例: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
ここに来たとき、実際に探していた「加法混色」アルゴリズムが見つかりませんでした。これはPhotoshopでも利用可能で、 "Screen"on Wikipedia 。 (別名「明るくする」または「乗算を逆にする」。)2つの光源を組み合わせた場合と同様の結果が得られます。
スクリーンブレンドモードでは、2つのレイヤーのピクセルの値が反転され、乗算され、再び反転されます。これにより、逆の効果が得られます。その結果、明るい画像が得られます。
ここにあります:
// (rgb values are 0-255)
function screen(color1, color2) {
var r = Math.round((1 - (1 - color1.R / 255) * (1 - color2.R / 255)) * 255);
var g = Math.round((1 - (1 - color1.G / 255) * (1 - color2.G / 255)) * 255);
var b = Math.round((1 - (1 - color1.B / 255) * (1 - color2.B / 255)) * 255);
return new Color(r, g, b);
}
C++を使用して @ Markus Jarderot のsRGB
ブレンド応答(これはデフォルトのレガシーであるためガンマ補正されない)のようなものを記述/使用しました。
//same as Markus Jarderot's answer
float red, green, blue;
alpha = (1.0 - (1.0 - back.alpha)*(1.0 - front.alpha));
red = (front.red * front.alpha / alpha + back.red * back.alpha * (1.0 - front.alpha));
green = (front.green * front.alpha / alpha + back.green * back.alpha * (1.0 - front.alpha));
blue = (front.blue * front.alpha / alpha + back.blue * back.alpha * (1.0 - front.alpha));
//faster but equal output
alpha = (1.0 - (1.0 - back.alpha)*(1.0 - front.alpha));
red = (back.red * (1.0 - front.alpha) + front.red * front.alpha);
green = (back.green * (1.0 - front.alpha) + front.green * front.alpha);
blue = (back.blue * (1.0 - front.alpha) + front.blue * front.alpha);
//even faster but only works when all values are in range 0 to 255
int red, green, blue;
alpha = (255 - (255 - back.alpha)*(255 - front.alpha));
red = (back.red * (255 - front.alpha) + front.red * front.alpha) / 255;
green = (back.green * (255 - front.alpha) + front.green * front.alpha) / 255;
blue = (back.blue * (255 - front.alpha) + front.blue * front.alpha) / 255;
ここでは、高度に最適化されたスタンドアロンc ++クラス、パブリックドメイン、浮動小数点、および関数とマクロの両方の形式で最適化された2つの8ビットブレンドメカニズム、および手元の問題とその方法、および重要性の両方の技術的な説明を示しますこの問題の最適化:
Markus Jarderot、Andras Zoltan、hkurabkoに感謝します。 RGBイメージのリストをブレンドするためのPythonコードです。
Markus Jarderotのコードを使用して、RGBAカラーを生成できます。次に、 Andras Zoltan and hkurabko's メソッドを使用して、RGBAをRGBに変換します。
ありがとうございました!
import numpy as np
def Blend2Color(C1,C2):
c1,c1a=C1
c2,c2a=C2
A = 1 - (1 - c1a) * (1 - c2a);
if (A < 1.0e-6):
return (0,0,0) #Fully transparent -- R,G,B not important
Result=(np.array(c1)*c1a+np.array(c2)*c2a*(1-c1a))/A
return Result,A
def RGBA2RGB(RGBA,BackGround=(1,1,1)):# whilt background
A=RGBA[-1]
RGB=np.add(np.multiply(np.array(RGBA[:-1]),A),
np.multiply(np.array(BackGround),1-A))
return RGB
def BlendRGBList(Clist,AlphaList=None,NFloat=2,ReturnRGB=True,
RGB_BackGround=(1,1,1)):
N=len(Clist)
if AlphaList==None:
ClistUse=Clist.copy()
else:
if len(AlphaList)==N:
AlphaListUse=np.multiply(AlphaList,10**NFloat).astype(int)
ClistUse=np.repeat(np.array(Clist), AlphaListUse, axis=0)
else:
raise('len of AlphaList must equal to len of Clist!')
while N!=1:
temp=ClistUse.copy()
ClistUse=[]
for C in temp[:-1]:
c1,a1=C
c2,a2=temp[-1]
ClistUse.append(Blend2Color(C1=(c1,a1*(1-1/N)),C2=(c2,a2*1/N)))
N=len(ClistUse)
Result=np.append(ClistUse[0][0],ClistUse[0][1])
if ReturnRGB:
Result=RGBA2RGB(Result,BackGround=RGB_BackGround)
return Result
テスト
BlendRGBList([[(1,0,0),1],[(0,1,0),1]],ReturnRGB=True)
#array([0.75, 0.5 , 0.25])
BlendRGBList([[(1,0,0),1],[(0,1,0),1]],ReturnRGB=False)
#array([0.66666667, 0.33333333, 0. , 0.75 ])