iT邦幫忙

2023 iThome 鐵人賽

DAY 24
0

一、介紹

我們已經學會如何擷取影像的輪廓並進行幾何運算。接下來,我們要討論輪廓的實際應用,其中最常見的應用之一就是輪廓比對。透過輪廓比對,我們可以判定影像上的物體是否為我們尋找的目標,輪廓比對需要一種量化的方式來進行,而這就是我們今天要談的主題 - "矩"

矩是一種數學工具,用於描述輪廓的高階幾何特徵,例如質心面積方向等。在輪廓比對中,我們使用矩來量化的表示物體的形狀和結構,讓得我們可以進行辨識和比對。

二、原理

1. 空間矩(Spatial Moments)

空間矩可以用來計算兩種輸入,分別為輪廓點或是單通道影像。矩由以下的數學公式表達:
https://ithelp.ithome.com.tw/upload/images/20230927/20161732cU0U7Pu6Ff.png

  • f(x,y):二值化影像或是輪廓點,每個像素值不是0就是1。
  • N:圖片f(x,y)的高度。
  • M:圖片f(x,y)的寬度。
  • n:圖片像素的總數,同NxM。
  • i:圖片第幾個像素點。xiyi為像素點的座標。
  • p、q:因數,表示矩的階數,假設m01,p跟q的值分別是01

1) 零階矩

零階矩m00數學的定義是將非零的像素點加總起來,這個矩的數學定義會根據你的輸入改變,但仍可以使用相同的公式:

  • 二值化影像:非零像素的總面積。
  • 輪廓點:輪廓的周長,或是稱輪廓的面積也行,因為輪廓的周長即為面積。

https://ithelp.ithome.com.tw/upload/images/20230927/20161732RnCXnmR6bh.png

2) 一階矩

一階矩包含了m10m01,和零階矩同理,將p、q其中一方帶入1就是一階矩。一階矩描述的是座標的加總值,m10描述的是圖片中相素值不為0或輪廓點的X座標值加總,m01則是描述Y座標值加總。
https://ithelp.ithome.com.tw/upload/images/20230927/20161732JioeHbOjuw.png

如果分別對m10、m01除以m00,會得到物體的x、y座標平均值,即為物體的中心點。

https://ithelp.ithome.com.tw/upload/images/20230927/201617325vxOGLjFTC.png

https://ithelp.ithome.com.tw/upload/images/20230927/201617322UKWiPNzwp.png

2. 中心矩(Central Moment)

假設今天有兩個一樣的物體,其中一個被平移到不同的位置時中心點的座標會改變,造成空間矩改變,影響兩物體比對的結果。我們希望求出來的矩不會受平移關係影響。中心矩為了解決這個問題,先求出物體的中心座標,並將物體的中心平移到原點O(0,0),中心矩的數學公式如下:
https://ithelp.ithome.com.tw/upload/images/20230927/20161732ArZN4PmmEO.png

一階中心矩會將物體的質心移到坐標原點O(0,0),因此後續中心矩的計算不再受到平移的影響。這是中心矩的一個重要特性,使物體的形狀特徵不受位置的影響,這對於物體、輪廓辨識非常有用。

https://ithelp.ithome.com.tw/upload/images/20230927/20161732mnj9t7Y5nr.png

1) 零階中心矩

零階中心矩的輸出會等於m00,因為因素p、q為0,消掉了後面的算式。
https://ithelp.ithome.com.tw/upload/images/20230927/201617324C8CL7EN8M.png

2) 一階中心矩

只要是一階中心矩如μ10μ01,由於質心坐標 x̄ 和 ȳ 是通過計算物體輪廓上所有點的坐標並求平均值得到的。在前面的敘述我們知道了一階矩描述的是座標的加總值。然而在計算中心矩時,實際上已經將物體的中心移到了坐標原點O(0,0),所以輪廓點座標的總和為0。
https://ithelp.ithome.com.tw/upload/images/20230927/20161732qhXbvXuCZt.png

舉個例子,假設我們有一顆公平的五面骰,骰到的點數的機率平均,我們使用P(X=1)這個機率質量函數來表示骰到1的機率為多少。如果我們對這個機率質量函數做一階中心矩,會發生什麼事情呢?沒有錯,一階的中心矩結果為0。
https://ithelp.ithome.com.tw/upload/images/20230927/20161732oNNbiWNSGM.png

3. 正規中心矩

