C++画像処理--画像カラーブレンド(下)
9696 ワード
ヒントを読む:
「C++画像処理」シリーズはコードがはっきりしていて、可読性が主で、すべてC++コードを使用しています.
「Delphi画像処理」シリーズは効率に重点を置き、一般コードはPASCAL、コアコードはBASMを採用している.
できるだけ両者の内容を一致させ、互いに対照することができる.
本明細書のコードは、「C++画像処理--データ型および共通関数」の文書中のBmpDataを含む必要がある.hヘッダファイル.
『C++画像処理--画像色混合(上)』と『C++画像処理--画像色混合(中)』の2つの文章でPhotoshopカラーレイヤー混合モードの基本機能を実現し、本稿ではこれに基づいてAlphaチャネル付きカラーレイヤー混合モードをさらに実現する予定である.このようにPhotoshopの色画層混合モードは比較的完全に実現されていると言える.
Alphaチャネル付き32ビットカラーレイヤブレンドモードを実現するには、24ビットカラーレイヤブレンドモードよりも複雑であり、後者は前者の特例にすぎない.
次に、Alphaチャネル付き32ビットカラーレイヤブレンドモードのすべてのコードを示します.
『C++画像処理−画像色混合(中)』のコードを比較すると,色混合の基本機能コードは変わっていないが,階調画像染色関数ImageTintと色混合関数ImageColorMixerを修正しただけで,元のImageColorMixer関数はRgbColorMixerと改称され,新しいImageColorMixer関数の一部となった.また、新しいImageColorMixer関数には、0~1の範囲で上位画像の不透明度を変更するためのalphaパラメータも追加されました.
本稿では、サンプルコードを貼るつもりはなく、以下のpngピクチャの色合成と染色のテスト効果図をいくつか示します.
ソース図一(りんご)ソース図二(雪)
上の列は雪花図が下層で、リンゴ図は上層で実現された色の混合で、左から右にかけて、Alphaはそれぞれ100%、80%、50%である.
次の列はアップル図が下層で、雪図が上層で実現された色の混合で、左から右にかけて、Alphaはそれぞれ100%、80%、50%である.
左から右へ:雪図100%Alpha赤色染色、雪図50%Alpha赤色染色、リンゴ図100%Alpha青色染色.
なお、Photoshopのレイヤーブレンドオプションでは、不透明度に加えて、充填数オプションがありますが、両者の違いは、不透明度オプションとは、レイヤー自体にとって、充填数オプションはブレンド時の本レイヤーピクセルの充填割合です.しかし、実際には両者は本質的に同じことであり、つまり両者の積が最終的な充填不透明度である.
本人のレベルが限られているため、改善されたが、間違いは避けられない.貴重な意見を提出することを歓迎します.メールアドレス:[email protected]
レベルが限られているため、間違いは避けられないので、指摘と指導を歓迎します.メールアドレス:[email protected]
ここでは『C++画像処理--記事インデックス』にアクセスできます.
「C++画像処理」シリーズはコードがはっきりしていて、可読性が主で、すべてC++コードを使用しています.
「Delphi画像処理」シリーズは効率に重点を置き、一般コードはPASCAL、コアコードはBASMを採用している.
できるだけ両者の内容を一致させ、互いに対照することができる.
本明細書のコードは、「C++画像処理--データ型および共通関数」の文書中のBmpDataを含む必要がある.hヘッダファイル.
『C++画像処理--画像色混合(上)』と『C++画像処理--画像色混合(中)』の2つの文章でPhotoshopカラーレイヤー混合モードの基本機能を実現し、本稿ではこれに基づいてAlphaチャネル付きカラーレイヤー混合モードをさらに実現する予定である.このようにPhotoshopの色画層混合モードは比較的完全に実現されていると言える.
Alphaチャネル付き32ビットカラーレイヤブレンドモードを実現するには、24ビットカラーレイヤブレンドモードよりも複雑であり、後者は前者の特例にすぎない.
次に、Alphaチャネル付き32ビットカラーレイヤブレンドモードのすべてのコードを示します.
//---------------------------------------------------------------------------
typedef FLOAT BWParams, *PBWParams;
// : , , , , ,
CONST INT _BWDefault[] = {410, 614, 410, 819, 205, 614};
enum
{
BWIndexBlue = 0x40000,
BWIndexGreen = 0x20000,
BWIndexRed = 0x00000
};
enum
{
IndexBlue = 0x00000,
IndexGreen = 0x10000,
IndexRed = 0x20000
};
typedef union //
{
INT tmp; //
struct
{
SHORT value; //
SHORT index; //
};
}RGBIndex;
//---------------------------------------------------------------------------
//
FORCEINLINE
VOID SwapRgb(RGBIndex &a, RGBIndex &b)
{
a.tmp ^= b.tmp;
b.tmp ^= a.tmp;
a.tmp ^= b.tmp;
}
//---------------------------------------------------------------------------
//
FORCEINLINE
INT GetBWGray(CONST PARGBQuad pixel, CONST PINT bwParams)
{
RGBIndex max, mid, min;
min.tmp = pixel->Blue | BWIndexBlue;
mid.tmp = pixel->Green | BWIndexGreen;
max.tmp = pixel->Red | BWIndexRed;
if (max.value < mid.value)
SwapRgb(max, mid);
if (max.value < min.value)
SwapRgb(max, min);
if (min.value > mid.value)
SwapRgb(min, mid);
return (((max.value - mid.value) * bwParams[max.index] +
(mid.value - min.value) * bwParams[max.index + mid.index - 1] +
512) >> 10) + min.value;
}
//---------------------------------------------------------------------------
VOID ColorMix(PARGBQuad pd, CONST PARGBQuad ps, INT gray)
{
// : , 、
CONST INT ys[3] = {113, 604, 307};
RGBIndex max, mid, min;
min.tmp = ps->Blue | IndexBlue;
mid.tmp = ps->Green | IndexGreen;
max.tmp = ps->Red | IndexRed;
if (max.value < mid.value)
SwapRgb(max, mid);
if (max.value < min.value)
SwapRgb(max, min);
if (min.value > mid.value)
SwapRgb(min, mid);
INT max_min = max.value - min.value;
// 0,
if (max_min == 0)
{
pd->Blue = pd->Green = pd->Red = gray;
return;
}
INT mid_min = mid.value - min.value;
INT newMax, newMid, newMin;
gray <<= 10;
newMax = (gray + (max_min - mid_min) * ys[mid.index] + max_min * ys[min.index] + 512) >> 10;
newMin = newMax - max_min;
if (newMax > 255)
{
INT hueCoef = (mid_min << 10) / max_min;
INT v0 = (ys[mid.index] * hueCoef) >> 10;
INT v1 = ys[min.index] + ys[mid.index] - v0;
newMin = (gray - (ys[max.index] + v0) * 255 + (v1 >> 1)) / v1;
newMid = newMin + (((255 ^ newMin) * hueCoef + 512) >> 10);
newMax = 255;
}
else if (newMin < 0)
{
INT hueCoef = (mid_min << 10) / max_min;
INT tmp = ys[max.index] + ((ys[mid.index] * hueCoef + 512) >> 10);
newMax = (gray + (tmp >> 1)) / tmp;
newMid = (newMax * hueCoef + 512) >> 10;
newMin = 1;
}
else
newMid = newMin + mid_min;
((LPBYTE)pd)[max.index] = newMax;
((LPBYTE)pd)[mid.index] = newMid;
((LPBYTE)pd)[min.index] = newMin;
}
//---------------------------------------------------------------------------
// 。
// bwParams 6 , , , , , ,
VOID ImageBlackWhite(BitmapData *data, CONST PBWParams bwParams = NULL)
{
// ,
INT params[6], *pparams;
if (bwParams)
{
for (INT i = 0; i < 6; i ++)
params[i] = (INT)(bwParams[i] * 1024 + 0.5);
params[3] ^= params[5];
params[5] ^= params[3];
params[3] ^= params[5];
pparams = params;
}
else
pparams = (INT*)_BWDefault;
PARGBQuad p = (PARGBQuad)data->Scan0;
INT dataOffset = (data->Stride >> 2) - (INT)data->Width;
for (UINT y = 0; y < data->Height; y ++, p += dataOffset)
{
for (UINT x = 0; x < data->Width; x ++, p ++)
{
INT gray = GetBWGray(p, pparams);
p->Blue = p->Green = p->Red =
(gray & ~0xff) == 0? gray : gray > 255? 255 : 0;
}
}
}
//---------------------------------------------------------------------------
// 。
VOID ImageTint(BitmapData *grayData, ARGB color)
{
ARGBQuad colorTable[256];
PARGBQuad p = colorTable;
ARGB alpha = color >> 24;
if (alpha == 0) return;
for (INT i = 0; i < 256; i ++, p ++)
{
ColorMix(p, (PARGBQuad)&color, i);
p->Alpha = 0;
}
if (alpha < 255)
{
p = colorTable;
for (INT i = 0; i < 256; i ++, p ++)
{
p->Blue = i + ((p->Blue - i) * alpha + 127) / 255;
p->Green = i + ((p->Green - i) * alpha + 127) / 255;
p->Red = i + ((p->Red - i) * alpha + 127) / 255;
}
}
p = (PARGBQuad)grayData->Scan0;
INT dataOffset = (grayData->Stride >> 2) - (INT)grayData->Width;
for (UINT y = 0; y < grayData->Height; y ++, p += dataOffset)
{
for (UINT x = 0; x < grayData->Width; x ++, p ++)
{
p->Color = (p->Color & 0xff000000) | colorTable[p->Blue].Color;
}
}
}
//---------------------------------------------------------------------------
VOID RgbColorMixer(BitmapData *dest, CONST BitmapData *source)
{
PARGBQuad pd, ps;
UINT width, height;
INT dstOffset, srcOffset;
GetDataCopyParams(dest, source, width, height, pd, ps, dstOffset, srcOffset);
for (UINT y = 0; y < height; y ++, pd += dstOffset, ps += srcOffset)
{
for (UINT x = 0; x < width; x ++, pd ++, ps ++)
{
ColorMix(pd, ps, GetBWGray(pd, (PINT)_BWDefault));
}
}
}
//---------------------------------------------------------------------------
VOID ArgbColorMixer(BitmapData *dest, CONST BitmapData *source, INT alpha)
{
PARGBQuad pd, ps;
UINT width, height;
INT dstOffset, srcOffset;
GetDataCopyParams(dest, source, width, height, pd, ps, dstOffset, srcOffset);
for (UINT y = 0; y < height; y ++, pd += dstOffset, ps += srcOffset)
{
for (UINT x = 0; x < width; x ++, pd ++, ps ++)
{
INT a = (ps->Alpha * alpha + 127) / 255;
if (a)
{
ARGBQuad c;
ColorMix(&c, ps, GetBWGray(pd, (PINT)_BWDefault));
pd->Red = pd->Red + (((c.Red - pd->Red) * a + 127) / 255);
pd->Green = pd->Green + (((c.Green - pd->Green) * a + 127) / 255);
pd->Blue = pd->Blue + (((c.Blue - pd->Blue) * a + 127) / 255);
}
}
}
}
//---------------------------------------------------------------------------
FORCEINLINE
VOID PArgbMixer(PARGBQuad pd, CONST PARGBQuad ps, INT alpha)
{
pd->Blue = (pd->Blue * pd->Alpha + 127) / 255;
pd->Green = (pd->Green * pd->Alpha + 127) / 255;
pd->Red = (pd->Red * pd->Alpha + 127) / 255;
pd->Blue += (((ps->Blue - pd->Blue) * alpha + 127) / 255);
pd->Green += (((ps->Green - pd->Green) * alpha + 127) / 255);
pd->Red += (((ps->Red - pd->Red) * alpha + 127) / 255);
pd->Alpha += (alpha - (pd->Alpha * alpha + 127) / 255);
pd->Blue = pd->Blue * 255 / pd->Alpha;
pd->Green = pd->Green * 255 / pd->Alpha;
pd->Red = pd->Red * 255 / pd->Alpha;
}
//---------------------------------------------------------------------------
VOID PArgbColorMixer(BitmapData *dest, CONST BitmapData *source, INT alpha)
{
PARGBQuad pd, ps;
UINT width, height;
INT dstOffset, srcOffset;
GetDataCopyParams(dest, source, width, height, pd, ps, dstOffset, srcOffset);
for (UINT y = 0; y < height; y ++, pd += dstOffset, ps += srcOffset)
{
for (UINT x = 0; x < width; x ++, pd ++, ps ++)
{
INT a = (ps->Alpha * alpha + 127) / 255;
if (a)
{
if (pd->Alpha == 0) *pd = *ps;
else
{
ARGBQuad c, d;
c.Color = d.Color = pd->Color;
pd->Color = ps->Color;
// c
ColorMix(&c, ps, GetBWGray(&d, (PINT)_BWDefault));
// c a
PArgbMixer(&d, &c, a);
// ,
PArgbMixer(pd, &d, c.Alpha);
}
}
}
}
}
//---------------------------------------------------------------------------
//
VOID ImageColorMixer(BitmapData *dest, CONST BitmapData *source, FLOAT alpha = 1)
{
INT a = (INT)(alpha * 255);
if (a <= 0) return;
if (a > 255) a = 255;
if (a == 255 && !HasAlphaFlag(dest) && !HasAlphaFlag(source))
RgbColorMixer(dest, source);
else if (!HasAlphaFlag(dest))
ArgbColorMixer(dest, source, a);
else
PArgbColorMixer(dest, source, a);
}
//---------------------------------------------------------------------------
『C++画像処理−画像色混合(中)』のコードを比較すると,色混合の基本機能コードは変わっていないが,階調画像染色関数ImageTintと色混合関数ImageColorMixerを修正しただけで,元のImageColorMixer関数はRgbColorMixerと改称され,新しいImageColorMixer関数の一部となった.また、新しいImageColorMixer関数には、0~1の範囲で上位画像の不透明度を変更するためのalphaパラメータも追加されました.
本稿では、サンプルコードを貼るつもりはなく、以下のpngピクチャの色合成と染色のテスト効果図をいくつか示します.
ソース図一(りんご)ソース図二(雪)
上の列は雪花図が下層で、リンゴ図は上層で実現された色の混合で、左から右にかけて、Alphaはそれぞれ100%、80%、50%である.
次の列はアップル図が下層で、雪図が上層で実現された色の混合で、左から右にかけて、Alphaはそれぞれ100%、80%、50%である.
左から右へ:雪図100%Alpha赤色染色、雪図50%Alpha赤色染色、リンゴ図100%Alpha青色染色.
なお、Photoshopのレイヤーブレンドオプションでは、不透明度に加えて、充填数オプションがありますが、両者の違いは、不透明度オプションとは、レイヤー自体にとって、充填数オプションはブレンド時の本レイヤーピクセルの充填割合です.しかし、実際には両者は本質的に同じことであり、つまり両者の積が最終的な充填不透明度である.
本人のレベルが限られているため、改善されたが、間違いは避けられない.貴重な意見を提出することを歓迎します.メールアドレス:[email protected]
レベルが限られているため、間違いは避けられないので、指摘と指導を歓迎します.メールアドレス:[email protected]
ここでは『C++画像処理--記事インデックス』にアクセスできます.