You are on page 1of 98

ĐẠI HỌC QUỐC GIA THÀNH PHỐ HỒ CHÍ MINH

TRƯỜNG ĐẠI HỌC BÁCH KHOA


………………….oOo………………..

BÀI TẬP LỚN SỐ 1 – Nhóm 1


Sinh viên thực hiện: Giáo viên hướng dẫn:

1 /2010003 Tống Phước Thanh An 1/ Lê Đức Hạnh (LT)

2/ 2010284 Lê Đức Huy 2/ Lương Thanh Nhật (BT)

3/ 2014467 Phạm Ngô Châu Thanh

4/ 2014240 Nguyễn Đắc Đạo Quang

5/ 2013798 Trịnh Vũ Quốc Minh


TP. HỒ CHÍ MINH - NĂM 2022
Cho ma trận sau:

1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1
1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1
1

Câu a) Tìm các blob bằng tay (tọa độ, góc, chu vi, diện tích) và lập trình tìm blob sử dụng hu-moment
❖ Tính tay

Moment của ảnh xám được tính:

𝑚𝑘𝑙 = ∑ ∑ 𝑖 𝑘 𝑗 𝑙 𝐴𝑖,𝑗
𝑖 𝑗

Hình 1:

𝑚00 = ∑ ∑ 𝑖 0 𝑗 0 𝐴𝑖,𝑗 = 32
𝑖 𝑗

𝑚01 = ∑ ∑ 𝑖 0 𝑗 1 𝐴𝑖,𝑗 = 6 × 1 + 6 × 2 + 4 × 3 + 4 × 4 + 6 × 5 + 6 × 6 = 112


𝑖 𝑗

𝑚10 = ∑ ∑ 𝑖 1 𝑗 0 𝐴𝑖,𝑗 = 6 × 1 + 6 × 2 + 4 × 3 + 4 × 4 + 6 × 5 + 6 × 6 = 112


𝑖 𝑗

Trọng tâm của hình 1:


𝑚01 112
𝑥1 = = = 3,5
𝑚00 32
𝑚10 112
𝑥2 = = = 3,5
𝑚00 32
Tìm góc xoay của hình 1:

𝑚11 = ∑ ∑ 𝑖 1 𝑗 1 𝐴𝑖,𝑗 = 392


𝑖 𝑗
𝑚20 = ∑ ∑ 𝑖 2 𝑗 0 𝐴𝑖,𝑗 = 496
𝑖 𝑗

𝑚02 = ∑ ∑ 𝑖 0 𝑗 2 𝐴𝑖,𝑗 = 496


𝑖 𝑗

Suy ra:
𝑚11 392
𝑚′11 = −𝑥1 𝑦1 = − 3,5 × 3,5 = 0
𝑚00 32
𝑚20 496
𝑚′20 = − 𝑦12 = − 3,5 × 3,5 = 3,25
𝑚00 32
𝑚02 496
𝑚′02 = − 𝑥12 = − 3,5 × 3,5 = 3,25
𝑚00 32
1 2𝑚′11 1 0
θ= arctan( ) = arctan( ) = 0°
2 𝑚′20 − 𝑚′02 2 0
Chu vi và diện tích hình 1:
Perimeter = 4 × 1 + 16 × 1 = 20
3
Area = 5 × 5 − (8 × + 4) = 18
8
Hình 2:

𝑚00 = ∑ ∑ 𝑖 0 𝑗 0 𝐴𝑖,𝑗 = 39
𝑖 𝑗

𝑚01 = ∑ ∑ 𝑖 0 𝑗 1 𝐴𝑖,𝑗 = 487


𝑖 𝑗

𝑚10 = ∑ ∑ 𝑖 1 𝑗 0 𝐴𝑖,𝑗 = 156


𝑖 𝑗

Trọng tâm của hình 1:


𝑚01 487
𝑥2 = = = 12,49
𝑚00 39
𝑚10 156
𝑦2 = = =4
𝑚00 39
Tìm góc xoay của hình 1:

𝑚11 = ∑ ∑ 𝑖 1 𝑗 1 𝐴𝑖,𝑗 = 1948


𝑖 𝑗

𝑚20 = ∑ ∑ 𝑖 2 𝑗 0 𝐴𝑖,𝑗 = 748


𝑖 𝑗

𝑚02 = ∑ ∑ 𝑖 0 𝑗 2 𝐴𝑖,𝑗 = 6287


𝑖 𝑗

Suy ra:
𝑚11 1948
𝑚′11 = −𝑥1 𝑦2 = − 12,49 × 4 = 0
𝑚00 39
𝑚20 748
𝑚′20 = − 𝑦22 = − 4 × 4 = 3,18
𝑚00 39
𝑚02 6287
𝑚′02 = − 𝑥12 = − 12,49 × 12,49 = 5,28
𝑚00 39
1 2𝑚′11 1 0
θ= arctan( )= arctan( ) = 0°
2 𝑚′20 − 𝑚′02 2 3,18 − 5,28
Chu vi và diện tích hình 2:
Perimeter = 9 × 1 + 6 × (1 + √2)/2 + 5 ×× √2 = 23.31
1 1 5 3 1 5 3 1
Area = 25 + 3 × + 11 × + 2 × + 4 × − (6 + 4 × + 2 × + 2 × + 2 × ) = 24
4 2 8 8 4 8 8 2
Hình 3:

𝑚00 = ∑ ∑ 𝑖 0 𝑗 0 𝐴𝑖,𝑗 = 36
𝑖 𝑗

𝑚01 = ∑ ∑ 𝑖 0 𝑗 1 𝐴𝑖,𝑗 = 126


𝑖 𝑗

𝑚10 = ∑ ∑ 𝑖 1 𝑗 0 𝐴𝑖,𝑗 = 414


𝑖 𝑗

Trọng tâm của hình 1:


𝑚01 126
𝑥3 = = = 3,5
𝑚00 36
𝑚10 414
𝑦3 = = = 11,5
𝑚00 36
Tìm góc xoay của hình 1:

𝑚11 = ∑ ∑ 𝑖 1 𝑗 1 𝐴𝑖,𝑗 = 1449


𝑖 𝑗

𝑚20 = ∑ ∑ 𝑖 2 𝑗 0 𝐴𝑖,𝑗 = 4954


𝑖 𝑗

𝑚02 = ∑ ∑ 𝑖 0 𝑗 2 𝐴𝑖,𝑗 = 634


𝑖 𝑗

