http://www.cnblogs.com/wuchaodong/archive/2009/04/28/1444792.html
近日逛博客的時候偶然發現了一個有關圖片相似度的Python算法實現。想著很有意思便搬到C#上來了,給大家看看。
閒言碎語
才疏學淺,只把計算圖像相似度的一個基本算法的基本實現方式給羅列了出來,以至於在最後自己測評的時候也大發感慨,這個算法有點不靠譜。不管怎麼樣,這個算法有時候還是有用的,所以還是列出來跟大家伙一起分享分享~~
PS:圖像處理這一塊博大精深,個人偶爾發現了點東西拿來分享。說的不好的地方,寫得太糟的地方,諸位准備扔磚頭還望淡定,淡定~~
基本知識介紹
顏色直方圖
顏色直方圖是在許多圖像檢索系統中被廣泛采用的顏色特征,它所描述的是不同色彩在整幅圖像中所佔的比例,而並不關心每種色彩所處的空間位置,即無法描述圖像中的對象或物體。顏色直方圖特別適用於描述那些難以進行自動分割的圖像。
灰度直方圖
灰度直方圖是灰度級的函數,它表示圖像中具有每種灰度級的像素的個數,反映圖像中每種灰度出現的頻率。灰度直方圖的橫坐標是灰度級,縱坐標是該灰度級出現的頻率,是圖像的最基本的統計特征。
本文中即是使用灰度直方圖來計算圖片相似度,關於算法那一塊也不贅言了,畢竟圖像學圖形學,直方圖我是門兒都不懂,我也不准備打腫臉充胖子,只想實現一個最基本的算法,然後從最直觀的角度看看這個算法的有效性,僅此而已。
算法實現
諸位看官休怪筆者囫圇吞棗,淺嘗輒止的學習態度。額畢竟是因興趣而來,於此方面並無半點基礎(當然,除了知道RGB是啥玩意兒——這還幸虧當年計算機圖形學的老師是個Super美女,因此多上了幾節課的緣故),更談不上半點造詣,看官莫怪莫怪,且忍住怒氣,是走是留,小生不敢有半點阻攔~~
大致步驟如下:
1, 將圖像轉換成相同大小,以有利於計算出相像的直方圖來
2, 計算轉化後的灰度直方圖
3, 利用XX公式,得到直方圖相似度的定量度量
4, 輸出這些不知道有用沒用的相似度結果數據
代碼實現
步驟1, 將圖像轉化成相同大小,我們暫且轉化成256 X 256吧。
public Bitmap Resize(string imageFile, string newImageFile)
{
img = Image.FromFile(imageFile);
Bitmap imgOutput = new Bitmap(img, 256, 256);
imgOutput.Save(newImageFile, System.Drawing.Imaging.ImageFormat.Jpeg);
imgOutput.Dispose();
return (Bitmap)Image.FromFile(newImageFile);
}
這部分代碼很好懂,imageFile為原始圖片的完整路徑,newImageFile為強轉大小後的256 X 256圖片的路徑,為了「賽」後可以看到我們轉化出來的圖片長啥樣,所以我就把它保存到了本地了,以至於有了上面略顯丑陋的代碼。
步驟2,計算圖像的直方圖
public int[] GetHisogram(Bitmap img)
{
BitmapData data = img.LockBits( new System.Drawing.Rectangle( 0 , 0 , img.Width , img.Height ), ImageLockMode.ReadWrite , PixelFormat.Format24bppRgb );
int[ ] histogram = new int[ 256 ];
unsafe
{
byte* ptr = ( byte* )data.Scan0;
int remain = data.Stride - data.Width * 3;
for( int i = 0 ; i < histogram.Length ; i ++ )
histogram[ i ] = 0;
for( int i = 0 ; i < data.Height ; i ++ )
{
for( int j = 0 ; j < data.Width ; j ++ )
{
int mean = ptr[ 0 ] + ptr[ 1 ] + ptr[ 2 ];
mean /= 3;
histogram[ mean ] ++;
ptr += 3;
}
ptr += remain;
}
}
img.UnlockBits( data );
return histogram;
}
這段就是驚天地泣鬼神的灰度直方圖計算方法,裡面的彎彎繞還是留給諸位自己去摻和。
步驟3,計算直方圖相似度度量
這一步驟的法寶在於這個:
Sim(G,S)= 其中G,S為直方圖,N 為顏色空間樣點數
為了大家少敲兩行字兒,也給出一堆亂七八糟的代碼:
//計算相減後的絕對值
private float GetAbs(int firstNum, int secondNum)
{
float abs = Math.Abs((float)firstNum - (float)secondNum);
float result = Math.Max(firstNum, secondNum);
if (result == 0)
result = 1;
return abs / result;
}
//最終計算結果
public float GetResult(int[] firstNum, int[] scondNum)
{
if (firstNum.Length != scondNum.Length)
{
return 0;
}
else
{
float result = 0;
int j = firstNum.Length;
for (int i = 0; i < j; i++)
{
result += 1 - GetAbs(firstNum[i], scondNum[i]);
Console.WriteLine(i + "----" + result);
}
return result/j;
}
}
步驟4,輸出
這個……諸位愛怎麼輸出就怎麼輸出吧。直接Console也好,七彩命令行輸出也罷,亦或者保存到文本文件中留作紀念啦啦,諸位「好自為之」~~
算法測評
真對不住大家,忘了跟大家說,我也不是一個專業的算法測評人員,但是作為一個半拉子測試人員免不了手癢癢想要看看這個算法到底有多大能耐,就拿出幾張圖片出來驗驗貨吧。
以下是算法測評結果。以下部分內容話帶調侃,絕無惡意,開開玩笑,娛樂大眾~~
路人甲 |
路人乙 |
圖像相似度 |
惡搞點評 |
100% |
裡面什麼都沒有!? 恭喜你,如果你看不出來這是兩張白底圖片,那麼你還真是小白,因為你連自家人都認不出來啊~~ |
||
100% |
天下烏鴉一般黑,這個算法在這一點上立場還算堅定,表現不錯~ |
||
100% |
碰到Win7也不動心,意志堅定地給出了100%的正確答案。
這算法比我意志堅定多了,我可是win7剛出來個7000就裝了,還一直用到現在,不過確實好用~~ |
||
88.84% |
明明很不一樣的「我」跟「你」擺在那裡,怎麼就相似度這麼高咧??
難道,「我」,「你」都認不出來??
哦,我忘了,這兩張圖片的大背景是一樣的,難怪…… |
||
16.08% |
MS跟Apple這麼水火不相容? 【均使用默認桌面~~】 |
||
50.64% |
終於了解了Jack跟Rose不能在一起的真正原因: 不是愛的不夠深,也不是泰坦尼克號沉了,用老媽的話說「沒有『夫妻』相」 —— 還是老媽這個過來人老道~~ |
||
99.21% |
哇,太不可思議了,竟然是這樣。 這算法這樣「黑」「白」不分??
我得向Jack跟Rose的忠實Fans道歉了,上面的話是一時失言~~祝他們倆白頭偕老,下輩子千萬別做船了,坐船也不出海,出海也不去北極,…… |
經過我略顯玩世不恭的測評活動,說實話,我對這個算法是相當的失望,尤其是最後一次對比中的結果,目前情緒低落中。這倒不是說這算法的一無是處,應該是我或者某些前輩用錯了地方,個人覺得算法使用的局限性太大,也或許是我的期望值太高了吧。
後記
開始看到這玩意兒的時候覺得這玩意兒很簡單啊,可是一想不對勁,沒有這麼容易的事情,要不Google,MS這些大牛們做了這麼久還沒有像樣的玩意兒出來。果不其然,為了多了解一點相關的內容,我不得不Google了一下,覺得那些術語完全不知所雲,看不懂啊;看來我得祭出我一般不使用的大殺器了——百度一搜。嘿,還真找出來了一堆東西,比Google上面的看起來容易多了,可是打開鏈接進去瞅瞅,發現還是非我當前能力之所及。沒學到東西,但是好歹還是了解了一點皮毛上的皮毛。
全文完
諸位看官若覺得講得沒有意義,浪費了你的時間,那就權當作冷笑話聽聽緩解一下緊張的神經~~