擊中擊不中變換定義
擊中擊不中變換(HMT)需要兩個結構元素B1和B2,合成一個結構元素對B=(B1,B2)
一個用於探測圖像內部,作為擊中部分;另一個用於探測圖像外部,作為擊不中部分。顯然,B1和B2是不應該相連接的,即B1∩B2=Φ。擊中擊不中變換的數學表達式為:
g(x, y)=hitmiss[f(x, y), B]=erode[f(x, y), B1]AND erode[fc(x, y), B2]
其中,fc(x,y)表示的是f(x,y)的補集。
Hit-miss算法步驟:
擊中擊不中變換是形態學中用來檢測特定形狀所處位置的一個基本工具。它的原理就是使用腐蝕;如果要在一幅圖像A上找到B形狀的目標,我們要做的是:
首先,建立一個比B大的模板W;使用此模板對圖像A進行腐蝕,得到圖像假設為Process1;
其次,用B減去W,從而得到V模板(W-B);使用V模板對圖像A的補集進行腐蝕,得到圖像假設為Process2;
然后,Process1與Process2取交集;得到的結果就是B的位置。這里的位置可能不是B的中心位置,要視W-B時對齊的位置而異;
其實很簡單,兩次腐蝕,然后交集,結果就出來了。
Hit-miss原理:
基於腐蝕運算的一個特性:腐蝕的過程相當於對可以填入結構元素的位置作標記的過程。
腐蝕中,雖然標記點取決於原點在結構元素中的相對位置,但輸出圖像的形狀與此無關,改變原點的位置,只會導致輸出結果發生平移。
既然腐蝕的過程相當於對可以填入結構元素的位置作標記的過程,可以利用腐蝕來確定目標的位置。進行目標檢測,既要檢測到目標的內部,也要檢測到外部,即在一次運算中可以同時捕獲內外標記。
由於以上兩點,采用兩個結構基元H、M,作為一個結構元素對B=(H,M),一個探測目標內部,一個探測目標外部。當且僅當H平移到某一點可填入X的內部,M平移到該點可填入X的外部時,該點才在擊中擊不中變換的輸出中。
Hit-miss示意圖:
在A圖中尋找B圖所示的圖像目標的位置。
解:
1、確定結構元素
既然是尋找圖B所示形狀,選取H為圖B所示的形狀。再選一個小窗口W,W包含H,M=W-H。如下圖所示:
2、求
3、求
4、求
/// <summary> /// 擊中擊不中:只能處理位深度為8的512*512圖像 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void hitMiss_Click(object sender, EventArgs e) { if (curBitmap != null) { hitmiss hitAndMiss = new hitmiss(); if (hitAndMiss.ShowDialog() == DialogResult.OK) { Rectangle rect = new Rectangle(0, 0, curBitmap.Width, curBitmap.Height); BitmapData bmpData = curBitmap.LockBits(rect, ImageLockMode.ReadWrite, curBitmap.PixelFormat); IntPtr ptr = bmpData.Scan0; int bytes = curBitmap.Width * curBitmap.Height; byte[] grayValues = new byte[bytes]; Marshal.Copy(ptr, grayValues, 0, bytes); //得到擊中結構元素 bool[] hitStru = hitAndMiss.GetHitStruction; //得到擊不中結構元素 bool[] missStru = hitAndMiss.GetMissStruction; byte[] tempArray = new byte[bytes]; byte[] temp1Array = new byte[bytes]; byte[] temp2Array = new byte[bytes]; for (int i = 0; i < bytes; i++) { //原圖補集 tempArray[i] = (byte)(255 - grayValues[i]); temp1Array[i] = 255; temp2Array[i] = 255; } //應用擊中結構元素進行腐蝕運算 for (int i = 1; i < curBitmap.Height - 1; i++) { for (int j = 1; j < curBitmap.Width - 1; j++) { //當前位置是黑色或者是擊中結構元素的這一位置沒有選中 if ((grayValues[(i - 1) * curBitmap.Width + j - 1] == 0 || hitStru[0] == false) && (grayValues[(i - 1) * curBitmap.Width + j] == 0 || hitStru[1] == false) && (grayValues[(i - 1) * curBitmap.Width + j + 1] == 0 || hitStru[2] == false) && (grayValues[i * curBitmap.Width + j - 1] == 0 || hitStru[3] == false) && (grayValues[i * curBitmap.Width + j] == 0 || hitStru[4] == false) && (grayValues[i * curBitmap.Width + j + 1] == 0 || hitStru[5] == false) && (grayValues[(i + 1) * curBitmap.Width + j - 1] == 0 || hitStru[6] == false) && (grayValues[(i + 1) * curBitmap.Width + j] == 0 || hitStru[7] == false) && (grayValues[(i + 1) * curBitmap.Width + j + 1] == 0 || hitStru[8] == false)) { temp1Array[i * curBitmap.Width + j] = 0; } } } //應用擊不中結構元素進行腐蝕運算 for (int i = 1; i < curBitmap.Height - 1; i++) { for (int j = 1; j < curBitmap.Width - 1; j++) { ////當前位置是黑色或者是擊不中結構元素的這一位置沒有選中 if ((tempArray[(i - 1) * curBitmap.Width + j - 1] == 0 || missStru[0] == false) && (tempArray[(i - 1) * curBitmap.Width + j] == 0 || missStru[1] == false) && (tempArray[(i - 1) * curBitmap.Width + j + 1] == 0 || missStru[2] == false) && (tempArray[i * curBitmap.Width + j - 1] == 0 || missStru[3] == false) && (tempArray[i * curBitmap.Width + j] == 0 || missStru[4] == false) && (tempArray[i * curBitmap.Width + j + 1] == 0 || missStru[5] == false) && (tempArray[(i + 1) * curBitmap.Width + j - 1] == 0 || missStru[6] == false) && (tempArray[(i + 1) * curBitmap.Width + j] == 0 || missStru[7] == false) && (tempArray[(i + 1) * curBitmap.Width + j + 1] == 0 || missStru[8] == false)) { temp2Array[i * curBitmap.Width + j] = 0; } } } //兩個腐蝕運算結果再進行“與”操作 for (int i = 0; i < bytes; i++) { if (temp1Array[i] == 0 && temp2Array[i] == 0) { tempArray[i] = 0; } else { tempArray[i] = 255; } } grayValues = (byte[])tempArray.Clone(); Marshal.Copy(grayValues, 0, ptr, bytes); curBitmap.UnlockBits(bmpData); } Invalidate(); } }