Suy ra:
𝑚11 1449
𝑚′11 = −𝑥1 𝑦1 = − 3,5 × 11,5 = 0
𝑚00 36
𝑚20 4954
𝑚′20 = − 𝑦32 = − 11,52 = 5,36
𝑚00 36
𝑚02 634
𝑚′02 = − 𝑥32 = − 3,52 = 5,36
𝑚00 36
1 2𝑚′11 1 0
θ= arctan( )= arctan( ) = 0°
2 𝑚′20 − 𝑚′02 2 5,36 − 5,36
Chu vi và diện tích hình 3:
Perimeter = 8 × 1 + 8 × (1 + √2)/2 + 4 × √2 = 23.31
1 3 3 1
Area = 32 + 12 × +8× − (16 + 8 × + 8 × ) = 18
2 8 8 2
Hình 4:

𝑚00 = ∑ ∑ 𝑖 0 𝑗 0 𝐴𝑖,𝑗 = 21
𝑖 𝑗

𝑚01 = ∑ ∑ 𝑖 0 𝑗 1 𝐴𝑖,𝑗 = 235


𝑖 𝑗
𝑚10 = ∑ ∑ 𝑖 1 𝑗 0 𝐴𝑖,𝑗 = 252
𝑖 𝑗

Trọng tâm của hình 1:


𝑚01 235
𝑥4 = = = 11,19
𝑚00 21
𝑚10 252
𝑦4 = = = 11,5
𝑚00 21
Tìm góc xoay của hình 1:

𝑚11 = ∑ ∑ 𝑖 1 𝑗 1 𝐴𝑖,𝑗 = 2820


𝑖 𝑗

𝑚20 = ∑ ∑ 𝑖 2 𝑗 0 𝐴𝑖,𝑗 = 3122


𝑖 𝑗

𝑚02 = ∑ ∑ 𝑖 0 𝑗 2 𝐴𝑖,𝑗 = 2663


𝑖 𝑗

Suy ra:
𝑚11 2820
𝑚′11 = −𝑥4 𝑦4 = − 11,19 × 12 = 0
𝑚00 21
𝑚20 3122
𝑚′20 = − 𝑦42 = − 122 = 4,67
𝑚00 21
𝑚02 2663
𝑚′02 = − 𝑥42 = − 11,192 = 1,58
𝑚00 21
1 2𝑚′11 1 0
θ= arctan( )= arctan( ) = 0°
2 𝑚′20 − 𝑚′02 2 4,67 − 1,58
Chu vi và diện tích hình 3:

Perimeter = 7 × 1 + 2 × (1 + √2)/2 + 7 × √2 = 19,31


1 1 1 1 3 1
Area = 9 + 13 × +1× +2× − (4 + 3 × +2× +3× )= 9
2 4 8 2 8 4

❖ Lập trình

Ta tiến hành lập trình theo trình tự như giải thuật tính tay, khai báo ma trận đầu vào, sử dụng hàm conectedComponents
gán nhãn cho từng vùng trong ảnh.
float scr_array[18 * 17] = {

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,0,

0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,

0,1,1,0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,

0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,

0,1,1,1,1,1,1,0,0,1,1,1,1,0,1,1,1,0,

0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,

0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,

0,0,1,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0,

0,1,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,

1,1,0,0,0,0,1,1,0,0,1,1,1,0,0,0,0,0,

1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,0,0,0,

1,1,0,0,0,0,1,1,0,0,1,0,0,1,1,0,0,0,

1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,0,0,0,
0,1,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,

0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,

0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,

};

Mat scr = Mat(17, 18, CV_32F, scr_array);

scr.convertTo(scr, CV_8U);

threshold(scr, scr, 0, 255, THRESH_BINARY);

Mat label = Mat::zeros(17, 18,CV_8U);

//cout << scr;

//label các blob

connectedComponents(scr, label, 4, 4); // chọn connective = 4

label.convertTo(label, CV_8U);

Kết quả hình ảnh sau khi label


Hình 1.1. Kết quả gán nhãn hình ảnh

Tiếp theo, ta tiến hành tìm các đường bao của hình bằng hàm findContours, sử dụng tham số mode là RETR_TREE
để lấy tất cả các đường biên và tạo phân cấp của các đường lồng nhau, sau đó trích xuất các đường biên trong và ngoài

vector <vector<Point>> contours;

vector <vector<Point>> out_contours;

vector <vector<Point>> in_contours;

vector <Vec4i> hierarchy;

Mat drawing = Mat::zeros(scr.size(),CV_8U);

findContours(scr, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));


// Hàm contours trả về kết quả contour từ cuối mảng lên đầu mảng, reverse lại cho đúng thứ tự

reverse(contours.begin(), contours.end()); //Các contours ngoài cùng là contour số lẻ 1,3,5,7

// Trích xuất các contour nằm ngoài cùng

for (int i = 1; i < contours.size(); i+=2)

out_contours.push_back(contours[i]);

// Trích xuất các contour bên trong

for (int i = 0; i < contours.size(); i += 2)

in_contours.push_back(contours[i]);

Tiến hành tìm các moment để tính toán trọng tâm bao gồm m0,0 , m0,1 , m1,0 để tính toán trọng tâm, vì các hình ảnh
đều có tính đối xứng, tức là tâm của các đường biên trong cùng 1 ảnh trùng nhau, do đó, ta chỉ cần moment của đường biên
ngoài cùng để tìm trọng tâm.

vector<Moments> mu_out(out_contours.size());
vector<Moments> mu_in(in_contours.size());

for (int i = 0; i < out_contours.size(); i++)

mu_out[i] = moments(out_contours[i], false);

// Tìm momen các contour bên trong

for (int i = 0; i < in_contours.size(); i++)

mu_in[i] = moments(in_contours[i], false);

Tìm diện tích bằng cách đếm các ô đã label

// Diện tích tìm bằng cách đếm ô

for (int i = 0; i < label.cols; i++)

for (int j = 0; j < label.rows; j++)


{

if (label.at<uchar>(Point(i, j)) == 1)

area[0]++;

else if (label.at<uchar>(Point(i, j)) == 2)

area[1]++;

else if (label.at<uchar>(Point(i, j)) == 3)

area[2]++;

else if (label.at<uchar>(Point(i, j)) == 4)

{
area[3]++;

Sau khi tính được các moment, tìm trọng tâm và hướng của vật

// tính trọng tâm


mc[i] = Point2f(mu_out[i].m10 / mu_out[i].m00, mu_out[i].m01 / mu_out[i].m00);
cout << endl << "toa do tam hinh " << i + 1 << " " << mc[i] << endl;
// tính góc
u11 = (mu_out[i].m11 / mu_out[i].m00) - (mu_out[i].m10 / mu_out[i].m00) * (mu_out[i].m01 /
mu_out[i].m00);
u20 = (mu_out[i].m20 / mu_out[i].m00) - (mu_out[i].m10 / mu_out[i].m00) * (mu_out[i].m10 /
mu_out[i].m00);
u02 = (mu_out[i].m02 / mu_out[i].m00) - (mu_out[i].m01 / mu_out[i].m00) * (mu_out[i].m01 /
mu_out[i].m00);
if (u20 == u02) // xử lí trường hợp 0/0
{
u20 += 1e-8;
}
theta[i] = 0.5 * atan(2 * u11 / (u20 - u02));
theta[i] = theta[i] * 180 / CV_PI;u02));
theta[i] = theta[i] * 180 / CV_PI;

