Skip to content

Commit cf5edf0

Browse files
committed
geometric_detection & contour
1 parent ed7d415 commit cf5edf0

File tree

4 files changed

+143
-16
lines changed

4 files changed

+143
-16
lines changed

CV.ipynb

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@
1919
"from geometric_transform import *\n",
2020
"from region_growing import *\n",
2121
"from edge_detection import *\n",
22-
"from dft import *\n"
22+
"from dft import *\n",
23+
"from morphology_process import *\n",
24+
"from geometry import *\n",
25+
"from contour import *"
2326
]
2427
},
2528
{
2629
"cell_type": "code",
27-
"execution_count": null,
30+
"execution_count": 2,
2831
"metadata": {},
2932
"outputs": [],
3033
"source": [
@@ -109,6 +112,12 @@
109112
"\n",
110113
" elif method == '形态学处理':\n",
111114
" processed_image = morphology_process(image, sub_option)\n",
115+
"\n",
116+
" elif method == '几何形状检测':\n",
117+
" processed_image = geometric_dectect(image, sub_option)\n",
118+
"\n",
119+
" elif method == '轮廓提取':\n",
120+
" processed_image = contours(image, sub_option, threshold)\n",
112121
" \n",
113122
" \n",
114123
" \n",
@@ -123,7 +132,7 @@
123132
{
124133
"data": {
125134
"application/vnd.jupyter.widget-view+json": {
126-
"model_id": "4d6c6bf9fd1a4f3598e6b825cb3d8e79",
135+
"model_id": "a0f5536bf2dc4ff0aa5be60830e15400",
127136
"version_major": 2,
128137
"version_minor": 0
129138
},
@@ -137,12 +146,12 @@
137146
{
138147
"data": {
139148
"application/vnd.jupyter.widget-view+json": {
140-
"model_id": "6474644e0a12414b82a83da934ed6083",
149+
"model_id": "38bed6e2e9c3438a8222f46142c55dc2",
141150
"version_major": 2,
142151
"version_minor": 0
143152
},
144153
"text/plain": [
145-
"Dropdown(description='处理方法:', options=('灰度化', '二值化', '滤波', '锐化', '几何变换', '图像分割', '边缘检测', '频域滤波'), value='灰度化')"
154+
"Dropdown(description='处理方法:', options=('灰度化', '二值化', '滤波', '锐化', '几何变换', '图像分割', '边缘检测', '频域滤波', '形态学处理', '几何形…"
146155
]
147156
},
148157
"metadata": {},
@@ -151,7 +160,7 @@
151160
{
152161
"data": {
153162
"application/vnd.jupyter.widget-view+json": {
154-
"model_id": "5bf6822fc8b04386a972a853c84efbbf",
163+
"model_id": "efc34abc5469467cbc3b0f8754ec4326",
155164
"version_major": 2,
156165
"version_minor": 0
157166
},
@@ -165,12 +174,12 @@
165174
{
166175
"data": {
167176
"application/vnd.jupyter.widget-view+json": {
168-
"model_id": "efcecbb0d851489d84443616f17e215d",
177+
"model_id": "9040e55ae6b84282ba8aecff544d9f70",
169178
"version_major": 2,
170179
"version_minor": 0
171180
},
172181
"text/plain": [
173-
"IntSlider(value=127, description='阈值:', layout=Layout(visibility='hidden'), max=255)"
182+
"IntSlider(value=99, description='阈值:', layout=Layout(visibility='visible'), max=255)"
174183
]
175184
},
176185
"metadata": {},
@@ -179,7 +188,7 @@
179188
{
180189
"data": {
181190
"application/vnd.jupyter.widget-view+json": {
182-
"model_id": "d265ad29c3264622b8171a6f11b8f524",
191+
"model_id": "410ce78eb16e46869a94ec05c3a043e5",
183192
"version_major": 2,
184193
"version_minor": 0
185194
},
@@ -193,7 +202,7 @@
193202
{
194203
"data": {
195204
"application/vnd.jupyter.widget-view+json": {
196-
"model_id": "2d2cdd9bf06f4d39832329fff88a755b",
205+
"model_id": "042c3e14d54341edb8ea3d561cbd625e",
197206
"version_major": 2,
198207
"version_minor": 0
199208
},
@@ -207,7 +216,7 @@
207216
{
208217
"data": {
209218
"application/vnd.jupyter.widget-view+json": {
210-
"model_id": "c2832dbb2fa345feb2869736a9b65ace",
219+
"model_id": "a5cb4cd65ef2421ca03352e2d8bcd7b7",
211220
"version_major": 2,
212221
"version_minor": 0
213222
},
@@ -221,7 +230,7 @@
221230
{
222231
"data": {
223232
"application/vnd.jupyter.widget-view+json": {
224-
"model_id": "4b3de52f7b4c4a99a5de86544574cbf9",
233+
"model_id": "b65e54fce3b14a589c8b716f0e2609a5",
225234
"version_major": 2,
226235
"version_minor": 0
227236
},
@@ -235,7 +244,7 @@
235244
{
236245
"data": {
237246
"application/vnd.jupyter.widget-view+json": {
238-
"model_id": "0ac2dc72a40b4490ad9e1c4a5244d20b",
247+
"model_id": "1f38c97f4dd74b7a82956a21bc2fd75e",
239248
"version_major": 2,
240249
"version_minor": 0
241250
},
@@ -256,7 +265,8 @@
256265
"\n",
257266
"# 方法选择下拉菜单\n",
258267
"method_dropdown = widgets.Dropdown(\n",
259-
" options=['灰度化', '二值化', '滤波', '锐化', '几何变换', '图像分割', '边缘检测', '频域滤波', '形态学处理'], \n",
268+
" options=['灰度化', '二值化', '滤波', '锐化', '几何变换', '图像分割', \n",
269+
" '边缘检测', '频域滤波', '形态学处理', '几何形状检测', '轮廓提取'], \n",
260270
" value='灰度化',\n",
261271
" description='处理方法:'\n",
262272
")\n",
@@ -333,7 +343,10 @@
333343
" elif edge_method == 'Canny算子':\n",
334344
" low_threshold = edge_detection_params.children[1].value\n",
335345
" high_threshold = edge_detection_params.children[2].value\n",
336-
" processed_image = canny_edge_detection(image, low_threshold, high_threshold) \n",
346+
" processed_image = canny_edge_detection(image, low_threshold, high_threshold) \n",
347+
" elif method_dropdown.value == '轮廓提取':\n",
348+
" if sub_option_dropdown.value == '轮廓绘制':\n",
349+
" processed_image = process_image(image, method_dropdown.value, sub_option_dropdown.value, threshold=threshold_slider.value)\n",
337350
" else:\n",
338351
" processed_image = process_image(image, method_dropdown.value, sub_option_dropdown.value)\n",
339352
" \n",
@@ -422,6 +435,16 @@
422435
"\n",
423436
" elif change['new'] == '形态学处理':\n",
424437
" sub_option_dropdown.options = ['膨胀', '腐蚀', '开运算', '闭运算', '形态学梯度', '顶帽', '黑帽']\n",
438+
"\n",
439+
" elif change['new'] == '几何形状检测':\n",
440+
" sub_option_dropdown.options = ['轮廓检测']\n",
441+
" \n",
442+
" elif change['new'] == '轮廓提取':\n",
443+
" sub_option_dropdown.options = ['轮廓绘制']\n",
444+
" if sub_option_dropdown.value == '轮廓绘制':\n",
445+
" threshold_slider.layout.visibility = 'visible'\n",
446+
" else:\n",
447+
" threshold_slider.layout.visibility = 'hidden'\n",
425448
" \n",
426449
"method_dropdown.observe(on_method_change, names='value')\n",
427450
"\n",
@@ -477,6 +500,11 @@
477500
" edge_detection_params.layout.visibility = 'visible'\n",
478501
" edge_detection_params.children[1].layout.visibility = 'visible' if change['new'] == 'Canny算子' else 'hidden'\n",
479502
" edge_detection_params.children[2].layout.visibility = 'visible' if change['new'] == 'Canny算子' else 'hidden'\n",
503+
" \n",
504+
" elif method_dropdown.value == '轮廓提取':\n",
505+
" if change['new'] == '轮廓绘制':\n",
506+
" threshold_slider.layout.visibility = 'visible'\n",
507+
"\n",
480508
"\n",
481509
"sub_option_dropdown.observe(on_sub_option_change, names='value')\n",
482510
"\n",

contour.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import cv2 as cv
2+
3+
def contours(img, sub_option, threshold):
4+
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # 转换为灰度图像
5+
img_blurred = cv.GaussianBlur(img_gray, (5, 5), 0) # 高斯滤波,去噪
6+
if sub_option == '轮廓绘制':
7+
# 二值化
8+
_, th_fixed = cv.threshold(img_blurred, threshold, 255, cv.THRESH_BINARY)
9+
10+
contours, hierarchy = cv.findContours(th_fixed, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) # 查找轮廓
11+
# 绘制轮廓
12+
img_contours = img.copy()
13+
cv.drawContours(img_contours, contours, -1, (0, 255, 0), 1)
14+
15+
return img_contours

geometry.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import numpy as np
2+
import cv2 as cv
3+
def detect_shapes(image):
4+
# 将图像转换为灰度图像
5+
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
6+
7+
# 应用高斯模糊以减少噪声并改进轮廓检测
8+
blurred = cv.GaussianBlur(gray, (5, 5), 0)
9+
10+
# 使用Canny边缘检测器检测边缘
11+
edges = cv.Canny(blurred, 50, 150)
12+
13+
# 查找边缘图像中的轮廓
14+
contours, _ = cv.findContours(edges.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
15+
16+
shapes = []
17+
18+
for contour in contours:
19+
# 计算轮廓的面积
20+
area = cv.contourArea(contour)
21+
22+
# 跳过面积过小的轮廓
23+
if area < 10:
24+
continue
25+
26+
# 近似轮廓
27+
epsilon = 0.02 * cv.arcLength(contour, True)
28+
approx = cv.approxPolyDP(contour, epsilon, True)
29+
30+
# 根据顶点数量确定形状
31+
if len(approx) == 3:
32+
shape = "triangle" # 三角形
33+
elif len(approx) == 4:
34+
# 检查是否为正方形或矩形
35+
x, y, w, h = cv.boundingRect(approx)
36+
aspect_ratio = float(w) / h
37+
if 0.95 <= aspect_ratio <= 1.05:
38+
shape = "square" # 正方形
39+
else:
40+
shape = "rectangle" # 矩形
41+
elif len(approx) == 5:
42+
shape = "pentagon" # 五边形
43+
elif len(approx) == 6:
44+
shape = "hexagon" # 六边形
45+
else:
46+
# 检查是否为圆形或椭圆
47+
area = cv.contourArea(contour)
48+
perimeter = cv.arcLength(contour, True)
49+
circularity = 4 * np.pi * (area / (perimeter ** 2))
50+
51+
if circularity > 0.8:
52+
shape = "circle" # 圆形
53+
else:
54+
# 拟合椭圆并检查其偏心率
55+
ellipse = cv.fitEllipse(contour)
56+
major_axis = max(ellipse[1])
57+
minor_axis = min(ellipse[1])
58+
eccentricity = np.sqrt(1 - (minor_axis / major_axis) ** 2)
59+
60+
if eccentricity < 0.1:
61+
shape = "circle" # 圆形
62+
elif eccentricity < 0.7:
63+
shape = "ellipse" # 椭圆
64+
else:
65+
shape = "unknown"
66+
67+
# 在图像上绘制轮廓并在中心位置标注形状名称
68+
cv.drawContours(image, [contour], -1, (0, 255, 0), 2)
69+
M = cv.moments(contour)
70+
if M["m00"] != 0:
71+
cX = int(M["m10"] / M["m00"])
72+
cY = int(M["m01"] / M["m00"])
73+
cv.putText(image, shape, (cX, cY), cv.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
74+
75+
shapes.append(shape)
76+
77+
return image, shapes
78+
79+
def geometric_dectect(image, sub_option):
80+
if sub_option == '轮廓检测':
81+
result_image = image.copy()
82+
result_image, detected_shapes = detect_shapes(result_image)
83+
print(f"检测到的形状: {detected_shapes}")
84+
return result_image

region_growing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def region_growing(image, seed_point, threshold):
1414
- segmented_image: 分割后的图像,其中区域标记为255。
1515
"""
1616
image = cv.cvtColor(image, cv.COLOR_BGR2GRAY) # 转换为灰度图像
17-
image = np.float32(image)
17+
image = np.float32(image) # 转换为浮点数
1818

1919
# 获取图像的尺寸
2020
rows, cols = image.shape

0 commit comments

Comments
 (0)