阅读提示

    《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;}elsenewMid = 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;}elsepparams = (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;// 源像素与目标像素灰度混合到cColorMix(&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);elsePArgbColorMixer(dest, source, a);
}
//---------------------------------------------------------------------------

比较一下《C++图像处理 -- 图像颜色混合(中)》的代码,可以发现颜色混合的基本功能代码并没有改变,只是修改了灰度图象染色函数ImageTint和颜色混合函数ImageColorMixer,原来的ImageColorMixer函数更名为RgbColorMixer,成为了新的ImageColorMixer函数的一部分。另外,新的ImageColorMixer函数还增添了一个alpha参数,用以改变上层图像的不透明度,取值范围为0 - 1。

本文不准备再贴例子代码,只给出下面几张png图片颜色合成和染色的测试效果图:

源图一(苹果)                                                  源图二(雪花)

上面一排是雪花图为底层,苹果图为上层实现的颜色混合,从左到右,Alpha分别为100%、80%和50%。

下面一排是苹果图为底层,雪花图为上层实现的颜色混合,从左到右,Alpha分别为100%、80%和50%。

从左到右:雪花图100%Alpha红色染色,雪花图50%Alpha红色染色,苹果图100%Alpha蓝色染色。

另外,需要说明的是,在Photoshop的图层混合选项中,除了不透明度外,还有个填充数选项,二者的区别是:不透明度选项指的是图层本身而言,填充数选项是混合时本图层像素的填充比例。但实际上二者在本质上是一回事,也就是是说二者的乘积就是最终的填充不透明度。

由于本人水平有限,虽经改进,但错误在所难免,欢迎提出宝贵意见,邮箱地址:maozefa@hotmail.com

    因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com

这里可访问《C++图像处理 -- 文章索引》。

C++图像处理 -- 图像颜色混合(下)相关推荐

  1. C++图像处理 -- 图像颜色混合(上)

    阅读提示:     <C++图像处理>系列以代码清晰,可读性为主,全部使用C++代码.     <Delphi图像处理>系列以效率为侧重点,一般代码为PASCAL,核心代码采用 ...

  2. [Python图像处理] 二十一.图像金字塔之图像向下取样和向上取样

    该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门.OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子.图像增强技术.图像分割等,后期结合深度学习研究图像识别 ...

  3. 跟我学Python图像处理丨关于图像金字塔的图像向下取样和向上取样

    摘要:本文讲述图像金字塔知识,了解专门用于图像向上采样和向下采样的pyrUp()和pyrDown()函数. 本文分享自华为云社区<[Python图像处理] 二十一.图像金字塔之图像向下取样和向上 ...

  4. 图像处理--图像上采样和下采样

    下采样 在图像处理中,在图像超分辨重建的时候经常涉及对图像进行下采样.关于下采样,我看到一个很好的描述:对于一幅图像I尺寸为M*N,对其进行s倍下采样,即得到(M/s)*(N/s)尺寸的分辨率图像,当 ...

  5. python图像下采样_[Python图像处理]十二.图像向下取样和向上取样

    图像向下取样 在图像向下取样中,使用最多的是高斯金字塔.它将对图像Gi进行高斯核卷积,并删除图像中所有的偶数行和列,最终缩小图像,其中高斯卷积核运算就是对整幅图像进行加权平均的过程,每一个像素点的值, ...

  6. PS图像菜单下计算命令

    PS图像菜单下计算命令通过通道的混合模式得到的选区非常精细,从而调色的时候过度非常好.功能十分强大.   下面用计算命令中的"相加"和"减去"模式做实例解析,这 ...

  7. 数字图像处理课设图像的锐化_数字图像处理图像锐化处理.ppt

    数字图像处理图像锐化处理 4.7.2 灰度级到彩色转换 灰度级到彩色转换(例) 在HSI彩色空间的直方图均衡强度均衡处理没有改变图像的色调和饱和度值,但它的确影响了整体图像的彩色感观. 向量分量可以用 ...

  8. Matlab数字图像处理——图像的空间变换

    Matlab空间变换函数 imtransform Matlab空间变换函数 imtransform 可以实现图像仿射变换(如 平移.旋转.剪切.缩放).投影变换, 该函数可与 maketform 配合 ...

  9. OpenCV中的图像处理 —— 图像阈值+图像平滑+形态转换

    OpenCV中的图像处理 -- 图像阈值+图像平滑+形态转换 目录 OpenCV中的图像处理 -- 图像阈值+图像平滑+形态转换 1. 图像阈值 1.1 简单阈值 1.2 自适应阈值 1.3 Otsu ...

最新文章

  1. 思维dp ---- CF41D Pawn [可达状态统计dp]
  2. bzoj1174 Toponyms
  3. java的finally_java的finally用法
  4. mysql从 lvs_mysql主从之LVS+keepalived+双主MySQL 负载均衡
  5. python深度学习第三讲——用python写神经网络梯度下降(手写字符识别mnist)
  6. ONAP如何将Open-O和ECOMP数百万行代码合并?
  7. android遍历图片,Android获取手机所有图片并显示
  8. 身份证扫描件用手机怎么弄?手把手教你生成电子身份证
  9. 服务器无线路由器桥接,三个无线路由器怎么设置桥接
  10. 计算飞机票价格python_Python 带你薅羊毛:手把手教你揪出特价机票信息
  11. Docker 端口映射到宿主机后, 外网无法通过ip访问对应宿主机端口
  12. matlab2015的marker,matlab中markersize什么意思
  13. 【数据挖掘实验】利用朴素贝叶斯方法对百万搜狐新闻文本数据进行分类
  14. 凸优化学习笔记 6:共轭函数
  15. outlook从服务器中恢复已删除项目,Outlook 邮件误删,请问能否恢复?谢谢
  16. 【死链】JDK1.7中HashMap在多线程环境的并发问题源码分析
  17. 字节数组与String类型的转换
  18. C语言——报数问题:有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。
  19. 用事实说话,成熟的ORM性能不是瓶颈,灵活性不是问题:EF5.0、PDF.NET5.0、Dapper...
  20. MKS Robin Nano V3.0使用说明书

热门文章

  1. 怎么用matlab编写quad8算法,MATLAB程序设计教程(8)---MATLAB数值积分与微分
  2. 计算机合成的音乐后缀,计算机音乐课程――《声音制作与合成基础》
  3. B端产品经理如何快速了解并分析陌生领域的产品
  4. 七个常用shell运维脚本
  5. Sqlite3小结(小型数据库中增删改查的操作)
  6. Python进度条库tqdm详解
  7. 201671010402-陈靖 实验十四 团队项目评审课程学习总结
  8. Qt设计师-提升法(自定义部件)“提升为”
  9. python抓取网页数据并截图_网络爬虫-使用Python抓取网页数据
  10. LoRa学习:LoRa进行跳频扩频通信(FHSS)的原理