Có thể sử dụng hàm arcLength để tìm chu vi thông qua các contour biên ngoài cùng

perimeter[i] = arcLength(out_contours[i], true);

Tìm diện tích bằng moment

area_func[i] = mu_out[i].m00 - mu_in[i].m00;

Kết quả về trọng tâm, góc, chu vi, diện tích


Hình 1.2. Kết quả tìm Blob bằng moment
Nhận xét: Có sự khác nhau về cách tính diện tích bằng moment (và hàm contoursArea) so với cách thức đếm ô là do
trong OpenCV không xem 1 trên đường biên là 1 đơn vị diện tích mà xem như 1 điểm (point) tọa độ, sau đó thực hiện tìm
diện tích dựa vào các tọa độ đó ví dụ như out_contours 1 có 4 tọa độ là (1,1), (1,6) (6,1) (6,6) như vậy diện tích sẽ tính theo
phép tính sau 𝑆 = (𝑦2 − 𝑦1 )(𝑥2 − 𝑥1 ) = (6 − 1)(6 − 1) = 25 trong khi đó nếu xem 1 ô là 1 đơn vị diện tích thay vì 1 điểm
thì diện tích đếm được là 36

Phụ lục: CODE

#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <math.h>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
float scr_array[18 * 17] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,0,
0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,
0,1,1,0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,
0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,
0,1,1,1,1,1,1,0,0,1,1,1,1,0,1,1,1,0,
0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,
0,0,1,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,
1,1,0,0,0,0,1,1,0,0,1,1,1,0,0,0,0,0,
1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,0,0,0,
1,1,0,0,0,0,1,1,0,0,1,0,0,1,1,0,0,0,
1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,0,0,0,
0,1,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,
0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
};
Mat scr = Mat(17, 18, CV_32F, scr_array);
scr.convertTo(scr, CV_8U);
threshold(scr, scr, 0, 255, THRESH_BINARY);
Mat label = Mat::zeros(17, 18,CV_8U);
//cout << scr;
//label các blob
connectedComponents(scr, label, 4, 4); // chọn connective = 4
label.convertTo(label, CV_8U);
cout << "Ket qua sau khi label" << endl << label<<endl;
// tìm contours của ảnh
vector <vector<Point>> contours;
vector <vector<Point>> out_contours;
vector <vector<Point>> in_contours;
vector <Vec4i> hierarchy;
Mat drawing = Mat::zeros(scr.size(),CV_8U);
findContours(scr, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0,
0));
// Hàm contours trả về kết quả contour từ cuối mảng lên đầu mảng, reverse lại cho đúng thứ
tự
reverse(contours.begin(), contours.end()); //Các contours ngoài cùng là contour số lẻ 1,3,5,7
// Trích xuất các contour nằm ngoài cùng
for (int i = 1; i < contours.size(); i+=2)
{
out_contours.push_back(contours[i]);
}
// Trích xuất các contour bên trong
for (int i = 0; i < contours.size(); i += 2)
{
in_contours.push_back(contours[i]);
}
for (int i = 0; i < out_contours.size(); i++)
{
cout << out_contours[i] << endl;
}
// Tìm momen các contour ngoài cùng
vector<Moments> mu_out(out_contours.size());
vector<Moments> mu_in(in_contours.size());
for (int i = 0; i < out_contours.size(); i++)
{
mu_out[i] = moments(out_contours[i], false);
}
// Tìm momen các contour bên trong
for (int i = 0; i < in_contours.size(); i++)
{
mu_in[i] = moments(in_contours[i], false);
}
//Tọa độ tâm và góc
double theta[4]; // mảng chứa 4 giá trị theta
double perimeter[4]; // mảng chứa chu vi
double area[4] = { 0, 0, 0, 0 }; //mảng chứa giá trị diện tích tìm theo cách đếm label
double area_func[4] = { 0,0,0,0 }; // mảng chứa giá trị diện tích tìm theo m00
double u11, u20, u02;
vector<Point2f>mc(mu_out.size());
// Diện tích tìm bằng cách đếm ô
for (int i = 0; i < label.cols; i++)
{
for (int j = 0; j < label.rows; j++)
{
if (label.at<uchar>(Point(i, j)) == 1)
{
area[0]++;
}
else if (label.at<uchar>(Point(i, j)) == 2)
{
area[1]++;
}
else if (label.at<uchar>(Point(i, j)) == 3)
{
area[2]++;
}
else if (label.at<uchar>(Point(i, j)) == 4)
{
area[3]++;
}

}
}
// Diện tích tính theo tọa độ
for (int i = 0; i < in_contours.size(); i++)
{
area_func[i] = mu_out[i].m00 - mu_in[i].m00;
}
for (int i = 0; i < out_contours.size(); i++)
{
// tính trọng tâm
mc[i] = Point2f(mu_out[i].m10 / mu_out[i].m00, mu_out[i].m01 / mu_out[i].m00);
cout << endl << "toa do tam hinh " << i + 1 << " " << mc[i] << endl;
// tính góc
u11 = (mu_out[i].m11 / mu_out[i].m00) - (mu_out[i].m10 / mu_out[i].m00) *
(mu_out[i].m01 / mu_out[i].m00);
u20 = (mu_out[i].m20 / mu_out[i].m00) - (mu_out[i].m10 / mu_out[i].m00) *
(mu_out[i].m10 / mu_out[i].m00);
u02 = (mu_out[i].m02 / mu_out[i].m00) - (mu_out[i].m01 / mu_out[i].m00) *
(mu_out[i].m01 / mu_out[i].m00);
if (u20 == u02) // xử lí trường hợp 0/0
{
u20 += 1e-8;
}
theta[i] = 0.5 * atan(2 * u11 / (u20 - u02));
theta[i] = theta[i] * 180 / CV_PI;
cout << "goc hinh " << i + 1 << ": " << theta[i] << " do" << endl;
perimeter[i] = arcLength(out_contours[i], true);
cout << "chu vi hinh " << i + 1 << ": " << perimeter[i] << endl;
cout << "dien tich tinh theo cach dem pixel hinh " << i + 1 << ": " << area[i] << endl;
cout << "dien tich tinh theo toa do hinh " << i + 1 << ": " << area_func[i] << endl;
}
}