我們求出了中心矩來避免物體發生平移時矩的改變。然而,假設今天有兩個一樣的物體,其中一個被放大或縮小時中心矩會改變。我們希望求出來的矩不會受縮放關係影響,因此需要對中心矩進行正規化。可以看到下列數學公式,正規中心矩是基於中心矩除以m00的因素pq次方,來達到正規化:
https://ithelp.ithome.com.tw/upload/images/20230927/20161732FEdl4d1cor.png

https://ithelp.ithome.com.tw/upload/images/20230927/20161732435Ch4mZYa.png

4. Hu不變矩(Hu's invariant moments)

最後,我們解決了上述各種操作對矩的影響,包括平移縮放。然而我們還沒有考慮旋轉、鏡射對矩的影響,為了解決旋轉、鏡射對矩的影響,Hu不變矩因此誕生。Hu不變矩是正規中心矩的線性組合,以下公式為Hu矩的數學定義:
https://ithelp.ithome.com.tw/upload/images/20230927/20161732FBBWt1jJE6.png

下圖是一張測試圖,圖片中字母經過了旋轉、平移、縮放。對照下表Hu矩輸出結果,你會發現其實同一個字母的Hu矩差異不大。

https://ithelp.ithome.com.tw/upload/images/20230927/20161732gYD9wd5WXl.png

1) S字母

輪廓 hu0 hu1 hu2 hu3 hu4 hu5 hu6
2 0.84 0.26 0.00 0.00 0.00 -0.00 0.00
8 0.82 0.25 0.01 0.00 0.00 0.00 0.00
11 0.74 0.20 0.00 0.00 0.00 0.00 0.00
12 0.76 0.21 0.00 0.00 0.00 0.00 0.00
14 0.74 0.20 0.00 0.00 -0.00 0.00 0.00
15 0.73 0.20 0.00 0.00 0.00 0.00 0.00

2) L字母

輪廓 hu0 hu1 hu2 hu3 hu4 hu5 hu6
0 0.96 0.61 0.27 0.04 -0.00 -0.00 -0.00
3 0.93 0.57 0.36 0.08 0.01 0.03 -0.01
4 0.85 0.47 0.23 0.04 0.00 0.01 -0.00
5 0.84 0.46 0.19 0.03 0.00 0.00 -0.00
6 0.83 0.45 0.23 0.04 0.00 0.01 -0.00
7 1.07 0.80 0.51 0.15 0.03 0.09 -0.02

3) I字母

輪廓 hu0 hu1 hu2 hu3 hu4 hu5 hu6
1 0.95 0.88 0.00 0.00 0.00 0.00 0.00
9 0.76 0.55 0.00 0.00 0.00 0.00 0.00
10 0.76 0.55 0.00 0.00 0.00 0.00 -0.00
13 0.95 0.88 0.00 0.00 0.00 0.00 0.00

三、程式碼

1. 逐行解釋

1) 取得二值化影像或輪廓的Moments

計算給定輪廓contours[i]矩(moments),包含了不同階的矩的值,例如零階矩、一階矩、二階矩...等等。

cv::Moments moments=cv::moments(contours[i]);
  1. cv::moments(contours[i]):計算輪廓contours[i]的矩(moments)。

函式的輸出式cv::Moments用於儲存輸出結果,空間矩、中心矩、正規中心矩的結果都會被存放到這個類(Class)裡面。

  • 空間矩(Spatial Moments)
    • 零階矩:m00
    • 一階矩:m10、m01
    • 二階矩:m20、m11、m02
    • 三階矩:m30、m21、m12、m03
  • 中心矩(Central Moments)
    • 二階矩:mu20、mu11、mu02
    • 三階矩:mu30、mu21、mu12、mu03
  • 正規中心矩(Central Normalized Moments)
    • 二階矩:nu20、nu11、nu02
    • 三階矩:nu30、nu21、nu12、nu03

2) 取得Hu矩

這段程式碼使用OpenCV函式庫來計算影像的Hu不變矩。可以看到,我們用一個double陣列hu去接Hu矩的結果。

double hu[7];
cv::HuMoments(moments,hu);
  • moments:這是一個包含影像矩的數據結構,由前面cv::moments函式計算得知。
  • hu:這是一個數組,用於存儲計算出的Hu不變矩的值。

