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.Format24bppRgb);
// 取整個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
下面是我的演算法。
/// <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半透明的呈現,貌似水印樣的效果。有了上面兩種處理圖片的經驗,不難認識到,大圖b0的指定區域中需要混合原始畫素和Logo圖片b1畫素後的新畫素填充。
不難想象,新畫素值應該結合了大圖和Logo圖兩方面因素的。Logo圖越透明,大圖受的影響越小。所以這裡我們可以採用加權平均的方法來得到混合後的新值。
新值 = (1 - 不透明度%)* 大影象素值 + 不透明度% * Logo影象素值
完整的函式如下:
/// <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);
}
}
相關文章
- 圖片上傳及圖片處理
- 圖形影像處理之簡單圖片
- 002.00 圖片處理
- Thumbnailator處理圖片AI
- 【YLCircleImageView】圖片處理View
- webpack圖片處理Web
- DDGScreenShot —iOS 圖片處理--多圖片拼接 (swift)iOSSwift
- android圖片處理,讓圖片變成圓形Android
- 你知道前端對圖片的處理方式嗎?前端
- SwiftUI Image 圖片處理SwiftUI
- webpack 圖片處理 loaderWeb
- Python批量處理圖片Python
- 圖片編輯工具:FotoJet Photo Editor更好的處理圖片
- 走近webpack(3)–圖片的處理Web
- SwiftUI圖片處理(縮放、拼圖)SwiftUI
- 處理圖片流資料
- CGContextRef處理圓形圖片GCContext
- java thumbnailator 做圖片處理JavaAI
- OpenCv--圖片處理操作OpenCV
- java 圖片水印處理類Java
- JavaScript WebGL 圖片透明處理JavaScriptWeb
- webpack(6)webpack處理圖片Web
- Golang 圖片處理 — image 庫Golang
- photoshop常用圖片處理技巧
- 圖片識別文字,分享給你!
- Laravel 使用 Intervention/image 處理圖片Laravel
- Photoshop類圖片處理軟體
- 002.01 圖片去外框處理
- 002.00 圖片處理 PIL.Image
- 圖片展示 [ Numpy 處理, Matplotlib 展示 ]
- 如何處理 Web 圖片優化?Web優化
- 分享一個圖片處理類
- 圖片區域點選處理
- DxO PhotoLab 5,raw圖片處理
- Mac raw圖片處理軟體Mac
- DxO PhotoLab for mac(RAW圖片處理)Mac
- 呼叫系統檔案管理器選擇圖片,呼叫系統裁剪AIP對圖片處理,顯示裁剪之後的圖片...AI
- thumbor:功能強大的圖片處理庫
- JavaScript中的圖片處理與合成(四)JavaScript