Câu b) Thining bằng tay và lập trình, output kết quả từng bước

❖ Tính tay

Ma trận ban đầu:

1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1
1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1
1

Gọi B là kernel và A là pixel trong hình áp dụng công thức sau

• Với ( 𝐴 ⊛ 𝐵) là hit and miss transform


• Với 𝐵 = {𝐵1 , 𝐵2 , 𝐵3 , … , 𝐵𝑛 }, Bi là ma trận xoay của 𝐵𝑖−1
• 𝐴 ⊗ {𝐵} = ((… ((𝐴 ⊗ 𝐵1 ) ⊗ 𝐵2 ) … ) ⊗ 𝐵𝑛 )
Với Kernel {𝐵}

-1 -1 -1 -1 -1 1 -1 1 1 1 1 1 1 1 -1 1
1 1 1 -1 1 1 -1 1 1 -1 1 -1 1 1 -1 1 1
1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1

𝐵1 𝐵2 𝐵3 𝐵4 𝐵5 𝐵6 𝐵7

𝐴 ⊛ 𝐵1

-1 -1 -1
1
1 1 1

Cho kernel 𝐵1 trượt trên hình A


Trượt ma trận tại vị trí (0,0), ta có:

1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1
1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1
1

Ta có kết quả trả về ở lần trượt thứ nhất là 0. Do đó ma trận result là:
0
Ở lượt trượt thứ hai, tương tự như lần đầu.
Ta có kết quả trả về giống như ở lần trượt thứ nhất là 0. Do đó ma trận result là:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Trượt ở các vị trí đặc biệt:
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1
1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1
1
Ta có kết quả trả về là 1, do đó ma trận result là:
1
Làm tương tự cho đến hết ma trận
Ta được kết quả 𝐴 ⊛ 𝐵1

1 1 1 1 1 1 1
1

1 1 1 1

1 1
𝐴1 = 𝐴 ⊗ 𝐵1 = 𝐴 − (𝐴 ⊛ 𝐵1 ):

1 1
1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1
1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1
1
𝐴1 ⊛ 𝐵2

-1 -1
1 1 -1
1 1

Cho kernel 𝐵2 trượt trên 𝐴1, ta được kết quả 𝐴1 ⊛ 𝐵2

1
1

1
1
𝐴2 = 𝐴1 ⊗ 𝐵2 = 𝐴1 − ( 𝐴1 ⊛ 𝐵2 ) :

1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1
1
1 1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1
1

𝐴2 ⊛ 𝐵3
1 -1
1 1 -1
1 -1

Cho kernel 𝐵3 trượt trên 𝐴2 , ta được kết quả 𝐴2 ⊛ 𝐵3

1
1
1

1 1
1 1
1

𝐴3 = 𝐴2 ⊗ 𝐵3 = 𝐴2 − (𝐴2 ⊛ 𝐵3 ) :

1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1
1
1 1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1
1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1
1

𝐴3 ⊛ 𝐵4

1 1
1 1 -1
-1 -1

Cho kernel 𝐵4 trượt trên 𝐴3 , ta được kết quả 𝐴3 ⊛ 𝐵4

1
1
1
1

𝐴4 = 𝐴3 ⊗ 𝐵4 = 𝐴3 − (𝐴3 ⊛ 𝐵4 ) :

1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1
1 1
1
1 1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1
1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1
1

𝐴4 ⊛ 𝐵5
1 1 1
1
-1 -1 -1
Cho kernel 𝐵5 trượt trên 𝐴4 , ta được kết quả 𝐴4 ⊛ 𝐵5

1 1 1
1 1

𝐴5 = 𝐴4 ⊗ 𝐵5 = 𝐴4 − (𝐴4 ⊛ 𝐵5 ) :
1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1

1
1 1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1
1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1
1

𝐴5 ⊛ 𝐵6
1 1
-1 1 1
-1 -1

Cho kernel 𝐵6 trượt trên 𝐴5 , ta được kết quả 𝐴5 ⊛ 𝐵6

1
𝐴6 = 𝐴5 ⊗ 𝐵6 = 𝐴5 − (𝐴5 ⊛ 𝐵6 ) :

1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1

1
1 1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1
1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1
1

𝐴6 ⊛ 𝐵7
-1 1
-1 1 1
-1 1

Cho kernel 𝐵7 trượt trên 𝐴6 , ta được kết quả 𝐴6 ⊛ 𝐵7

1
1 1
1
𝐴7 = 𝐴6 ⊗ 𝐵7 = 𝐴6 − (𝐴6 ⊛ 𝐵7 ) , ta được kết quả cuối cùng:

1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1

1
1 1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1
1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1
1
❖ Lâp trình

Tiến hành nhập ma trận A phân ngưỡng và xuất ra như sau:

Mat src =(Mat_<uchar>(17,18)<<


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0,
0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0,
0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0,
1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);

src.convertTo(src, CV_8U);
threshold(src, src, 0, 255, THRESH_BINARY);
cout << "input image: " << endl << src << endl;
namedWindow("src", WINDOW_NORMAL);
imshow("src", src*255);
Kết quả được xuất ra:

Ma trận ban đầu sau khi phân ngưỡng Binary image before thining

Khai báo các kernel Bi:


Mat B1 = (Mat_<int>(3, 3) << -1, -1, -1, 0, 1, 0, 1, 1, 1);
Mat B2 = (Mat_<int>(3, 3) << 0, -1, -1, 1, 1, -1, 1, 1, 0);
Mat B3 = (Mat_<int>(3, 3) << 1, 0, -1, 1, 1, -1, 1, 0, -1);
Mat B4 = (Mat_<int>(3, 3) << 1, 1, 0, 1, 1, -1, 0, -1, -1);
Mat B5 = (Mat_<int>(3, 3) << 1, 1, 1, 0, 1, 0, -1, -1, -1);
Mat B6 = (Mat_<int>(3, 3) << 0, 1, 1, -1, 1, 1, -1, -1, 0);
Mat B7 = (Mat_<int>(3, 3) << -1, 0, 1, -1, 1, 1, -1, 0, 1);
Dùng hàm morphologyex() với inputarray là ma trận src(A) ban đầu , outputarray là dst , op là MORPH_HITMISS,
kernel là các kernel tổng hợp ở trên.

Sau đó lấy SRC- DST để thu được ma trận Ai, tiếp tục thực hiện thao tác dùng hàm và trừ hai ma trận để thu được
kết quả Thining:

