[
论文
政研论文下载论文大学下载论文大学下载关于长拳的论文浙大论文封面下载
]C 图片处理之给你的图片打上LOGO
C#图片处理之给你的图片打上LOGO
C#图片处理之:给你的图片打上LOGO
做图片处理软件,肯定会遇到这样的需求:如何把一张图片放在另一
张图片上,比如在大图片中打上公司或个人的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.Form
at24bppRgb);
// 取整个Logo
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++)
{
// 复制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
下面是我的算法。
///
/// 颜色是否近似
///
///
颜色0
///
颜色1
///
容差
///
是/否
public static bool ColorIsSimilar(Color c0, Color c1,
int delta)
{
int r0, r1, g0, g1, b0, b1;
r0 = c0.R;
r1 = c1.R;
g0 = c0.G;
g1 = c1.G;
b0 = c0.B;
b1 = c1.B;
if ((r0-r1)*(r0-r1) + (g0-g1) * (g0-g1) + (b0-b1)
* (b0-b1) <= delta * delta)
{
return true;
}
else
{
return false;
}
}
最后再来看看一种情况。我们需要Logo半透明的呈现,貌似水印样的效果。有了上面两种处理图片的经验,不难认识到,大图b0的指定区域中需要混合原始像素和Logo图片b1像素后的新像素填充。
不难想象,新像素值应该结合了大图和Logo图两方面因素的。Logo图越透明,大图受的影响越小。所以这里我们可以采用加权平均的方法来得到混合后的新值。
新值 = (1 - 不透明度%)* 大图像素值 + 不透明度% * Logo图像素值
完整的函数如下:
///
/// 合并两张图片,支持不透明度和透明色
///
///
图片一
///
图片二
///
起始坐标X
///
起始坐标Y
///
图片二的不透明度
///
被作为透明色处理的颜色
///
透明色的容差
///
合并后的图片
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;
}
当然方法有很多,这里只是从原理的角度阐述了一下我的观点,谁有
更好的方法请让我知道。
9who
关注 - 0
粉丝 - 4
关注博主0 0 0
(请您对文章做出评价)? 上一篇:ASP.Net C#2.0全能数据库组件 (开源含实例源码)|
? 下一篇:sqlhelper 下载 使用指南 代码
posted on 2008-07-28 12:08 9who 阅读(474) 评论(1) 编辑 收藏 所属分类: c#通用类库
评论
1480664
#1楼 回复 引用
您好,我这里有一部份程序。但不怎么看得懂,想在上面加个logo。
比如写上BUAA,怎么加呢,
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data;
using System.Drawing;
using System.Drawing.Imaging; using System.Text;
using System.Windows.Forms;
namespace WiimoteWhiteboard {
public partial class CalibrationForm : Form {
Bitmap bCalibration;
Graphics gCalibration;
int screenWidth = 1024;//defaults
int screenHeight = 768;
public CalibrationForm()
{
Rectangle rect = new Rectangle();
rect = Screen.GetWorkingArea(this);
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None;
this.Left = 0;
this.Top = 0;
this.Size = new Size(rect.Width, rect.Height);
this.Text = "Calibration - Working area:" + Screen.GetWorkingArea(this).ToString() + " || Real area: " +
Screen.GetBounds(this).ToString();
this.KeyDown += new
System.Windows.Forms.KeyEventHandler(this.OnKeyPress);
screenHeight = rect.Height;
screenWidth = rect.Width;
bCalibration = new Bitmap(screenWidth, screenHeight, PixelFormat.Format24bppRgb);
gCalibration = Graphics.FromImage(bCalibration);
pbCalibrate.Left = 0;
pbCalibrate.Top = 0;
pbCalibrate.Size = new Size(rect.Width, rect.Height);
gCalibration.Clear(Color.White);
BeginInvoke((MethodInvoker)delegate() { pbCalibrate.Image = bCalibration; });
}