国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

C#圖像處理之-給你的圖片打上LOGO

2019-11-17 04:27:27
字體:
供稿:網(wǎng)友

做圖片處理軟件,肯定會(huì)遇到這樣的需求:如何把一張圖片放在另一張圖片上,比如在大圖片中打上公司或個(gè)人的Logo等等。

其實(shí)原理很簡(jiǎn)單。假設(shè)我們有一張 800 * 600 的大圖片(Bitmap b0),想在(10,10)的位置上打上Logo(Bitmap b1),Logo圖片大小是 150 * 30。很簡(jiǎn)單,我們以大圖片的(10,10)位置作為起始位置,做一個(gè)150 * 30 的矩形,把Logo圖片整個(gè)替換這個(gè)矩形。

老規(guī)矩,還是用BitmapData來幫助我們實(shí)現(xiàn)這個(gè)功能。view plaincopy to clipboardPRint?
// 取以大圖(X,Y)為起點(diǎn),w * h 的矩形區(qū)域  
BitmapData srcData = b0.LockBits(new Rectangle(X,Y,w,h),ImageLockMode.WriteOnly,PixelFormat.Format24bppRgb);  
 
// 取整個(gè)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++)  
 {  
 
 // 復(fù)制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);  

// 取以大圖(X,Y)為起點(diǎn),w * h 的矩形區(qū)域
BitmapData srcData = b0.LockBits(new Rectangle(X,Y,w,h),ImageLockMode.WriteOnly,PixelFormat.Format24bppRgb);

// 取整個(gè)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++)
 {

 // 復(fù)制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的功能就實(shí)現(xiàn)了。當(dāng)然,還可以有別的方法,比如用BitBlt這個(gè)API函數(shù),或者使用GDI+的DrawImage函數(shù)。

但這樣有個(gè)小問題,Logo區(qū)域永遠(yuǎn)是個(gè)矩形。假如我的Logo是個(gè)圓形,只希望在大圖上出現(xiàn)圓形的Logo而不是整塊矩形,那又該如何實(shí)現(xiàn)呢?

這個(gè)問題其實(shí)可以這樣理解:我們讓Logo的背景變透明。而所謂透明,就是兩張圖片疊加時(shí),上層圖片“透明”區(qū)域中的像素,仍然使用下層圖的相應(yīng)像素來代替。但在RGB模式下,并沒有“透明”這個(gè)概念(我們的討論都在用PixelFormat.Format24bppRgb,PixelFormat.Format32bppArgb中的Alpha分量支持“透明度”),因此我們需要自己規(guī)定一種顏色,凡是這個(gè)顏色的,都作為“透明”處理。

比如,我們的Logo是黑底的,那么就指定黑色作為透明色。

修改算法也很簡(jiǎn)單,凡是遇到Logo圖中像素顏色是黑色(當(dāng)然這是個(gè)參數(shù),可以自己改)的,就用大圖相應(yīng)位置的顏色來代替,否則還是使用Logo的像素色。下面是算法:view plaincopy to clipboardprint?
// 因?yàn)橛锌赡芤貙懀栽瓐D的 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++)  
 {  
 
 // 判斷當(dāng)前點(diǎn)是否透明色  
 // 取得當(dāng)前色  
 Color c = Color.FromArgb(pLogo[2], pLogo[1], pLogo[0]);  
 if (!ColorIsSimilar(c, TransColor, delta))  
 {  
 // 不是透明色,b0當(dāng)前位置的顏色用b1對(duì)應(yīng)位置的來代替  
 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);  

// 因?yàn)橛锌赡芤貙懀栽瓐D的 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++)
 {

 // 判斷當(dāng)前點(diǎn)是否透明色
 // 取得當(dāng)前色
 Color c = Color.FromArgb(pLogo[2], pLogo[1], pLogo[0]);
 if (!ColorIsSimilar(c, TransColor, delta))
 {
 // 不是透明色,b0當(dāng)前位置的顏色用b1對(duì)應(yīng)位置的來代替
 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);
}我們?cè)谏厦娴乃惴ㄖ谐霈F(xiàn)了另一個(gè)小算法,判斷顏色C0,C1是否相等。當(dāng)然如果兩個(gè)顏色的RGB分量都相等那就肯定是相等了,但考慮到容差方面的因素,我們需要一個(gè)更靈活的算法。

我們知道,一個(gè)顏色由RGB三個(gè)分量組成,每個(gè)分量取值[0,255]的整數(shù)。這樣,任意一個(gè)顏色都是這個(gè)有限空間中的一個(gè)點(diǎn)。兩個(gè)顏色的“相似度”是否在閥值范圍內(nèi)的問題,最簡(jiǎn)單的思路也就可以認(rèn)為求證空間兩點(diǎn)的距離是否在閥值范圍內(nèi)。

空間兩點(diǎn)距離公式:r2 = (x0-x1)2 + (y0-y1)2 + (z0-z1)2

下面是我的算法。view plaincopy to clipboardprint?
/// <summary>  
/// 顏色是否近似  
/// </summary>  
/// <param name="c0">顏色0</param>  
/// <param name="c1">顏色1</param>  
/// <param name="delta">容差</param>  
/// <returns>是/否</returns>  
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;  
}              
 

 /// <summary>
 /// 顏色是否近似
 /// </summary>
 /// <param name="c0">顏色0</param>
 /// <param name="c1">顏色1</param>
 /// <param name="delta">容差</param>
 /// <returns>是/否</returns>
 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半透明的呈現(xiàn),貌似水印樣的效果。有了上面兩種處理圖片的經(jīng)驗(yàn),不難認(rèn)識(shí)到,大圖b0的指定區(qū)域中需要混合原始像素和Logo圖片b1像素后的新像素填充。

不難想象,新像素值應(yīng)該結(jié)合了大圖和Logo圖兩方面因素的。Logo圖越透明,大圖受的影響越小。所以這里我們可以采用加權(quán)平均的方法來得到混合后的新值。

新值 = (1 - 不透明度%)* 大圖像素值 + 不透明度% * Logo圖像素值

完整的函數(shù)如下:view plaincopy to clipboardprint?
/// <summary>  
/// 合并兩張圖片,支持不透明度和透明色  
/// </summary>  
/// <param name="b0">圖片一</param>  
/// <param name="b1">圖片二</param>  
/// <param name="X">起始坐標(biāo)X</param>  
/// <param name="Y">起始坐標(biāo)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; // 不是透明色,加權(quán)平均  
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;  

 /// <summary>
 /// 合并兩張圖片,支持不透明度和透明色
 /// </summary>
 /// <param name="b0">圖片一</param>
 /// <param name="b1">圖片二</param>
 /// <param name="X">起始坐標(biāo)X</param>
 /// <param name="Y">起始坐標(biāo)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; // 不是透明色,加權(quán)平均
 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;
 }當(dāng)然方法有很多,這里只是從原理的角度闡述了一下我的觀點(diǎn),誰(shuí)有更好的方法請(qǐng)讓我知道。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 米脂县| 鄯善县| 都匀市| 施秉县| 肇源县| 克拉玛依市| 偃师市| 香河县| 东丰县| 宁乡县| 洛隆县| 台湾省| 泰兴市| 南和县| 乐清市| 尼勒克县| 阜平县| 拉萨市| 镇赉县| 剑河县| 长治市| 菏泽市| 新竹市| 孟连| 广西| 福贡县| 绥阳县| 奎屯市| 治多县| 承德市| 都江堰市| 隆化县| 合水县| 新乡市| 苏尼特左旗| 仪征市| 东阿县| 广汉市| 应用必备| 台州市| 铁岭县|