Image after thining with kernel B1 Image after thining with kernel B2
Image after thining with kernel B3 Image after thining with kernel B4

Image after thining with kernel B6


Image after thining with kernel B5
Binary image after thining
Image after thining in final

Source code:
#include<opencv2/opencv.hpp>
#include<iostream>

using namespace cv;


using namespace std;

int main() {

Mat dst;

Mat src =(Mat_<uchar>(17,18)<<


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0,
0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0,
0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0,
1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);

src.convertTo(src, CV_8U);
threshold(src, src, 0, 255, THRESH_BINARY);
cout << "input image: " << endl << src << endl;
namedWindow("src", WINDOW_NORMAL);
imshow("src", src*255);

Mat B1 = (Mat_<int>(3, 3) << -1, -1, -1, 0, 1, 0, 1, 1, 1);


Mat B2 = (Mat_<int>(3, 3) << 0, -1, -1, 1, 1, -1, 1, 1, 0);
Mat B3 = (Mat_<int>(3, 3) << 1, 0, -1, 1, 1, -1, 1, 0, -1);
Mat B4 = (Mat_<int>(3, 3) << 1, 1, 0, 1, 1, -1, 0, -1, -1);
Mat B5 = (Mat_<int>(3, 3) << 1, 1, 1, 0, 1, 0, -1, -1, -1);
Mat B6 = (Mat_<int>(3, 3) << 0, 1, 1, -1, 1, 1, -1, -1, 0);
Mat B7 = (Mat_<int>(3, 3) << -1, 0, 1, -1, 1, 1, -1, 0, 1);
morphologyEx(src, dst, MORPH_HITMISS, B1);
src = src - dst;
cout << "Image after thinning with kernel B1: " << endl << src << endl;

morphologyEx(src, dst, MORPH_HITMISS, B2);


src = src - dst;
cout << "Image after thinning with kernel B2: " << endl << src << endl;

morphologyEx(src, dst, MORPH_HITMISS, B3);


src = src - dst;
cout << "Image after thinning with kernel B3: " << endl << src << endl;

morphologyEx(src, dst, MORPH_HITMISS, B4);


src = src - dst;
cout << "Image after thinning with kernel B4: " << endl << src << endl;

morphologyEx(src, dst, MORPH_HITMISS, B5);


src = src - dst;
cout << "Image after thinning with kernel B5: " << endl << src << endl;

morphologyEx(src, dst, MORPH_HITMISS, B6);


src = src - dst;
cout << "Image after thinning with kernel B6: " << endl << src << endl;

morphologyEx(src, dst, MORPH_HITMISS, B7);


src = src - dst;

namedWindow("dst", WINDOW_NORMAL);
imshow("dst", src * 255);
cout << "Image after thinning in final: " << endl << src << endl;
waitKey(0);
}

Câu c) Tính tay và lập trình bằng giải thuật skeleton:


❖ Tính tay
Ta có ma trận A ban đầu sau như sau:

1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1
1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1
1

Tiến hành thêm padding 0 cho ma trận ban đầu để tính toán:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 0 0
0 0 1 1 0 0 1 1 0 0 1 1 1 1 0 1 1 1 0 0
0 0 1 1 0 0 1 1 0 0 1 1 0 0 0 0 1 1 1 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 0 1 1 1 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0
0 0 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 0 1 1 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

❖ Công thức Skeleton:

❖ Công thức erosion:


𝐴 ⊝ 𝐵 = {𝑧|(𝐵) 𝑍 ⊆ 𝐴}
Trong đó:
A: ma trận điểm ảnh của ảnh nhị phân
B: phần tử cấu trúc (structuring element)
❖ Công thức dilation:

