前言
圖片處理是開發過程中經常遇到和要處理的問題,畢竟圖片是比較耗系統資源,網絡資源,存儲空間。。。,因此,圖片的處理顯得格外重要,去百度查閱資料,就會看到很多關於讀片處理的方法,以及圖片的加載框架,他們都是用來處理圖片的。
常見的圖片處理方式大概有如下幾類。比如圖片的等比縮放,圖片海量加載,圖片的裁剪,圖片的無損壓縮,圖片的旋轉,圖片實現畫中畫,圖片實現陰影,改變圖片形狀等
然後,這些操作只是符合日常需求開發,如果涉及的高深的算法層面就不太實用,自己在做人臉識別項目中親自體驗常規處理真的難以解決高難度問題。今天,我介紹一下其他類型的圖片處理算法。首先,我們需要了解倆個概念RGB和ARGB。
RGB和ARGB
-
RGB
RGB色彩模式使用RGB模型爲圖像中每個像素的RGB分量分配一個0~255範圍內的強度值。RGB圖像僅僅使用三種顏色,R(red)、G(green)、B(blue),就能夠使它們依照不同的比例混合,在屏幕上呈現16777216(256 * 256 * 256)種顏色。在電腦中,RGB的所謂“多少”就是指亮度,並使用整數來表示。通常情況下,RGB各有256級亮度,用數字表示爲從0、1、2…直到255。
-
ARGB
一種色彩模式,也就是RGB色彩模式附加上Alpha(透明度)通道,常見於32位位圖的存儲結構。
ARGB—Alpha,Red,Green,Blue.
知道了這倆個概念,就可以瞭解更多較深層次的圖片處理算法了。下面我介紹一下自己在做人臉識別中常用到的算法。
灰度化
在RGB模型中,假設R=G=B時,則彩色表示一種灰度顏色,當中R=G=B的值叫灰度值,因此,灰度圖像每一個像素僅僅需一個字節存放灰度值(又稱強度值、亮度值),灰度範圍爲0-255。一般有下面四種方法對彩色圖像進行灰度化,詳細方法參考csdn大牛博客,講的非常好,點我就知道。
二值化
一幅圖像包含目標物體、背景還有噪聲,要想從多值的數字圖像中直接提取出目標物體,最經常使用的方法就是設定一個全局的閾值T,用T將圖像的數據分成兩部分:大於T的像素羣和小於T的像素羣。將大於T的像素羣的像素值設定爲白色(或者黑色),小於T的像素羣的像素值設定爲黑色(或者白色)。
比方:計算每個像素的(R+G+B)/3,假設>127,則設置該像素爲白色,即R=G=B=255;否則設置爲黑色,即R=G=B=0。
簡單實現代碼如下:
{
Bitmap bitImage = new Bitmap(pictureBox1.Image);//二值化pictureBox1中的圖片
Color c;
int height = pictureBox1.Image.Height;
int width = pictureBox1.Image.Width;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
c = bitImage.GetPixel(j,i);
int r = c.R;
int g = c.G;
int b = c.B;
if ((r + g + b) / 3 >= 127)
{
bitImage.SetPixel(j, i, Color.FromArgb(255, 255, 255));
}
else
{
bitImage.SetPixel(j, i, Color.FromArgb(0,0,0));
}
}
}
return bitImage;
}ascript
膨脹算法
膨脹是將與物體接觸的全部背景點合併到該物體中,使邊界向外部擴張的過程。
能夠用來填補物體中的空洞。
用3x3的結構元素,掃描圖像的每個像素
用結構元素與其覆蓋的二值圖像做“或”操作
假設都爲0,結果圖像的該像素爲0。否則爲1
結果:使二值圖像擴大一圈
膨脹(dilation)能夠看做是腐蝕的對偶運算,其定義是:把結構元素B平移a後得到Ba,若Ba擊中X,我們記下這個a點。全部滿足上述條件的a點組成的集合稱做X被B膨脹的結果。用公式表示爲:D(X)={a | Ba↑X}=X腐蝕,膨脹,細化算法B,例如以下圖所看到的。圖中X是被處理的對象,B是結構元素,不難知道,對於隨意一個在陰影部分的點a,Ba擊中X,所以X被B膨脹的結果就是那個陰影部分。陰影部分包含X的全部範圍,就象X膨脹了一圈似的,這就是爲什麼叫膨脹的原因
在下圖中,左邊是被處理的圖象X(二值圖象,我們針對的是黑點),中間是結構元素B。膨脹的方法是,拿B的中心點和X上的點及X周圍的點一個一個地對,假設B上有一個點落在X的範圍內,則該點就爲黑;右邊是膨脹後的結果。能夠看出,它包含X的全部範圍,就象X膨脹了一圈似的。
我設計了一個簡單的膨脹算法,依次遍歷整個圖片的像素,分析每個像素的周圍八個像素,僅僅要該像素周圍存在黑色的像素,就設置該像素顏色爲黑色。以下是使用膨脹算法處理經過二值化後的圖像的實現代碼:
public bool[] getRoundPixel(Bitmap bitmap, int x, int y)//返回(x,y)周圍像素的情況,爲黑色,則設置爲true
{
bool[] pixels=new bool[8];
Color c;
int num = 0;
for (int i = -1; i < 2; i++)
{
for (int j = -1; j < 2; j++)
{
c = bitmap.GetPixel(x+i,y+j);
if (i != 0 || j != 0)
{
if (255 == c.G)//由於經過了二值化,所以僅僅要檢查RGB中一個屬性的值
{
pixels[num] = false;//爲白色,設置爲false
num++;
}
else if(0==c.G)
{
pixels[num] = true;//爲黑色,設置爲true
num++;
}
}
}
}
return pixels;
}
public Bitmap expend()
{
Bitmap bitImage = new Bitmap(pictureBox2.Image);//處理pictureBox2中的圖片
Bitmap bitImage1 = new Bitmap(pictureBox2.Image);
int height = pictureBox1.Image.Height;
int width = pictureBox1.Image.Width;
bool[] pixels;
for (int i = 1; i < width-1; i++)
{
for (int j = 1; j < height-1; j++)
{
if (bitImage.GetPixel(i, j).R != 0)
{
pixels = getRoundPixel(bitImage, i, j);
for (int k = 0; k < pixels.Length; k++)
{
if (pixels[k] == true)
{
//set this piexl's color to black
bitImage1.SetPixel(i, j, Color.FromArgb(0,0,0));
break;
}
}
}
}
}
return bitImage1;
}
看看最終執行的效果圖如下:
腐蝕算法
腐蝕是一種消除邊界點,使邊界向內部收縮的過程。能夠用來消除小且無意義的物體。
用3x3的結構元素,掃描圖像的每個像素
用結構元素與其覆蓋的二值圖像做“與”操作
假設都爲1,結果圖像的該像素爲1。否則爲0。
結果:使二值圖像減小一圈
把結構元素B平移a後得到Ba,若Ba包括於X,我們記下這個a點,全部滿足上述條件的a點組成的集合稱做X被B腐蝕(Erosion)的結果:
下圖中X是被處理的對象,B是結構元素。不難知道,對於隨意一個在陰影部分的點a,Ba包括於X,所以X被B腐蝕的結果就是那個陰影部分。陰影部分在X的範圍之內,且比X小,就象X被剝掉了一層似的,這就是爲什麼叫腐蝕的原因。
我設計了一個簡單的腐蝕算法,一次遍歷圖像中每個像素,檢查它四周的八個像素,假設有白色的像素,則設置改點爲白色。用二值化處理後的圖片進行腐蝕算法代碼例如以下:
public Bitmap corrode()
{
Bitmap bitImage = new Bitmap(pictureBox2.Image);
Bitmap bitImage1 = new Bitmap(pictureBox2.Image);
Color c;
int height = pictureBox1.Image.Height;
int width = pictureBox1.Image.Width;
bool[] pixels;
for (int i = 1; i < width - 1; i++)
{
for (int j = 1; j < height - 1; j++)
{
c = bitImage.GetPixel(i, j);
if (bitImage.GetPixel(i, j).R == 0)
{
pixels = getRoundPixel(bitImage, i, j);
for (int k = 0; k < pixels.Length; k++)
{
if (pixels[k] == false)
{
//set this piexl's color to black
bitImage1.SetPixel(i, j, Color.FromArgb(255, 255, 255));
break;
}
}
}
}
}
return bitImage1;
}
處理結果如下:
開運算
先腐蝕後膨脹的過程稱爲開運算。用來消除小物體、在纖細點處分離物體、平滑較大物體的邊界的同一時候並不明顯改變其面積。
閉運算
先膨脹後腐蝕的過程稱爲閉運算。用來填充物體內細小空洞、連接鄰近物體、平滑其邊界的同一時候並不明顯改變其面積。
模糊效果
模糊主要是改變背景色的透明度,這裏主要講解如何利用高斯矩陣算法實現模糊鮮果。代碼如下:
public static Bitmap blurImageAmeliorate(Bitmap bmp)
{
long start = System.currentTimeMillis();
// 高斯矩陣
int[] gauss = new int[] { 1, 2, 1, 2, 4, 2, 1, 2, 1 };
int width = bmp.getWidth();
int height = bmp.getHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
int pixR = 0;
int pixG = 0;
int pixB = 0;
int pixColor = 0;
int newR = 0;
int newG = 0;
int newB = 0;
int delta = 75; // 值越小圖片會越亮,越大則越暗
int idx = 0;
int[] pixels = new int[width * height];
bmp.getPixels(pixels, 0, width, 0, 0, width, height);
for (int i = 1, length = height - 1; i < length; i++)
{
for (int k = 1, len = width - 1; k < len; k++)
{
idx = 0;
for (int m = -1; m <= 1; m++)
{
for (int n = -1; n <= 1; n++)
{
pixColor = pixels[(i + m) * width + k + n];
pixR = Color.red(pixColor);
pixG = Color.green(pixColor);
pixB = Color.blue(pixColor);
newR = newR + (int) (pixR * gauss[idx]);
newG = newG + (int) (pixG * gauss[idx]);
newB = newB + (int) (pixB * gauss[idx]);
idx++;
}
}
newR /= delta;
newG /= delta;
newB /= delta;
newR = Math.min(255, Math.max(0, newR));
newG = Math.min(255, Math.max(0, newG));
newB = Math.min(255, Math.max(0, newB));
pixels[i * width + k] = Color.argb(255, newR, newG, newB);
newR = 0;
newG = 0;
newB = 0;
}
}
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
long end = System.currentTimeMillis();
return bitmap;
}
混合處理
這裏主要包含模糊、混合、矩陣卷積計算等,代碼如下:
public static Bitmap blurBitmap(Bitmap bitmap, Context context) {
// to blur
Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
RenderScript rs = RenderScript.create(context);//RenderScript是Android在API 11之後增加的
ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
// bitmaps
Allocation allIn = Allocation.createFromBitmap(rs, bitmap);
Allocation allOut = Allocation.createFromBitmap(rs, outBitmap);
blurScript.setRadius(25.f);
blurScript.setInput(allIn);
blurScript.forEach(allOut);
allOut.copyTo(outBitmap);
bitmap.recycle();
rs.destroy();
return outBitmap;
}