2.完整程式碼

  1. 讀取影像:使用cv::imread函式讀取了一張灰階影像,並將其存儲在image變數中。
  2. 二值化影像:使用cv::threshold函式對image進行了二值化處理,使用了Otsu閾值化方法,結果存儲在binary變數中。
  3. 創建了兩個空白影像:contours_imagemoments_image,這兩個影像將用於繪製輪廓和輪廓的重心。
  4. 找到影像中的輪廓:使用cv::findContours函式找到了影像中的外部輪廓,這些輪廓存儲在contours向量中。
  5. 使用for迴圈遍歷所有找到的輪廓。
    • 對於每個輪廓,計算其包圍矩形、標記輪廓索引號、繪製輪廓以及計算輪廓的Hu不變矩。
    • 輪廓索引號和Hu不變矩的信息被輸出到控制台上。
#include <iostream>
#include <opencv2/opencv.hpp>
#include "opencv2/core/utils/logger.hpp"
using namespace std;

int main()
{
    cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_ERROR);
	cv::Mat image = cv::imread("C:\\Users\\vince\\Downloads\\test_image5.jpg",cv::IMREAD_GRAYSCALE);
	cv::Mat binary;
	cv::threshold(image, binary, 0, 255, cv::THRESH_OTSU);

	cv::Mat contours_image=cv::Mat::zeros(cv::Size(binary.cols,binary.rows),CV_8UC3);
	cv::Mat moments_image=cv::Mat::zeros(cv::Size(binary.cols,binary.rows),CV_8UC3);
	vector<vector<cv::Point>> contours;
	cv::findContours(binary, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_TC89_KCOS);
	for (int i = 0;i < contours.size();i++) {
		cv::Rect bounding_rect=cv::boundingRect(contours[i]);
		cv::putText(contours_image, to_string(i), cv::Point(bounding_rect.x, bounding_rect.y - 5),cv::FONT_HERSHEY_COMPLEX,0.5,cv::Scalar(0,255,0));
		cv::drawContours(contours_image, contours, i, cv::Scalar(0,255,0));


		cv::Moments moments=cv::moments(contours[i]);
		cv::Point center=cv::Point(moments.m10/moments.m00,moments.m01/moments.m00);
		cv::circle(contours_image, center, 3, cv::Scalar(0, 255, 255), -1);

		double hu[7];
		cv::HuMoments(moments,hu);
		printf("|%d|%.2f|%.2f|%.2f|%.2f|%.2f|%.2f|%.2f|\n",i,hu[0],hu[1],hu[2],hu[3],hu[4],hu[5],hu[6]);
	}
	cv::imshow("Contours",contours_image);
	cv::waitKey(0);
	return 0;
}

3.輸出結果

1) 測試圖

https://ithelp.ithome.com.tw/upload/images/20230927/20161732mFkFPbT2eF.jpg

輸出的影像會畫出物體的邊緣並加上數字標號,並用黃色點標出字母的中心。在Console端會看到每個輪廓的Hu矩

https://ithelp.ithome.com.tw/upload/images/20230927/20161732QtbPksOz97.png

|0|0.96|0.61|0.27|0.04|-0.00|-0.00|-0.00|
|1|0.95|0.88|0.00|0.00|0.00|0.00|0.00|
|2|0.84|0.26|0.00|0.00|0.00|-0.00|0.00|
|3|0.93|0.57|0.36|0.08|0.01|0.03|-0.01|
|4|0.85|0.47|0.23|0.04|0.00|0.01|-0.00|
|5|0.84|0.46|0.19|0.03|0.00|0.00|-0.00|
|6|0.83|0.45|0.23|0.04|0.00|0.01|-0.00|
|7|1.07|0.80|0.51|0.15|0.03|0.09|-0.02|
|8|0.82|0.25|0.01|0.00|0.00|0.00|0.00|
|9|0.76|0.55|0.00|0.00|0.00|0.00|0.00|
|10|0.76|0.55|0.00|0.00|0.00|0.00|-0.00|
|11|0.74|0.20|0.00|0.00|0.00|0.00|0.00|
|12|0.76|0.21|0.00|0.00|0.00|0.00|0.00|
|13|0.95|0.88|0.00|0.00|0.00|0.00|0.00|
|14|0.74|0.20|0.00|0.00|-0.00|0.00|0.00|
|15|0.73|0.20|0.00|0.00|0.00|0.00|0.00|

上一篇
【Day23】使用OpenCV進行輪廓的幾何運算
下一篇
【Day25】OpenCV 使用Hu矩:比較形狀相似度
系列文
圖解C++影像處理與OpenCV應用:從基礎到高階,深入學習超硬核技術!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言