𝐴⨁𝐵 = {𝑧| (𝐵̂)𝑍 ∩ 𝐴 ≠ ∅

Trong đó:
A: ma trận điểm ảnh của ảnh nhị phân
B: phần tử cấu trúc (structuring element)
Ta sẽ sử dụng kernel B sau để tiến hành skeleton:
x 1 x
1 1 1
x 1 x

Các bước thực hiện:


1. Erode ma trận src1 được ma trận eroded.
2. Tiến hành dilate ma trận eroded ta được ma trận tempt.
3. Sau đó lấy ma trận src1 trừ cho ma trận tempt vừa tìm được ta được ma trận tempt mới.
4. Cộng dồn các ma trận tempt mới này với nhau để được kết quả skeleton.
5. Gán ma trận eroded cho src1 và tiếp tục lại các bước như trên cho đến khi ma trận src là ma trận 0 thì ta thu được kết
quả skeleton cuối cùng.

Bước 1: Đầu tiên, tiến hành erode ma trận A bẳng kernel B:


Trượt lần lượt kernel B lên ma trận A. Ở vị trí (0,0) ta có:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 0 0
0 0 1 1 0 0 1 1 0 0 1 1 1 1 0 1 1 1 0 0
0 0 1 1 0 0 1 1 0 0 1 1 0 0 0 0 1 1 1 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 0 1 1 1 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0
0 0 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 0 1 1 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Erode: Lần lượt so sánh tại các vị trí có giá trị bằng 1 của kernel B. Nếu giống nhau hoàn toàn thì giá trị trả về là 1. Ngược
lại là 0\
Nhận xét: Các vị trí có giá trị 1 tại vị trí đang xét trong ma trận A không trùng khớp với kernel B.
Ta có kết quả trả về ở lần trượt thứ nhất là 0. Do đó ma trận result là:
0
Tại vị trí (0,1) ta có:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 0 0
0 0 1 1 0 0 1 1 0 0 1 1 1 1 0 1 1 1 0 0
0 0 1 1 0 0 1 1 0 0 1 1 0 0 0 0 1 1 1 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 0 1 1 1 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0
0 0 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 0 1 1 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Ta có kết quả trả về ở lần trượt thứ hai là 0. Do đó ma trận result là:
0 0
Ta trượt ở các vị trí đặc biệt:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 0 0
0 0 1 1 0 0 1 1 0 0 1 1 1 1 0 1 1 1 0 0
0 0 1 1 0 0 1 1 0 0 1 1 0 0 0 0 1 1 1 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 0 1 1 1 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0
0 0 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 0 1 1 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Nhận xét: Các vị trí có giá trị 1 tại vị trí kernel B hoàn toàn trùng khớp với vị trí đang xét trong ma trận A.
Suy ra kết quả trả về là 1. Do đó ma trận result là:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1

Trượt ở vị trí khác:


0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 0 0
0 0 1 1 0 0 1 1 0 0 1 1 1 1 0 1 1 1 0 0
0 0 1 1 0 0 1 1 0 0 1 1 0 0 0 0 1 1 1 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 0 1 1 1 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0
0 0 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 0 1 1 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 1 1 1 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Ta có kết quả trả về là 1. Do đó ma trận result là:


0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 1

Trượt lần lượt kernel B đến hết ma trận A ta thu được ma trận result là:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Bước 2: Dilate ma trận đã erode ở trên


Đầu tiên thêm padding 0 cho ma trận eroded:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Trượt ma trận tại vị trí (0,0), ta có:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Nhận xét: Các vị trí có giá trị 1 tại vị trí kernel B không trùng khớp với bất kì vị trí nào đang xét trong ma trận đã eroded.
Ta có kết quả trả về ở lần trượt thứ nhất là 0. Do đó ma trận result là:
0

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Nhận xét: Ở lượt trượt thứ hai, tương tự như lần đầu.
Ta có kết quả trả về ở lần trượt thứ nhất là 0. Do đó ma trận result là:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Trượt ở các vị trí đặc biệt :


0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Nhận xét: Vị trí giá trị 1 tại vị trí đang xét có 1 điểm trùng hợp với giá trị 1 trong kernel.
Ta có kết quả trả về là 1. Do đó ma trận Result là:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1

Trượt ở các vị trí đặc biệt khác:


0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Ta có kết quả trả về là 1. Do đó ma trận Result là:


0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0

Tiếp tục trượt lần lượt đến hết ma trận eroded ta được:
Trượt lần lượt đến hết ma trận eroded, ta thu được ma trận Result cuối cùng là:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0
0 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 0 0
0 0 1 0 0 1 0 0 0 1 1 1 1 0 1 1 1 0
0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0
0 1 1 1 1 1 1 0 0 1 1 1 1 0 1 1 1 0
0 0 1 0 0 1 0 0 0 0 1 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Bước 3: Lấy ma trận ban đầu trừ đi ma trận dilated:
Kết quả sau khi subtract:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 1 1 0 1 0 0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0
0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0
1 1 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0
1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0
1 1 0 0 0 0 1 1 0 1 0 0 1 1 0 0 0 0
1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0
0 1 1 1 1 1 1 0 0 1 1 1 0 0 0 0 0 0
0 0 1 1 1 1 0 0 0 1 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0

Vì đây là lần thực hiện đầu tiên nên ma trận subtract cũng là ma trận skeleton.
Gán giá trị thu được ở ma trận subtract ngược lại cho ma trận src để thực hiện tiếp lần 2.
2) Thực hiện lần 2:
Erode ma trận đã src (ma trận eroded ở lần thực hiện thứ nhất) :
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Nhận xét: Có thể dễ dàng nhận ra được răng không có vị trí nào thỏa để erode xuất hiện giá trị 1. Do đó ma trận
eroded2 như sau:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Vì không hề có giá trị 1 nào xuất hiện trong ma trận vì thể ma trận dilated cũng là ma trận 0.
Tiến hành lấy ma trận eroded trừ cho ma trận dilated ta ra được ma trận subtract2:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Cuối cùng ta sẽ tổng hợp ma trận skeleton ở lần thực hiện đầu và ma trận subtract2 sẽ thu được ma trận skeleton2:
Kết quả thu được cho ma trận skeleton2:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0
0 0 1 0 0 1 0 0 0 1 0 0 1 0 1 0 0 0
0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 1 0 0
0 1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0
0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0
0 1 0 1 1 0 1 0 0 1 0 0 1 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0
0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0
1 1 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0
1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0
1 1 0 0 0 0 1 1 0 1 0 0 1 1 0 0 0 0
1 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0
0 1 1 1 1 1 1 0 0 1 1 1 0 0 0 0 0 0
0 0 1 1 1 1 0 0 0 1 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0

Nhận xét: ở hình 1 và 2 cơ bản đã thu được ảnh đẹp và hợp lí sau khi skeleton nhưng hình 3 và 4 vẫn giữ nguyên vì
thế tiến hành dùng những kernel khác để skeleton.
Vì về cơ bản 2 hình đầu tiên đã cho ra hình ảnh hợp lí rồi nên bây giờ chỉ tập trung detect 2 hình còn lại.
Ta dùng các kernel sau cho hình thứ 3 :
Kernel B1:

0 1 0
1 1 0
0 1 0
Kernel B2

0 1 0
0 1 1
0 1 0
Kernel B3

0 1 0
1 1 1
0 0 0
Kernel B4

0 0 0
1 1 1
0 1 0

Thực hiện skeleton bằng lần lượt từng kernel trên với ma trận ban đầu ta thu được các kết quả skeleton như sau:
Skeleton Kernel B1:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Skeleton Kernel B2:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Skeleton Kernel B3:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Skeleton kernel B4:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Ý tưởng : ta sẽ lọc lấy những vị trí điểm nào không hề trùng khớp nhau giữa từng cặp ma trận ( 1,2 và 3,4) , thực
hiện tương tự như lệnh bitwise_xor . Sau đó ta sẽ dùng ma trận hình 3 ban đầu để trừ đi những vị trí điểm lọc được
này.
Bitwise_or cặp ma trận đầu , ta thu được ma trận sau:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Sau khi lấy hình ban đầu trừ đi:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Làm tương tự cho cặp ma trận còn lại, ta được:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Cuối cùng , giao 2 ma trận lại ta được ma trận Result cuối cùng:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Nhận xét: Hình ảnh thu được đã đẹp và hợp lí hơn.


Hình 4:
Thực hiện tương tự các bước như trên với 2 kernel sau
Kernel B41

0 1 0
1 1 1
0 0 0
Kernel B42

0 0 0
1 1 1
0 1 0
Ta thu được ma trận Result là:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
❖ Lập trình
Ma trận hình ban đầu sau khi phân ngưỡng:

Structuring element:

Vòng lặp đê tiến hành skeleton:


bool done;
do
{
erode(src, eroded, element, Point(-1, -1), 1, BORDER_CONSTANT);
cout << "eroded" << endl << eroded << endl;
dilate(eroded, dilated, element);
cout << "dilate" << endl << dilated << endl;
subtract(src, dilated, subtracted);
cout << "subtract" << endl << subtracted << endl;
bitwise_or(skel, subtracted, skel);
cout << "skeleton" << endl << skel << endl;
eroded.copyTo(src);
done = (countNonZero(src) == 0);
} while (!done);

Ma trận cuối cùng sau khi skeleton:


