需求背景:需要計算兩個圖片的相似度,然後將相似的圖片進行歸納
由於我是CRUD後端仔,對影象處理沒什麼概念。因此網上調研了幾種相似度演演算法分析其適用場景。
直方圖演演算法
獲取要比較的2個圖片的直方圖資料,然後再將直方圖資料歸一化比較,最終得到一個相似指數,通過設定相似指數的邊界,以此判斷是否相同圖片。
平均值雜湊演演算法 aHash
轉灰度壓縮之後計算均值,最終通過畫素比較得出雜湊值,速度很快,但敏感度很高,稍有變化就會極大影響判定結果,精準度較差。因此比較適用於縮圖比較,最常用的就是以圖搜圖
感知雜湊演演算法 pHash
在均值雜湊基礎上加入DCT
(離散餘弦變化),兩次DCT
就可以很好的將影象按照頻度分開,取左上角高能低頻資訊做均值雜湊,因此,精確度很高,但是速度方面較差一些。相比較aHash
,pHash
更加適合用於縮圖比較,也非常適合比較兩個近似圖片是否相等。
差異值雜湊演演算法 dHash
灰度壓縮之後,比較相鄰畫素之間差異。假設有10×10的影象,每行10個畫素,就會產生9個差異值,一共10行,就一共有9×10=90個差異值。最終生成雜湊值即指紋。速度上來說,介於aHash
和pHash
之間,精準度同樣也介於aHash
和pHash
之間。
結構相似性演演算法 SSIM
SSIM(structural similarity)
,結構相似性,是一種衡量兩幅影象相似度的指標。SSIM
演演算法主要用於檢測兩張相同尺寸的影象的相似度、或者檢測影象的失真程度。原論文中,SSIM
演演算法主要通過分別比較兩個影象的亮度,對比度,結構,然後對這三個要素加權並用乘積表示。
SSIM
演演算法在設計上考慮了人眼的視覺特性,它能夠考慮到影象的結構資訊在人的感知上的模糊變化,該模型還引入了一些與感知上的變化有關的感知現象,包含亮度mask和對比mask,結構資訊指的是畫素之間有著內部的依賴性,尤其是空間上靠近的畫素點。這些依賴性攜帶著目標物件視覺感知上的重要資訊。
經過調研對比,這裡就選擇SSIM
演演算法。
通過NuGet包管理器進行下載。搜尋OpenCVSharp4
下載。
請注意其描述資訊:OpenCV wrapper for .NET. Since this package includes only core managed libraries, another package of native bindings for your OS is required (OpenCvSharp4.runtime.*).
這是說:OpenCV 包只是一個核心庫,如需在你的系統上使用,還需要對應的執行時包,這裡是Windows系統,因此還需下載 OpenCvSharp4.runtime.win
在專案中引入OpenCvSharp
using OpenCvSharp;
由於OpenCVSharp4
沒有直接提供封裝SSIM
演演算法的介面,因此需要自行寫這部分程式碼。完整程式碼如下
public Scalar Compare_SSIM(string imgFile1, string imgFile2)
{
var image1 = Cv2.ImRead(imgFile1);
var image2Tmp = Cv2.ImRead(imgFile2);
// 將兩個圖片處理成同樣大小,否則會有錯誤: The operation is neither 'array op array' (where arrays have the same size and the same number of channels), nor 'array op scalar', nor 'scalar op array'
var image2 = new Mat();
Cv2.Resize(image2Tmp, image2, new OpenCvSharp.Size(image1.Size().Width, image1.Size().Height));
double C1 = 6.5025, C2 = 58.5225;
var validImage1 = new Mat();
var validImage2 = new Mat();
image1.ConvertTo(validImage1, MatType.CV_32F); //資料型別轉換為 float,防止後續計算出現錯誤
image2.ConvertTo(validImage2, MatType.CV_32F);
Mat image1_1 = validImage1.Mul(validImage1); //影象乘積
Mat image2_2 = validImage2.Mul(validImage2);
Mat image1_2 = validImage1.Mul(validImage2);
Mat gausBlur1 = new Mat(), gausBlur2 = new Mat(), gausBlur12 = new Mat();
Cv2.GaussianBlur(validImage1, gausBlur1, new OpenCvSharp.Size(11, 11), 1.5); //高斯折積核計算影象均值
Cv2.GaussianBlur(validImage2, gausBlur2, new OpenCvSharp.Size(11, 11), 1.5);
Cv2.GaussianBlur(image1_2, gausBlur12, new OpenCvSharp.Size(11, 11), 1.5);
Mat imageAvgProduct = gausBlur1.Mul(gausBlur2); //均值乘積
Mat u1Squre = gausBlur1.Mul(gausBlur1); //各自均值的平方
Mat u2Squre = gausBlur2.Mul(gausBlur2);
Mat imageConvariance = new Mat(), imageVariance1 = new Mat(), imageVariance2 = new Mat();
Mat squreAvg1 = new Mat(), squreAvg2 = new Mat();
Cv2.GaussianBlur(image1_1, squreAvg1, new OpenCvSharp.Size(11, 11), 1.5); //影象平方的均值
Cv2.GaussianBlur(image2_2, squreAvg2, new OpenCvSharp.Size(11, 11), 1.5);
imageConvariance = gausBlur12 - gausBlur1.Mul(gausBlur2);// 計算協方差
imageVariance1 = squreAvg1 - gausBlur1.Mul(gausBlur1); //計算方差
imageVariance2 = squreAvg2 - gausBlur2.Mul(gausBlur2);
var member = ((2 * gausBlur1.Mul(gausBlur2) + C1).Mul(2 * imageConvariance + C2));
var denominator = ((u1Squre + u2Squre + C1).Mul(imageVariance1 + imageVariance2 + C2));
Mat ssim = new Mat();
Cv2.Divide(member, denominator, ssim);
var sclar = Cv2.Mean(ssim);
return sclar; // 變化率,即差異
}
實際檢測效果如下
這兩幅圖的相似度大約是92.21%,基本符合預期
這兩幅圖居然還有約18%的相似度,根據SSIM
演演算法特性,這應該是圖片大小的相似。
雖然也是拿來主義,畢竟我不是研究演演算法的大佬,需要站在巨人肩膀上幹活~
做個筆記。
本文來自部落格園,作者:宣君{https://www.nhit.icu/},轉載請註明原文連結:https://www.cnblogs.com/ycit/p/17688625.html