簡體   English   中英

比較圖像並標記差異c#

[英]Comparing images and labeling the differences c#

我目前正在從事一個項目,在該項目中,我需要編寫軟件來比較兩個由相同區域組成的圖像,並在差異周圍畫一個方框。 我在幾個小時內用c#.net編寫了該程序,但很快意識到運行它的成本令人難以置信。 這是我實現它的步驟。

  1. 創建了一個存儲每個像素的x,y坐標的Pixel類和一個存儲像素列表以及width,height,x和y屬性的PixelRectangle類。

  2. 遍歷每個圖像的每個像素,比較每個對應像素的顏色。 如果顏色不同,則使用該像素的x,y坐標創建一個新的像素對象,並將其添加到pixelDifference列表中。

  3. 接下來,我編寫了一種方法,該方法以遞歸方式檢查pixelDifference列表中的每個像素,以創建僅包含彼此直接相鄰的像素的PixelRectangle對象。 (肯定是這個壞男孩造成了大部分破壞,因為它給了我一個堆棧溢出錯誤。)

  4. 然后,我根據存儲在PixelRectangle對象列表中的像素計算出矩形的x,y坐標和尺寸,並在原始圖像上繪制一個矩形以顯示差異所在。

我的問題是:我要采用正確的方法嗎? 四叉樹對該項目有什么價值嗎? 如果您能給我一些基本步驟,通常如何實現這種效果,我將不勝感激。 提前致謝。

  • 戴夫

看起來您想實現斑點檢測。 我的建議是不要重新發明輪子,而只是使用openCVSharp或emgu來做到這一點。 谷歌“斑點檢測”和OpenCV

如果您想自己動手做,我的價值2美分:

首先,讓我們澄清一下您想做什么。 確實有兩件事:

  1. 計算兩個圖像之間的差異(我假設它們的尺寸相同)

  2. 在以“ 1”衡量的“不同”的“區域”周圍畫一個方框。這里的問題是什么是“區域”以及什么被認為是“不同”。

我對每個步驟的建議:

(我的假設都是圖像都是灰度的。如果不是,則計算每個像素的顏色總和以獲得灰度值)

1)循環瀏覽兩個圖像中的所有像素,然后將其相減。 在絕對差值上設置一個閾值,以確定它們的差值是否足以表示場景的實際變化(如果圖像來自相機,則與傳感器噪聲等相反)。 然后將結果存儲在第三張圖像中。 0,無差異。 255的差異。 如果做對了,這應該很快。 但是,在C#中,必須使用指針才能獲得不錯的性能。 這里是如何執行此操作的示例(注意:未測試代碼!):

  /// <summary>
    /// computes difference between two images and stores result in a third image
    /// input images must be of same dimension and colour depth
    /// </summary>
    /// <param name="imageA">first image</param>
    /// <param name="imageB">second image</param>
    /// <param name="imageDiff">output 0 if same, 255 if different</param>
    /// <param name="width">width of images</param>
    /// <param name="height">height of images</param>
    /// <param name="channels">number of colour channels for the input images</param>
    unsafe void ComputeDiffernece(byte[] imageA, byte[] imageB, byte[] imageDiff, int width, int height, int channels, int threshold)
    {
        int ch = channels;

        fixed (byte* piA = imageB, piB = imageB, piD = imageDiff)
        {

            if (ch > 1) // this a colour image (assuming for RGB ch == 3 and RGBA  == 4)
            {
                for (int r = 0; r < height; r++)
                {
                    byte* pA = piA + r * width * ch;
                    byte* pB = piB + r * width * ch;
                    byte* pD = piD + r * width; //this has only one channels!

                    for (int c = 0; c < width; c++)
                    {
                        //assuming three colour channels. if channels is larger ignore extra (as it's likely alpha)
                        int LA = pA[c * ch] + pA[c * ch + 1] + pA[c * ch + 2];
                        int LB = pB[c * ch] + pB[c * ch + 1] + pB[c * ch + 2];

                        if (Math.Abs(LA - LB) > threshold)
                        {
                            pD[c] = 255;
                        }
                        else
                        {
                            pD[c] = 0;
                        }

                    }
                }
            }
            else //single grey scale channels
            {
                for (int r = 0; r < height; r++)
                {
                    byte* pA = piA + r * width;
                    byte* pB = piB + r * width;
                    byte* pD = piD + r * width; //this has only one channels!

                    for (int c = 0; c < width; c++)
                    {
                        if (Math.Abs(pA[c] - pB[c]) > threshold)
                        {
                            pD[c] = 255;
                        }
                        else
                        {
                            pD[c] = 0;
                        }
                    }
                }
            }
        }
    }