Nhận xét: nhận thấy hình 3 và 4 chưa thể detect được bằng kernel trên nên ta sẽ tiến hành tách 2 hình 3 và 4 ra để
tiện cho việc detect
Dùng lệnh connectedComponents để tách các ảnh ra:
connectedComponents(src, label, 8, 4);
label.convertTo(label, CV_8U);
cout << " label " << endl << label << endl;
Sau đó tiến hành phân ngưỡng để tách từng hình ra :
threshold(label, edge4, 3, 255, THRESH_BINARY);
threshold(label, tempt2, 3, 255, THRESH_TOZERO_INV);
threshold(tempt2, edge3, 2, 255, THRESH_BINARY);
threshold(tempt2, tempt1, 2, 255, THRESH_TOZERO_INV);
threshold(tempt1, edge2, 1, 255, THRESH_BINARY);
threshold(tempt1, edge1, 1, 255, THRESH_TOZERO_INV);
threshold(edge1, edge1, 0, 255, THRESH_BINARY);

Hình 1:
Hình 2:

Hình 3:
Hình 4:

Sau khi skeleton hình 3 bẳng các kernel kể trên phần tính tay ta sẽ dùng các lệnh sau để tiến hành tổng hợp hình lại :
bitwise_xor(skelB1, skelB2, skelB5);
subtract(edge34, skelB5, skelB5);
bitwise_xor(skelB3, skelB4, skelB6);
subtract(edge34, skelB6, skelB6);
bitwise_and(skelB5, skelB6, skel1);
Hình 4 làm tương tự nhưng chỉ với 2 kernel:
bitwise_xor(skelB41, skelB42, skelB43);
subtract(edge43, skelB43, skelB43);
bitwise_or(skelB43, skel1, skel);

Ma trận Result sau khi skeleton của Hình 3 và 4 là:


+Code: Hình 1 và 2
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

using namespace std;


using namespace cv;
int main() {
Mat src = (Mat_<uchar>(17, 19) <<
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0,
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0);
src.convertTo(src, CV_8U);
threshold(src, src, 0, 255, THRESH_BINARY);
cout << "input image" << endl << src << endl;
namedWindow("src", WINDOW_NORMAL);
imshow("src", src * 255);
Mat eroded, tempt, dilated, subtracted, skeleton;
Mat skel = Mat::zeros(17, 19, CV_8U);
skel.convertTo(skel, CV_8U);
Mat element = getStructuringElement(MORPH_CROSS, Size(3, 3), Point(-1, -1));
element.convertTo(element, CV_8U);
cout << "element" << endl << element << endl;
bool done;
do
{
erode(src, eroded, element, Point(-1, -1), 1, BORDER_CONSTANT);
cout << "eroded" << endl << eroded << endl;
dilate(eroded, dilated, element);
cout << "dilate" << endl << dilated << endl;
subtract(src, dilated, subtracted);
cout << "subtract" << endl << subtracted << endl;
bitwise_or(skel, subtracted, skel);
cout << "skeleton" << endl << skel << endl;
eroded.copyTo(src);
done = (countNonZero(src) == 0);
} while (!done);
cout << "skeleton" << endl << skel << endl;
namedWindow("skeleton", WINDOW_NORMAL);
imshow("skeleton", skel * 255);
waitKey();
}

Code Hình 3 và 4:
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

using namespace std;


