C#結合OpenCVSharp4圖片相似度識別

2023-09-09 06:01:14

OpenCVSharp4圖片相似度識別

需求背景:需要計算兩個圖片的相似度,然後將相似的圖片進行歸納

1. 圖片相似度演演算法

由於我是CRUD後端仔,對影象處理沒什麼概念。因此網上調研了幾種相似度演演算法分析其適用場景。

直方圖演演算法

獲取要比較的2個圖片的直方圖資料,然後再將直方圖資料歸一化比較,最終得到一個相似指數,通過設定相似指數的邊界,以此判斷是否相同圖片。

平均值雜湊演演算法 aHash

轉灰度壓縮之後計算均值,最終通過畫素比較得出雜湊值,速度很快,但敏感度很高,稍有變化就會極大影響判定結果,精準度較差。因此比較適用於縮圖比較,最常用的就是以圖搜圖

感知雜湊演演算法 pHash

在均值雜湊基礎上加入DCT(離散餘弦變化),兩次DCT就可以很好的將影象按照頻度分開,取左上角高能低頻資訊做均值雜湊,因此,精確度很高,但是速度方面較差一些。相比較aHashpHash更加適合用於縮圖比較,也非常適合比較兩個近似圖片是否相等。

差異值雜湊演演算法 dHash

灰度壓縮之後,比較相鄰畫素之間差異。假設有10×10的影象,每行10個畫素,就會產生9個差異值,一共10行,就一共有9×10=90個差異值。最終生成雜湊值即指紋。速度上來說,介於aHashpHash之間,精準度同樣也介於aHashpHash之間。

結構相似性演演算法 SSIM

SSIM(structural similarity),結構相似性,是一種衡量兩幅影象相似度的指標。SSIM演演算法主要用於檢測兩張相同尺寸的影象的相似度、或者檢測影象的失真程度。原論文中,SSIM演演算法主要通過分別比較兩個影象的亮度,對比度,結構,然後對這三個要素加權並用乘積表示。

SSIM演演算法在設計上考慮了人眼的視覺特性,它能夠考慮到影象的結構資訊在人的感知上的模糊變化,該模型還引入了一些與感知上的變化有關的感知現象,包含亮度mask和對比mask,結構資訊指的是畫素之間有著內部的依賴性,尤其是空間上靠近的畫素點。這些依賴性攜帶著目標物件視覺感知上的重要資訊。

經過調研對比,這裡就選擇SSIM演演算法。

2. 下載OpenCVSharp4

通過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


3. 使用

在專案中引入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演演算法特性,這應該是圖片大小的相似。

雖然也是拿來主義,畢竟我不是研究演演算法的大佬,需要站在巨人肩膀上幹活~

做個筆記。