合并两张图片,支持不透明度和透明色
来源:互联网 发布:左旋肉碱 知乎 编辑:程序博客网 时间:2024/05/05 15:15
图片处理软件,肯定会遇到这样的需求:如何把一张图片放在另一张图片上,比如在大图片中打上公司或个人的Logo等等。
其实原理很简单。假设我们有一张 800 * 600 的大图片(Bitmap b0),想在(10,10)的位置上打上Logo(Bitmap b1),Logo图片大小是 150 * 30。很简单,我们以大图片的(10,10)位置作为起始位置,做一个150 * 30 的矩形,把Logo图片整个替换这个矩形。
老规矩,还是用BitmapData来帮助我们实现这个功能。
// 取以大图(X,Y)为起点,w * h 的矩形区域BitmapData srcData = b0.LockBits(new Rectangle(X,Y,w,h),ImageLockMode.WriteOnly,PixelFormat.Format24bppRgb);// 取整个LogoBitmapData dstData = b1.LockBits(new Rectangle(0,0,w,h),ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);unsafe{ byte* pIn = (byte*)srcData.Scan0.ToPointer(); byte* pLogo = (byte*)dstData.Scan0.ToPointer(); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { // 复制Logo图的像素 pIn[0] = (byte)pLogo[0]; pIn[1] = (byte)pLogo[1]; pIn[2] = (byte)pLogo[2]; pIn += 3; pLogo += 3; } pIn += srcData.Stride - w * 3; pLogo += dstData.Stride - w * 3; } b0.UnlockBits(srcData); b1.UnlockBits(dstData);}
这样,为图片打Logo的功能就实现了。当然,还可以有别的方法,比如用BitBlt这个API函数,或者使用GDI+的DrawImage函数。
但这样有个小问题,Logo区域永远是个矩形。假如我的Logo是个圆形,只希望在大图上出现圆形的Logo而不是整块矩形,那又该如何实现呢?
这个问题其实可以这样理解:我们让Logo的背景变透明。而所谓透明,就是两张图片叠加时,上层图片“透明”区域中的像素,仍然使用下层图的相应像素来代替。但在RGB模式下,并没有“透明”这个概念(我们的讨论都在用PixelFormat.Format24bppRgb,PixelFormat.Format32bppArgb中的Alpha分量支持“透明度”),因此我们需要自己规定一种颜色,凡是这个颜色的,都作为“透明”处理。
比如,我们的Logo是黑底的,那么就指定黑色作为透明色。
修改算法也很简单,凡是遇到Logo图中像素颜色是黑色(当然这是个参数,可以自己改)的,就用大图相应位置的颜色来代替,否则还是使用Logo的像素色。下面是算法:
// 因为有可能要回写,所以原图的 ImageLockMode 这次是 ReadWrite
BitmapData srcData = b0.LockBits(new Rectangle(X, Y, w, h), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);BitmapData dstData = b1.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);unsafe{ byte* pIn = (byte*)srcData.Scan0.ToPointer(); byte* pLogo = (byte*)dstData.Scan0.ToPointer(); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { // 判断当前点是否透明色 // 取得当前色 Color c = Color.FromArgb(pLogo[2], pLogo[1], pLogo[0]); if (!ColorIsSimilar(c, TransColor, delta)) { // 不是透明色,b0当前位置的颜色用b1对应位置的来代替 pIn[0] = (byte)pLogo[0]; pIn[1] = (byte)pLogo[1]; pIn[2] = (byte)pLogo[2]; } pIn += 3; pLogo += 3; } pIn += srcData.Stride - w * 3; pLogo += dstData.Stride - w * 3; } b0.UnlockBits(srcData); b1.UnlockBits(dstData);}
我们在上面的算法中出现了另一个小算法,判断颜色C0,C1是否相等。当然如果两个颜色的RGB分量都相等那就肯定是相等了,但考虑到容差方面的因素,我们需要一个更灵活的算法。
我们知道,一个颜色由RGB三个分量组成,每个分量取值[0,255]的整数。这样,任意一个颜色都是这个有限空间中的一个点。两个颜色的“相似度”是否在阀值范围内的问题,最简单的思路也就可以认为求证空间两点的距离是否在阀值范围内。
空间两点距离公式:r2 = (x0-x1)2 + (y0-y1)2 + (z0-z1)2
下面是我的算法。
/// <summary> /// 合并两张图片,支持不透明度和透明色 /// </summary> /// <param name="b0">图片一</param> /// <param name="b1">图片二</param> /// <param name="X">起始坐标X</param> /// <param name="Y">起始坐标Y</param> /// <param name="b1_alpha">图片二的不透明度</param> /// <param name="TransColor">被作为透明色处理的颜色</param> /// <param name="delta">透明色的容差</param> /// <returns>合并后的图片</returns> public static Bitmap KiMerge(Bitmap b0, Bitmap b1, int X, int Y, int b1_alpha,Color TransColor,int delta) { if (b0.Equals(null) || b1.Equals(null)) { return null; } int w0 = b0.Width; int h0 = b0.Height; int w1 = b1.Width; int h1 = b1.Height; int w, h; if (X + w1 > w0) { w = w0 - X; } else { w = w1; } if (Y + h1 > h0) { h = h0 - Y; } else { h = h1; } BitmapData srcData = b0.LockBits(new Rectangle(X, Y, w, h), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); BitmapData dstData = b1.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); unsafe { byte* pIn = (byte*)srcData.Scan0.ToPointer(); byte* pLogo = (byte*)dstData.Scan0.ToPointer(); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { // 判断透明色 Color c = Color.FromArgb(pLogo[2], pLogo[1], pLogo[0]); if (!ColorIsSimilar(c, TransColor, delta)) { float bili = (float)b1_alpha / (float)255; // 不是透明色,加权平均 float inbili = 1.0f - bili; int r, g, b; b = (int)(pIn[0] * inbili + pLogo[0] * bili); g = (int)(pIn[1] * inbili + pLogo[1] * bili); r = (int)(pIn[2] * inbili + pLogo[2] * bili); pIn[0] = (byte)b; pIn[1] = (byte)g; pIn[2] = (byte)r; } pIn += 3; pLogo += 3; } pIn += srcData.Stride - w * 3; pLogo += dstData.Stride - w * 3; } b0.UnlockBits(srcData); b1.UnlockBits(dstData); } return b0; }
- 合并两张图片,支持不透明度和透明色
- 如何使用OPENCV实现两张图片的混合(PS中的图层不透明度)
- wpf设置图片和不透明度
- wpf设置图片和不透明度
- 合并两张图片
- Android不透明度色值
- OpenCV 合并两张图片
- photoshop不透明度和填充度原理
- ios合并两张图片(叠加两张图片 重合两张图片)
- 合并两张图片(水印效果)
- C# 合并两张图片的方法
- android 两张图片合并作背景图
- android-合并两张图片bitmap
- OpenCV两张图片的合并
- java实现把两张图片合并
- android-合并两张图片bitmap
- [转]OpenCV两张图片的合并
- PHP合并两张图片的实现
- java个人所得税计算器
- “木桶原理”——吾之见学习法,成长法
- 用PHP创建PDF文件
- anroid dip 转 px 方法
- 正则表达式系列
- 合并两张图片,支持不透明度和透明色
- Hibernate 检索方式
- 笔记本用PCIE转并口卡连接H-JTAG烧写PXA270 NOR Flash
- C# 创建内存表 简单方法
- js日期
- c#.net常用函数列表
- C#处理文本文件
- andoird笔记(第二章--android应用界面编程)
- 在PHP中的正则表达式验证