using namespace cv;
int main() {
Mat src = (Mat_<uchar>(17, 19) <<
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0,
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0);
src.convertTo(src, CV_8U);
threshold(src, src, 0, 255, THRESH_BINARY);
cout << "input image" << endl << src << endl;
Mat eroded, tempt, dilated, subtracted, skeleton;
Mat skel = Mat::zeros(17, 19, CV_8U);
skel.convertTo(skel, CV_8U);
Mat element = getStructuringElement(MORPH_CROSS, Size(3, 3), Point(-1, -1));
element.convertTo(element, CV_8U);
Mat tempt1 = Mat::zeros(17, 19, CV_8U);
Mat tempt2 = Mat::zeros(17, 19, CV_8U);
Mat edge1 = Mat::zeros(17, 19, CV_8U);
Mat edge2 = Mat::zeros(17, 19, CV_8U);
Mat edge3 = Mat::zeros(17, 19, CV_8U);
Mat edge4 = Mat::zeros(17, 19, CV_8U);
Mat label;
connectedComponents(src, label, 8, 4);
label.convertTo(label, CV_8U);
cout << " label " << endl << label << endl;
threshold(label, edge4, 3, 255, THRESH_BINARY);
threshold(label, tempt2, 3, 255, THRESH_TOZERO_INV);
threshold(tempt2, edge3, 2, 255, THRESH_BINARY);
threshold(tempt2, tempt1, 2, 255, THRESH_TOZERO_INV);
threshold(tempt1, edge2, 1, 255, THRESH_BINARY);
threshold(tempt1, edge1, 1, 255, THRESH_TOZERO_INV);
threshold(edge1, edge1, 0, 255, THRESH_BINARY);
// cout << "Edge1" << endl << edge1 << endl;
// cout << "Edge2" << endl << edge2 << endl;
// cout << "Edge3" << endl << edge3 << endl;
// cout << "Edge4" << endl << edge4 << endl;
//Mat eroded, tempt, dilated, subtracted, skeleton;
//Mat skel = Mat::zeros(17, 19, CV_8U);
//skel.convertTo(skel, CV_8U);
Mat kernelB1 = (Mat_<int>(3, 3) <<
0, 1, 0,
1, 1, 0,
0, 1, 0);
Mat kernelB2 = (Mat_<int>(3, 3) <<
0, 1, 0,
0, 1, 1,
0, 1, 0);
Mat kernelB3 = (Mat_<int>(3, 3) <<
0, 1, 0,
1, 1, 1,
0, 0, 0);
Mat kernelB4 = (Mat_<int>(3, 3) <<
0, 0, 0,
1, 1, 1,
0, 1, 0);
kernelB1.convertTo(kernelB1, CV_8U);
kernelB2.convertTo(kernelB2, CV_8U);
kernelB3.convertTo(kernelB3, CV_8U);
kernelB4.convertTo(kernelB4, CV_8U);
Mat erodedB1, temptB1, dilatedB1, subtractedB1;
Mat erodedB2, temptB2, dilatedB2, subtractedB2;
Mat erodedB3, temptB3, dilatedB3, subtractedB3;
Mat erodedB4, temptB4, dilatedB4, subtractedB4;
Mat skelB1 = Mat::zeros(17, 19, CV_8U);
skelB1.convertTo(skelB1, CV_8U);
Mat skelB2 = Mat::zeros(17, 19, CV_8U);
skelB2.convertTo(skelB2, CV_8U);
Mat skelB3 = Mat::zeros(17, 19, CV_8U);
skelB3.convertTo(skelB3, CV_8U);
Mat skelB4 = Mat::zeros(17, 19, CV_8U);
skelB4.convertTo(skelB4, CV_8U);
Mat skelB5 = Mat::zeros(17, 19, CV_8U);
skelB5.convertTo(skelB5, CV_8U);
Mat skelB6 = Mat::zeros(17, 19, CV_8U);
skelB6.convertTo(skelB6, CV_8U);
Mat skel1 = Mat::zeros(17, 19, CV_8U);
skel1.convertTo(skel1, CV_8U);
Mat edge31, edge32, edge33, edge34, edge35;
edge3.copyTo(edge31);
edge3.copyTo(edge32);
edge3.copyTo(edge33);
edge3.copyTo(edge34);
edge3.copyTo(edge35);
bool done1, done2, done3, done4;
do
{
erode(edge3, erodedB1, kernelB1, Point(-1, -1), 1, BORDER_CONSTANT);
// cout << "erodedB1" << endl << erodedB1 << endl;
dilate(erodedB1, dilatedB1, kernelB1);
// cout << "dilateB1" << endl << dilatedB1 << endl;
subtract(edge3, dilatedB1, subtractedB1);
// cout << "subtractB1" << endl << subtractedB1 << endl;
bitwise_or(skelB1, subtractedB1, skelB1);
cout << "skeletonB1" << endl << skelB1 << endl;
erodedB1.copyTo(edge3);
done1 = (countNonZero(edge3) == 0);
} while (!done1);
do
{
erode(edge31, erodedB2, kernelB2, Point(-1, -1), 1, BORDER_CONSTANT);
// cout << "erodedB2" << endl << erodedB2 << endl;
dilate(erodedB2, dilatedB2, kernelB2);
// cout << "dilateB2" << endl << dilatedB2 << endl;
subtract(edge31, dilatedB2, subtractedB2);
// cout << "subtractB2" << endl << subtractedB2 << endl;
bitwise_or(skelB2, subtractedB2, skelB2);
cout << "skeleton" << endl << skelB2 << endl;
erodedB2.copyTo(edge31);
done2 = (countNonZero(edge31) == 0);
} while (!done2);
do
{
erode(edge32, erodedB3, kernelB3, Point(-1, -1), 1, BORDER_CONSTANT);
// cout << "erodedB3" << endl << erodedB2 << endl;
dilate(erodedB3, dilatedB3, kernelB3);
// cout << "dilateB3" << endl << dilatedB3 << endl;
subtract(edge32, dilatedB3, subtractedB3);
// cout << "subtractB3" << endl << subtractedB3 << endl;
bitwise_or(skelB3, subtractedB3, skelB3);
cout << "skeletonB3" << endl << skelB3 << endl;
erodedB3.copyTo(edge32);
done3 = (countNonZero(edge32) == 0);
} while (!done3);
do
{
erode(edge33, erodedB4, kernelB4, Point(-1, -1), 1, BORDER_CONSTANT);
// cout << "erodedB4" << endl << erodedB4 << endl;
dilate(erodedB4, dilatedB4, kernelB4);
// cout << "dilateB4" << endl << dilatedB4 << endl;
subtract(edge33, dilatedB4, subtractedB4);
// cout << "subtractB4" << endl << subtractedB4 << endl;
bitwise_or(skelB4, subtractedB4, skelB4);
cout << "skeletonB4" << endl << skelB4 << endl;
erodedB4.copyTo(edge33);
done4 = (countNonZero(edge33) == 0);
} while (!done4);
bitwise_xor(skelB1, skelB2, skelB5);
subtract(edge34, skelB5, skelB5);
bitwise_xor(skelB3, skelB4, skelB6);
subtract(edge34, skelB6, skelB6);
bitwise_and(skelB5, skelB6, skel1);
cout << "skeletonB5" << endl << skelB5 << endl;
cout << "skeletonB6" << endl << skelB6 << endl;
//Hinh 4
Mat erodedB41, temptB41, dilatedB41, subtractedB41;
Mat erodedB42, temptB42, dilatedB42, subtractedB42;
Mat edge41, edge42, edge43, edge44, edge45;
Mat skelB41 = Mat::zeros(17, 19, CV_8U);
skelB41.convertTo(skelB41, CV_8U);
Mat skelB42 = Mat::zeros(17, 19, CV_8U);
skelB42.convertTo(skelB42, CV_8U);
Mat skelB43 = Mat::zeros(17, 19, CV_8U);
skelB43.convertTo(skelB43, CV_8U);
Mat skelB44 = Mat::zeros(17, 19, CV_8U);
skelB44.convertTo(skelB44, CV_8U);
edge4.copyTo(edge41);
edge4.copyTo(edge42);
edge4.copyTo(edge43);
edge4.copyTo(edge44);
edge4.copyTo(edge45);
bool done5, done6;
do
{
erode(edge41, erodedB41, kernelB3, Point(-1, -1), 1, BORDER_CONSTANT);
cout << "erodedB41" << endl << erodedB41 << endl;
dilate(erodedB41, dilatedB41, kernelB3);
cout << "dilateB41" << endl << dilatedB41 << endl;
subtract(edge41, dilatedB41, subtractedB41);
cout << "subtractB41" << endl << subtractedB41 << endl;
bitwise_or(skelB41, subtractedB41, skelB41);
cout << "skeletonB41" << endl << skelB41 << endl;
erodedB41.copyTo(edge41);
done5 = (countNonZero(edge41) == 0);
} while (!done5);
do
{
erode(edge42, erodedB42, kernelB4, Point(-1, -1), 1, BORDER_CONSTANT);
cout << "erodedB42" << endl << erodedB42 << endl;
dilate(erodedB42, dilatedB42, kernelB4);
cout << "dilateB42" << endl << dilatedB42 << endl;
subtract(edge42, dilatedB42, subtractedB42);
cout << "subtractB42" << endl << subtractedB42 << endl;
bitwise_or(skelB42, subtractedB42, skelB42);
cout << "skeletonB42" << endl << skelB42 << endl;
erodedB42.copyTo(edge42);
done6 = (countNonZero(edge42) == 0);
} while (!done6);
bitwise_xor(skelB41, skelB42, skelB43);
subtract(edge43, skelB43, skelB43);
bitwise_or(skelB43, skel1, skel);
cout << "skeleton edge4" << endl << skelB43 << endl;
cout << "skeleton edge3 and 4" << endl << skel << endl;
namedWindow("skeleton", WINDOW_NORMAL);
imshow("skeleton", skel * 255);
waitKey();
}

You might also like