2)

不知道您在這里所說的面積是什么意思。 幾種解決方案取決於您的意思。 從最簡單到最困難。

a)將輸出中的每個差異像素塗成紅色

b)假設您只有一個差異區域(不太可能)計算輸出圖像中所有255個像素的邊界框。 這可以通過對所有255個像素的x和y位置使用簡單的max / min來完成。 單次通過圖像,應該非常快。

c)如果您有很多不同的區域需要更改,請計算“連接的組件”。 那是相互連接的像素的集合。 當然,這僅適用於二進制圖像(即,在本例中為開或關,或者為0和255)。 您可以在C#中實現此功能,而我之前已經做到了。 但我不會在這里為你做。 這有點涉及。 算法在那里。 再次使用opencv或google 連接組件

一旦有了CC的列表,就在每個CC旁邊畫一個方框。 完成。

您正在以正確的方式進行操作。 如果正確實現,步驟3不應引起StackOverflow異常,因此我將仔細研究該方法。

最有可能發生的事情是您對PixelDifference的每個成員的遞歸檢查正在無限運行。 確保跟蹤已檢查的像素。 檢查像素后,在檢查相鄰像素時不再需要考慮它。 在檢查任何相鄰像素之前,請確保尚未對其進行檢查。

作為跟蹤已檢查哪些像素的一種替代方法,您可以在檢查完像素后將其從PixelDifference中刪除。 當然,這可能需要更改實現算法的方式,因為在檢查列表時從列表中刪除該元素可能會帶來一系列新問題。

找到兩個圖像之間的差異的方法要簡單得多。

因此,如果您有兩個圖像

Image<Gray, Byte> A;
Image<Gray, Byte> B;

您可以通過以下方式快速獲得差異

A - B

當然,圖像不會存儲負值,因此在圖像B中的像素大於圖像A中的情況下會得到差異

B - A

將這些結合在一起

(A - B) + (B - A)

可以,但是我們可以做得更好。

可以使用傅立葉變換進行評估。

CvInvoke.cvDFT(A.Convert<Gray, Single>().Ptr, DFTA.Ptr, Emgu.CV.CvEnum.CV_DXT.CV_DXT_FORWARD, -1);
CvInvoke.cvDFT(B.Convert<Gray, Single>().Ptr, DFTB.Ptr, Emgu.CV.CvEnum.CV_DXT.CV_DXT_FORWARD, -1);

CvInvoke.cvDFT((DFTB - DFTA).Convert<Gray, Single>().Ptr, AB.Ptr, Emgu.CV.CvEnum.CV_DXT.CV_DXT_INVERSE, -1);
CvInvoke.cvDFT((DFTA - DFTB).Ptr, BA.Ptr, Emgu.CV.CvEnum.CV_DXT.CV_DXT_INVERSE, -1);

我發現此方法的結果要好得多。 您可以從中制作一個二進制圖像,即:對圖像進行閾值處理,以使沒有變化的像素為0,而具有變化的像素為255。

現在,就問題的第二部分而言,我想有一個簡單的粗略解決方案:

將圖像划分為矩形區域。 也許沒有必要使用四叉樹。 假設是8x8的網格...(對於不同的結果,您可以嘗試使用不同的網格大小)。

然后在這些區域內使用凸包函數。 通過查找頂點的最小和最大x和y坐標,可以將這些凸包轉換為矩形。

應該快速簡單

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM