ํํ ๋ณํ (Hough Transform)
ํํ ๋ณํ (Hough Transform)¶
๊ฐ์¶
ํํ ๋ณํ์ ์ด๋ฏธ์ง์์ ์ง์ , ์ ๋ฑ์ ๊ธฐํํ์ ํํ๋ฅผ ๊ฒ์ถํ๋ ์๊ณ ๋ฆฌ์ฆ์ ๋๋ค. ์ฃ์ง ๊ฒ์ถ ๊ฒฐ๊ณผ์์ ํน์ ๋ชจ์์ ์ฐพ๋ ๋ฐ ์ฌ์ฉ๋๋ฉฐ, ์ฐจ์ ๊ฒ์ถ, ๋์ ๊ฒ์ถ ๋ฑ ๋ค์ํ ์์ฉ ๋ถ์ผ๊ฐ ์์ต๋๋ค.
๋ชฉ์ฐจ¶
- ํํ ๋ณํ ๊ฐ๋
- ํํ ์ง์ ๋ณํ
- ํ๋ฅ ์ ํํ ์ง์ ๋ณํ
- ํํ ์ ๋ณํ
- ํ๋ผ๋ฏธํฐ ํ๋ ์ ๋ต
- ์ฐจ์ ๊ฒ์ถ ๊ธฐ์ด
- ์ฐ์ต ๋ฌธ์
1. ํํ ๋ณํ ๊ฐ๋ ¶
ํํ ๊ณต๊ฐ (Hough Space)¶
๊ธฐ๋ณธ ์์ด๋์ด:
์ด๋ฏธ์ง ๊ณต๊ฐ์ ์ โ ํํ ๊ณต๊ฐ์ ๊ณก์
์ด๋ฏธ์ง ๊ณต๊ฐ์ ์ง์ โ ํํ ๊ณต๊ฐ์ ์
์ด๋ฏธ์ง ๊ณต๊ฐ (x, y) ํํ ๊ณต๊ฐ (ฯ, ฮธ)
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ โ โ โ
โ โข โ โ โฑโฒ โ
โ โฒ โ โโโถ โ โฑ โฒ โ
โ โฒ โ โ โฑ โข โฒ โ
โ โข โ โ โฑ โฒ โ
โ โ โ โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
์ง์ ์์ ์ ๋ค ์ ํ๋๋ก ํํ
์ง์ ์ ํํ:
y = mx + b (๊ธฐ์ธ๊ธฐ, y์ ํธ) โ ์์ง์ ํํ ๋ถ๊ฐ
ฯ = xยทcos(ฮธ) + yยทsin(ฮธ) โ ๊ทน์ขํ ํํ (์ ํธ)
ฯ: ์์ ์์ ์ง์ ๊น์ง์ ์์ง ๊ฑฐ๋ฆฌ
ฮธ: ์์ง์ ๊ณผ x์ถ์ด ์ด๋ฃจ๋ ๊ฐ๋
ํํ ๋ณํ ๊ณผ์ ¶
1. ์ฃ์ง ๊ฒ์ถ (Canny ๋ฑ)
โ
โผ
2. ๊ฐ ์ฃ์ง ์ ์ ๋ํด ๊ฐ๋ฅํ ๋ชจ๋ ์ง์ ๊ณ์ฐ
(ฮธ๋ฅผ 0ยฐ~180ยฐ ๋ณํ์ํค๋ฉฐ ฯ ๊ณ์ฐ)
โ
โผ
3. ๋์ ๋ฐฐ์ด(Accumulator)์ ํฌํ
โ
โผ
4. ์๊ณ๊ฐ ์ด์์ ํฌํ๋ฅผ ๋ฐ์ ์ = ์ง์
๋์ ๋ฐฐ์ด ์๊ฐํ:
ฮธ
0ยฐ โโโโโโโโโโโโโโโโโโโโโถ 180ยฐ
ฯ โ ยท ยท ยท ยท ยท ยท ยท ยท
-maxโ ยท ยท โ
ยท ยท ยท ยท ยท โ
: ๋ง์ ํฌํ
โ ยท ยท ยท ยท ยท โ
ยท ยท = ์ง์ ์กด์ฌ
โ ยท ยท ยท ยท ยท ยท ยท ยท
maxโ ยท ยท ยท ยท ยท ยท ยท ยท
โผ
๊ฐ๋จํ ์์ ¶
import cv2
import numpy as np
# ํํ ๋ณํ ์๊ฐํ
def visualize_hough_space(image_path):
"""ํํ ๊ณต๊ฐ ์๊ฐํ"""
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
edges = cv2.Canny(img, 50, 150)
# ํํ ์ง์ ๋ณํ (๋์ ๋ฐฐ์ด ๋ฐํ)
lines = cv2.HoughLines(edges, 1, np.pi/180, 100)
# ์๊ฐํ
result = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
if lines is not None:
for line in lines:
rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
# ์ง์ ๊ทธ๋ฆฌ๊ธฐ (์ ๋ฐฉํฅ์ผ๋ก ๊ธธ๊ฒ)
x1 = int(x0 + 1000 * (-b))
y1 = int(y0 + 1000 * (a))
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * (a))
cv2.line(result, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv2.imshow('Edges', edges)
cv2.imshow('Hough Lines', result)
cv2.waitKey(0)
visualize_hough_space('lines.jpg')
2. ํํ ์ง์ ๋ณํ¶
cv2.HoughLines() ํจ์¶
lines = cv2.HoughLines(image, rho, theta, threshold)
| ํ๋ผ๋ฏธํฐ | ์ค๋ช |
|---|---|
| image | ์ ๋ ฅ ์ด๋ฏธ์ง (8๋นํธ, ๋จ์ผ ์ฑ๋, ์ด์งํ๋ ์ฃ์ง ์ด๋ฏธ์ง) |
| rho | ฯ ํด์๋ (ํฝ์ ๋จ์, ๋ณดํต 1) |
| theta | ฮธ ํด์๋ (๋ผ๋์ ๋จ์, ๋ณดํต np.pi/180) |
| threshold | ์ง์ ์ผ๋ก ์ธ์ ํ ์ต์ ํฌํ ์ |
| lines | ๊ฒ์ถ๋ ์ง์ [(ฯ, ฮธ), ...] |
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ¶
import cv2
import numpy as np
def hough_lines_example(image_path):
"""ํ์ค ํํ ์ง์ ๊ฒ์ถ"""
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ์ฃ์ง ๊ฒ์ถ
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
# ํํ ์ง์ ๋ณํ
lines = cv2.HoughLines(
edges,
rho=1, # ฯ ํด์๋: 1 ํฝ์
theta=np.pi/180, # ฮธ ํด์๋: 1๋
threshold=100 # ์ต์ ํฌํ ์
)
result = img.copy()
if lines is not None:
print(f"๊ฒ์ถ๋ ์ง์ ์: {len(lines)}")
for line in lines:
rho, theta = line[0]
# ๊ทน์ขํ โ ์ง๊ต์ขํ ๋ณํ
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
# ์ง์ ๊ทธ๋ฆฌ๊ธฐ (๋ฌดํ ์ง์ )
x1 = int(x0 + 1000 * (-b))
y1 = int(y0 + 1000 * (a))
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * (a))
cv2.line(result, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv2.imshow('Original', img)
cv2.imshow('Edges', edges)
cv2.imshow('Hough Lines', result)
cv2.waitKey(0)
hough_lines_example('building.jpg')
์ํ์ /์์ง์ ๋ง ๊ฒ์ถ¶
import cv2
import numpy as np
def detect_horizontal_vertical_lines(image_path):
"""์ํ์ ๊ณผ ์์ง์ ๋ง ๊ฒ์ถ"""
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)
lines = cv2.HoughLines(edges, 1, np.pi/180, 100)
result = img.copy()
horizontal = []
vertical = []
if lines is not None:
for line in lines:
rho, theta = line[0]
# ๊ฐ๋๋ก ๋ถ๋ฅ (ํ์ฉ ์ค์ฐจ 5๋)
angle_deg = np.degrees(theta)
if 85 < angle_deg < 95: # ์์ง์ (ฮธ โ 90ยฐ)
vertical.append((rho, theta))
color = (255, 0, 0) # ํ๋
elif angle_deg < 5 or angle_deg > 175: # ์ํ์ (ฮธ โ 0ยฐ ๋๋ 180ยฐ)
horizontal.append((rho, theta))
color = (0, 255, 0) # ๋
น์
else:
continue
# ์ง์ ๊ทธ๋ฆฌ๊ธฐ
a = np.cos(theta)
b = np.sin(theta)
x0, y0 = a * rho, b * rho
x1, y1 = int(x0 + 1000 * (-b)), int(y0 + 1000 * (a))
x2, y2 = int(x0 - 1000 * (-b)), int(y0 - 1000 * (a))
cv2.line(result, (x1, y1), (x2, y2), color, 2)
print(f"์ํ์ : {len(horizontal)}๊ฐ")
print(f"์์ง์ : {len(vertical)}๊ฐ")
cv2.imshow('H/V Lines', result)
cv2.waitKey(0)
detect_horizontal_vertical_lines('grid.jpg')
3. ํ๋ฅ ์ ํํ ์ง์ ๋ณํ¶
cv2.HoughLinesP() ํจ์¶
ํ์ค ํํ vs ํ๋ฅ ์ ํํ:
ํ์ค ํํ (HoughLines):
- ๋ฌดํ ์ง์ ๋ฐํ (ฯ, ฮธ)
- ๋ชจ๋ ์ ๊ฒ์ฌ
- ๋๋ฆผ, ์ ํ
ํ๋ฅ ์ ํํ (HoughLinesP):
- ์ ๋ถ ๋ฐํ (x1, y1, x2, y2)
- ๋ฌด์์ ์ ์ํ๋ง
- ๋น ๋ฆ, ์ค์ฉ์
lines = cv2.HoughLinesP(image, rho, theta, threshold, minLineLength, maxLineGap)
| ํ๋ผ๋ฏธํฐ | ์ค๋ช |
|---|---|
| image | ์ ๋ ฅ ์ฃ์ง ์ด๋ฏธ์ง |
| rho | ฯ ํด์๋ |
| theta | ฮธ ํด์๋ |
| threshold | ์ต์ ํฌํ ์ |
| minLineLength | ์ต์ ์ ๋ถ ๊ธธ์ด |
| maxLineGap | ์ ๋ถ ์ฌ์ด ์ต๋ ํ์ฉ ๊ฐ๊ฒฉ |
| lines | ๊ฒ์ถ๋ ์ ๋ถ [(x1, y1, x2, y2), ...] |
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ¶
import cv2
import numpy as np
def hough_lines_p_example(image_path):
"""ํ๋ฅ ์ ํํ ์ง์ ๊ฒ์ถ"""
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)
# ํ๋ฅ ์ ํํ ๋ณํ
lines = cv2.HoughLinesP(
edges,
rho=1,
theta=np.pi/180,
threshold=50,
minLineLength=50, # ์ต์ 50ํฝ์
์ด์
maxLineGap=10 # 10ํฝ์
์ด๋ด ๊ฐ๊ฒฉ์ ์ฐ๊ฒฐ
)
result = img.copy()
if lines is not None:
print(f"๊ฒ์ถ๋ ์ ๋ถ ์: {len(lines)}")
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(result, (x1, y1), (x2, y2), (0, 255, 0), 2)
# ์ ๋ถ ๋์ ํ์
cv2.circle(result, (x1, y1), 5, (255, 0, 0), -1)
cv2.circle(result, (x2, y2), 5, (0, 0, 255), -1)
cv2.imshow('HoughLinesP', result)
cv2.waitKey(0)
hough_lines_p_example('document.jpg')
์ ๋ถ ํํฐ๋ง¶
import cv2
import numpy as np
def filter_lines(image_path, angle_threshold=30):
"""๊ฐ๋์ ๊ธธ์ด๋ก ์ ๋ถ ํํฐ๋ง"""
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 50, minLineLength=30, maxLineGap=10)
result = img.copy()
if lines is None:
return result
for line in lines:
x1, y1, x2, y2 = line[0]
# ์ ๋ถ ๊ธธ์ด ๊ณ์ฐ
length = np.sqrt((x2 - x1)**2 + (y2 - y1)**2)
# ๊ฐ๋ ๊ณ์ฐ (์ํ ๊ธฐ์ค)
if x2 - x1 != 0:
angle = np.degrees(np.arctan(abs(y2 - y1) / abs(x2 - x1)))
else:
angle = 90
# ํํฐ๋ง: ํน์ ๊ฐ๋ ์ดํ๋ง
if angle < angle_threshold:
color = (0, 255, 0) # ๊ฑฐ์ ์ํ
elif angle > 90 - angle_threshold:
color = (255, 0, 0) # ๊ฑฐ์ ์์ง
else:
continue # ๋๊ฐ์ ์ ๋ฌด์
cv2.line(result, (x1, y1), (x2, y2), color, 2)
cv2.imshow('Filtered Lines', result)
cv2.waitKey(0)
return result
filter_lines('building.jpg', angle_threshold=20)
์ ๋ถ ๋ณํฉ¶
import cv2
import numpy as np
from collections import defaultdict
def merge_lines(lines, angle_threshold=10, distance_threshold=20):
"""์ ์ฌํ ์ ๋ถ๋ค ๋ณํฉ"""
if lines is None or len(lines) == 0:
return []
# ์ ๋ถ์ ๊ฐ๋๋ณ๋ก ๊ทธ๋ฃนํ
groups = defaultdict(list)
for line in lines:
x1, y1, x2, y2 = line[0]
# ๊ฐ๋ ๊ณ์ฐ
angle = np.degrees(np.arctan2(y2 - y1, x2 - x1)) % 180
# ๊ฐ๋ ๊ทธ๋ฃน (angle_threshold ๋จ์๋ก ์์ํ)
angle_group = round(angle / angle_threshold) * angle_threshold
groups[angle_group].append(line[0])
merged = []
for angle, group_lines in groups.items():
if len(group_lines) == 1:
merged.append(group_lines[0])
continue
# ๊ฐ์ ๊ทธ๋ฃน ๋ด์์ ๊ฐ๊น์ด ์ ๋ถ๋ค ๋ณํฉ
# ๊ฐ๋จํ: ์ ์ฒด ์ ๋ค์ ์ต์/์ต๋ ์ขํ๋ก ํ๋์ ์ ๋ถ ์์ฑ
all_points = []
for x1, y1, x2, y2 in group_lines:
all_points.extend([(x1, y1), (x2, y2)])
all_points = np.array(all_points)
# ์ฃผ ๋ฐฉํฅ์ผ๋ก ์ ๋ ฌํ์ฌ ์ ๋์ ์ ํ
if abs(np.cos(np.radians(angle))) > 0.5:
# ์ํ์ ๊ฐ๊น์: x๋ก ์ ๋ ฌ
sorted_pts = sorted(all_points, key=lambda p: p[0])
else:
# ์์ง์ ๊ฐ๊น์: y๋ก ์ ๋ ฌ
sorted_pts = sorted(all_points, key=lambda p: p[1])
start = sorted_pts[0]
end = sorted_pts[-1]
merged.append([start[0], start[1], end[0], end[1]])
return merged
4. ํํ ์ ๋ณํ¶
cv2.HoughCircles() ํจ์¶
ํํ ์ ๋ณํ:
์ด๋ฏธ์ง์์ ์ ๊ฒ์ถ
์์ ๋ฐฉ์ ์: (x - a)ยฒ + (y - b)ยฒ = rยฒ
ํ๋ผ๋ฏธํฐ: ์ค์ฌ (a, b), ๋ฐ์ง๋ฆ r
3์ฐจ์ ๋์ ๋ฐฐ์ด ํ์ โ ๋นํจ์จ์
โ ๊ทธ๋๋์ธํธ ๊ธฐ๋ฐ ๋ฐฉ๋ฒ ์ฌ์ฉ (cv2.HOUGH_GRADIENT)
cv2.HOUGH_GRADIENT ๋์:
1. ์ฃ์ง ๊ฒ์ถ
2. ๊ฐ ์ฃ์ง ์ ์์ ๊ทธ๋๋์ธํธ ๋ฐฉํฅ์ผ๋ก ํฌํ
3. ์ค์ฌ ํ๋ณด ์ ์
4. ๋ฐ์ง๋ฆ ์ถ์
circles = cv2.HoughCircles(image, method, dp, minDist, param1, param2, minRadius, maxRadius)
| ํ๋ผ๋ฏธํฐ | ์ค๋ช |
|---|---|
| image | ์ ๋ ฅ ๊ทธ๋ ์ด์ค์ผ์ผ ์ด๋ฏธ์ง |
| method | ๊ฒ์ถ ๋ฐฉ๋ฒ (cv2.HOUGH_GRADIENT ๋๋ cv2.HOUGH_GRADIENT_ALT) |
| dp | ๋์ ๋ฐฐ์ด ํด์๋ ๋น์จ (1 = ์๋ณธ๊ณผ ๋์ผ) |
| minDist | ๊ฒ์ถ๋ ์ ์ค์ฌ ๊ฐ ์ต์ ๊ฑฐ๋ฆฌ |
| param1 | Canny ์ฃ์ง์ ์์ ์๊ณ๊ฐ |
| param2 | ์ ๊ฒ์ถ ์๊ณ๊ฐ (๋ฎ์์๋ก ๋ง์ด ๊ฒ์ถ) |
| minRadius | ์ต์ ๋ฐ์ง๋ฆ (0 = ๋ฌด์ ํ) |
| maxRadius | ์ต๋ ๋ฐ์ง๋ฆ (0 = ๋ฌด์ ํ) |
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ¶
import cv2
import numpy as np
def hough_circles_example(image_path):
"""ํํ ์ ๊ฒ์ถ"""
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ๋
ธ์ด์ฆ ์ ๊ฑฐ (์ ๊ฒ์ถ์ ์ค์)
blurred = cv2.GaussianBlur(gray, (9, 9), 2)
# ํํ ์ ๋ณํ
circles = cv2.HoughCircles(
blurred,
cv2.HOUGH_GRADIENT,
dp=1, # ์๋ณธ ํด์๋
minDist=50, # ์ ์ค์ฌ ๊ฐ ์ต์ ๊ฑฐ๋ฆฌ
param1=100, # Canny ์์ ์๊ณ๊ฐ
param2=30, # ์ ๊ฒ์ถ ์๊ณ๊ฐ
minRadius=10, # ์ต์ ๋ฐ์ง๋ฆ
maxRadius=100 # ์ต๋ ๋ฐ์ง๋ฆ
)
result = img.copy()
if circles is not None:
circles = np.uint16(np.around(circles))
for circle in circles[0, :]:
cx, cy, r = circle
# ์ ๊ทธ๋ฆฌ๊ธฐ
cv2.circle(result, (cx, cy), r, (0, 255, 0), 2)
# ์ค์ฌ์
cv2.circle(result, (cx, cy), 3, (0, 0, 255), -1)
print(f"์: ์ค์ฌ({cx}, {cy}), ๋ฐ์ง๋ฆ={r}")
print(f"๊ฒ์ถ๋ ์ ์: {len(circles[0])}")
cv2.imshow('Circles', result)
cv2.waitKey(0)
hough_circles_example('coins.jpg')
๋์ ๊ฒ์ถ¶
import cv2
import numpy as np
def detect_coins(image_path):
"""๋์ ๊ฒ์ถ ๋ฐ ๋ถ๋ฅ"""
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (11, 11), 0)
# ํํ ์ ๋ณํ
circles = cv2.HoughCircles(
blurred,
cv2.HOUGH_GRADIENT,
dp=1.2,
minDist=80,
param1=100,
param2=35,
minRadius=30,
maxRadius=80
)
result = img.copy()
coin_count = 0
total_value = 0
if circles is not None:
circles = np.uint16(np.around(circles))
for circle in circles[0, :]:
cx, cy, r = circle
coin_count += 1
# ํฌ๊ธฐ๋ก ๋์ ์ข
๋ฅ ์ถ์ (์์)
if r < 40:
value = 10
color = (255, 0, 0) # ํ๋
elif r < 55:
value = 50
color = (0, 255, 0) # ๋
น์
else:
value = 100
color = (0, 0, 255) # ๋นจ๊ฐ
total_value += value
# ๊ทธ๋ฆฌ๊ธฐ
cv2.circle(result, (cx, cy), r, color, 2)
cv2.circle(result, (cx, cy), 3, (0, 0, 0), -1)
cv2.putText(result, f'{value}', (cx - 15, cy + 5),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
print(f"๋์ ๊ฐ์: {coin_count}")
print(f"์ด์ก: {total_value}์")
cv2.imshow('Coins', result)
cv2.waitKey(0)
return coin_count, total_value
detect_coins('coins.jpg')
HOUGH_GRADIENT_ALT (OpenCV 4.3+)¶
import cv2
import numpy as np
def hough_circles_alt(image_path):
"""HOUGH_GRADIENT_ALT ์ฌ์ฉ (๋ ์ ํ)"""
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (9, 9), 2)
# HOUGH_GRADIENT_ALT: ๋ ์ ํํ์ง๋ง ๋๋ฆผ
circles = cv2.HoughCircles(
blurred,
cv2.HOUGH_GRADIENT_ALT, # ๋์ฒด ์๊ณ ๋ฆฌ์ฆ
dp=1.5,
minDist=50,
param1=300, # ์ฃ์ง ๊ทธ๋๋์ธํธ ์๊ณ๊ฐ
param2=0.9, # ์ํ๋ ์๊ณ๊ฐ (0-1, ๋์์๋ก ์๊ฒฉ)
minRadius=20,
maxRadius=100
)
result = img.copy()
if circles is not None:
circles = np.uint16(np.around(circles))
for cx, cy, r in circles[0, :]:
cv2.circle(result, (cx, cy), r, (0, 255, 0), 2)
cv2.circle(result, (cx, cy), 3, (0, 0, 255), -1)
cv2.imshow('HOUGH_GRADIENT_ALT', result)
cv2.waitKey(0)
hough_circles_alt('circles.jpg')
5. ํ๋ผ๋ฏธํฐ ํ๋ ์ ๋ต¶
์ง์ ๊ฒ์ถ ํ๋ผ๋ฏธํฐ¶
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ HoughLines ํ๋ผ๋ฏธํฐ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ rho (ฯ ํด์๋) โ
โ - ์์์๋ก: ๋ ์ ๋ฐ, ๋ ๋ง์ ๋ฉ๋ชจ๋ฆฌ, ๋ ๋๋ฆผ โ
โ - ๊ถ์ฅ: 1 (1ํฝ์
) โ
โ โ
โ theta (ฮธ ํด์๋) โ
โ - ์์์๋ก: ๋ ์ ๋ฐํ ๊ฐ๋ โ
โ - ๊ถ์ฅ: np.pi/180 (1๋) โ
โ โ
โ threshold (์ต์ ํฌํ ์) โ
โ - ๋์์๋ก: ๋ ๊ฐํ(๊ธด) ์ง์ ๋ง ๊ฒ์ถ โ
โ - ๋ฎ์์๋ก: ์ฝํ(์งง์) ์ง์ ๋ ๊ฒ์ถ, ๋
ธ์ด์ฆ ์ฆ๊ฐ โ
โ - ํ๋ ๋ฐฉ๋ฒ: ์ด๋ฏธ์ง ํฌ๊ธฐ์ ์์ ์ง์ ๊ธธ์ด์ ๋ฐ๋ผ ์กฐ์ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ HoughLinesP ํ๋ผ๋ฏธํฐ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ minLineLength (์ต์ ์ ๋ถ ๊ธธ์ด) โ
โ - ๋์์๋ก: ๊ธด ์ ๋ถ๋ง ๊ฒ์ถ โ
โ - ๋
ธ์ด์ฆ ๊ฐ์์ ํจ๊ณผ์ โ
โ โ
โ maxLineGap (์ต๋ ๊ฐ๊ฒฉ) โ
โ - ๋์์๋ก: ๋์ด์ง ์ ๋ถ๋ ํ๋๋ก ์ฐ๊ฒฐ โ
โ - ์ ์ ๊ฒ์ถ ์ ์ ์ฉ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
์ ๊ฒ์ถ ํ๋ผ๋ฏธํฐ¶
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ HoughCircles ํ๋ผ๋ฏธํฐ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ dp (ํด์๋ ๋น์จ) โ
โ - 1: ์๋ณธ ํด์๋ โ ์ ํํ์ง๋ง ๋๋ฆผ โ
โ - 2: 1/2 ํด์๋ โ ๋น ๋ฅด์ง๋ง ๋ ์ ํ โ
โ - ๊ถ์ฅ: 1 ~ 1.5 โ
โ โ
โ minDist (์ต์ ์ค์ฌ ๊ฑฐ๋ฆฌ) โ
โ - ๋๋ฌด ์์ผ๋ฉด: ๊ฐ์ ์์ ์ฌ๋ฌ ๋ฒ ๊ฒ์ถ โ
โ - ๋๋ฌด ํฌ๋ฉด: ๊ฐ๊น์ด ์ ๋์นจ โ
โ - ๊ถ์ฅ: ์์ ์ ๋ฐ์ง๋ฆ * 2 ์ด์ โ
โ โ
โ param1 (Canny ์์ ์๊ณ๊ฐ) โ
โ - ๋์์๋ก: ๊ฐํ ์ฃ์ง๋ง ์ฌ์ฉ โ
โ - ๊ถ์ฅ: 100 ~ 200 โ
โ โ
โ param2 (๋์ ์๊ณ๊ฐ) โ
โ - ๋์์๋ก: ํ์คํ ์๋ง ๊ฒ์ถ โ
โ - ๋ฎ์์๋ก: ๋ถ์์ ํ ์๋ ๊ฒ์ถ โ
โ - ๊ถ์ฅ: 20 ~ 50 โ
โ โ
โ minRadius, maxRadius โ
โ - ์์ ์ ํฌ๊ธฐ ๋ฒ์ ์ง์ โ
โ - ์๋ชป ์ค์ ํ๋ฉด ๊ฒ์ถ ์คํจ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ํธ๋๋ฐ๋ก ํ๋ผ๋ฏธํฐ ํ๋¶
import cv2
import numpy as np
def tune_hough_circles(image_path):
"""ํธ๋๋ฐ๋ก HoughCircles ํ๋ผ๋ฏธํฐ ํ๋"""
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (9, 9), 2)
cv2.namedWindow('Circles')
def nothing(x):
pass
cv2.createTrackbar('minDist', 'Circles', 50, 200, nothing)
cv2.createTrackbar('param1', 'Circles', 100, 300, nothing)
cv2.createTrackbar('param2', 'Circles', 30, 100, nothing)
cv2.createTrackbar('minRadius', 'Circles', 10, 100, nothing)
cv2.createTrackbar('maxRadius', 'Circles', 100, 200, nothing)
while True:
minDist = cv2.getTrackbarPos('minDist', 'Circles')
param1 = cv2.getTrackbarPos('param1', 'Circles')
param2 = cv2.getTrackbarPos('param2', 'Circles')
minRadius = cv2.getTrackbarPos('minRadius', 'Circles')
maxRadius = cv2.getTrackbarPos('maxRadius', 'Circles')
# ์ ํจ์ฑ ๊ฒ์ฌ
if minDist < 1:
minDist = 1
if param2 < 1:
param2 = 1
circles = cv2.HoughCircles(
blurred,
cv2.HOUGH_GRADIENT,
dp=1,
minDist=minDist,
param1=param1,
param2=param2,
minRadius=minRadius,
maxRadius=maxRadius
)
result = img.copy()
if circles is not None:
circles = np.uint16(np.around(circles))
for cx, cy, r in circles[0, :]:
cv2.circle(result, (cx, cy), r, (0, 255, 0), 2)
cv2.circle(result, (cx, cy), 3, (0, 0, 255), -1)
# ๊ฒ์ถ๋ ์ ์ ํ์
cv2.putText(result, f'Circles: {len(circles[0])}', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
cv2.imshow('Circles', result)
if cv2.waitKey(1) & 0xFF == 27:
break
cv2.destroyAllWindows()
tune_hough_circles('coins.jpg')
6. ์ฐจ์ ๊ฒ์ถ ๊ธฐ์ด¶
์ฐจ์ ๊ฒ์ถ ํ์ดํ๋ผ์ธ¶
1. ๊ด์ฌ ์์ญ(ROI) ์ค์
โ
โผ
2. ๊ทธ๋ ์ด์ค์ผ์ผ ๋ณํ
โ
โผ
3. ๊ฐ์ฐ์์ ๋ธ๋ฌ
โ
โผ
4. Canny ์ฃ์ง ๊ฒ์ถ
โ
โผ
5. ๊ด์ฌ ์์ญ ๋ง์คํน
โ
โผ
6. ํํ ์ง์ ๋ณํ
โ
โผ
7. ์ ๋ถ ํํฐ๋ง ๋ฐ ํ๊ท ํ
โ
โผ
8. ๊ฒฐ๊ณผ ํฉ์ฑ
๊ธฐ๋ณธ ์ฐจ์ ๊ฒ์ถ¶
import cv2
import numpy as np
def detect_lane_lines(image):
"""๊ธฐ๋ณธ ์ฐจ์ ๊ฒ์ถ"""
height, width = image.shape[:2]
# ๊ทธ๋ ์ด์ค์ผ์ผ
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# ๊ฐ์ฐ์์ ๋ธ๋ฌ
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# Canny ์ฃ์ง
edges = cv2.Canny(blurred, 50, 150)
# ๊ด์ฌ ์์ญ (์ฌ๋ค๋ฆฌ๊ผด)
mask = np.zeros_like(edges)
vertices = np.array([[
(0, height),
(width * 0.45, height * 0.6),
(width * 0.55, height * 0.6),
(width, height)
]], dtype=np.int32)
cv2.fillPoly(mask, vertices, 255)
masked_edges = cv2.bitwise_and(edges, mask)
# ํํ ์ง์ ๋ณํ
lines = cv2.HoughLinesP(
masked_edges,
rho=1,
theta=np.pi/180,
threshold=50,
minLineLength=50,
maxLineGap=150
)
# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง
line_image = np.zeros_like(image)
if lines is not None:
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(line_image, (x1, y1), (x2, y2), (0, 255, 0), 3)
# ์๋ณธ๊ณผ ํฉ์ฑ
result = cv2.addWeighted(image, 0.8, line_image, 1, 0)
return result
# ์ฌ์ฉ ์
img = cv2.imread('road.jpg')
result = detect_lane_lines(img)
cv2.imshow('Lane Detection', result)
cv2.waitKey(0)
์ข/์ฐ ์ฐจ์ ๋ถ๋ฆฌ¶
import cv2
import numpy as np
def separate_lanes(image):
"""์ข/์ฐ ์ฐจ์ ๋ถ๋ฆฌ ๊ฒ์ถ"""
height, width = image.shape[:2]
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edges = cv2.Canny(blurred, 50, 150)
# ROI ๋ง์คํฌ
mask = np.zeros_like(edges)
vertices = np.array([[
(50, height),
(width * 0.45, height * 0.6),
(width * 0.55, height * 0.6),
(width - 50, height)
]], dtype=np.int32)
cv2.fillPoly(mask, vertices, 255)
masked = cv2.bitwise_and(edges, mask)
lines = cv2.HoughLinesP(masked, 1, np.pi/180, 30,
minLineLength=30, maxLineGap=100)
left_lines = []
right_lines = []
if lines is not None:
for line in lines:
x1, y1, x2, y2 = line[0]
# ๊ธฐ์ธ๊ธฐ ๊ณ์ฐ
if x2 - x1 == 0:
continue
slope = (y2 - y1) / (x2 - x1)
# ๊ธฐ์ธ๊ธฐ๋ก ์ข/์ฐ ๋ถ๋ฅ
# ์ด๋ฏธ์ง ์ขํ๊ณ: y์ถ์ด ์๋๋ก ์ฆ๊ฐ
# ์ผ์ชฝ ์ฐจ์ : ์์ ๊ธฐ์ธ๊ธฐ (/)
# ์ค๋ฅธ์ชฝ ์ฐจ์ : ์์ ๊ธฐ์ธ๊ธฐ (\)
if slope < -0.5:
left_lines.append(line[0])
elif slope > 0.5:
right_lines.append(line[0])
result = image.copy()
# ์ข/์ฐ ์ฐจ์ ๊ทธ๋ฆฌ๊ธฐ
for x1, y1, x2, y2 in left_lines:
cv2.line(result, (x1, y1), (x2, y2), (255, 0, 0), 3) # ํ๋
for x1, y1, x2, y2 in right_lines:
cv2.line(result, (x1, y1), (x2, y2), (0, 0, 255), 3) # ๋นจ๊ฐ
return result, left_lines, right_lines
# ์ฌ์ฉ ์
img = cv2.imread('road.jpg')
result, left, right = separate_lanes(img)
print(f"์ผ์ชฝ ์ฐจ์ : {len(left)}๊ฐ")
print(f"์ค๋ฅธ์ชฝ ์ฐจ์ : {len(right)}๊ฐ")
cv2.imshow('Lanes', result)
cv2.waitKey(0)
์ฐจ์ ํ๊ท ํ¶
import cv2
import numpy as np
def average_lane_lines(lines, height):
"""์ ๋ถ๋ค์ ํ๊ท ํ์ฌ ํ๋์ ์ง์ ์ผ๋ก"""
if len(lines) == 0:
return None
# ๋ชจ๋ ์ ์์ง
x_coords = []
y_coords = []
for x1, y1, x2, y2 in lines:
x_coords.extend([x1, x2])
y_coords.extend([y1, y2])
# 1์ฐจ ๋คํญ์ ํผํ
(์ง์ )
poly = np.polyfit(y_coords, x_coords, 1)
# ์ง์ ์ ์์์ ๊ณผ ๋์ ๊ณ์ฐ
y1 = height
y2 = int(height * 0.6)
x1 = int(np.polyval(poly, y1))
x2 = int(np.polyval(poly, y2))
return (x1, y1, x2, y2)
def detect_lanes_averaged(image):
"""ํ๊ท ํ๋ ์ฐจ์ ๊ฒ์ถ"""
height, width = image.shape[:2]
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edges = cv2.Canny(blurred, 50, 150)
# ROI
mask = np.zeros_like(edges)
vertices = np.array([[
(50, height),
(width * 0.45, height * 0.6),
(width * 0.55, height * 0.6),
(width - 50, height)
]], dtype=np.int32)
cv2.fillPoly(mask, vertices, 255)
masked = cv2.bitwise_and(edges, mask)
lines = cv2.HoughLinesP(masked, 1, np.pi/180, 30,
minLineLength=30, maxLineGap=100)
left_lines = []
right_lines = []
if lines is not None:
for line in lines:
x1, y1, x2, y2 = line[0]
if x2 - x1 == 0:
continue
slope = (y2 - y1) / (x2 - x1)
if slope < -0.5:
left_lines.append(line[0])
elif slope > 0.5:
right_lines.append(line[0])
result = image.copy()
# ํ๊ท ํ๋ ์ฐจ์ ๊ทธ๋ฆฌ๊ธฐ
left_avg = average_lane_lines(left_lines, height)
right_avg = average_lane_lines(right_lines, height)
if left_avg is not None:
cv2.line(result, (left_avg[0], left_avg[1]),
(left_avg[2], left_avg[3]), (255, 0, 0), 5)
if right_avg is not None:
cv2.line(result, (right_avg[0], right_avg[1]),
(right_avg[2], right_avg[3]), (0, 0, 255), 5)
# ์ฐจ์ ์์ญ ์ฑ์ฐ๊ธฐ
if left_avg is not None and right_avg is not None:
pts = np.array([
[left_avg[0], left_avg[1]],
[left_avg[2], left_avg[3]],
[right_avg[2], right_avg[3]],
[right_avg[0], right_avg[1]]
], np.int32)
overlay = result.copy()
cv2.fillPoly(overlay, [pts], (0, 255, 0))
result = cv2.addWeighted(overlay, 0.3, result, 0.7, 0)
return result
# ์ฌ์ฉ ์
img = cv2.imread('road.jpg')
result = detect_lanes_averaged(img)
cv2.imshow('Averaged Lanes', result)
cv2.waitKey(0)
7. ์ฐ์ต ๋ฌธ์ ¶
๋ฌธ์ 1: ์ฒด์คํ ๊ฒ์ถ¶
์ฒด์คํ ์ด๋ฏธ์ง์์ ๋ชจ๋ ์ง์ ์ ๊ฒ์ถํ๊ณ ๊ต์ฐจ์ ์ ์ฐพ์ผ์ธ์.
์ ๋ต ์ฝ๋
import cv2
import numpy as np
def detect_chessboard_lines(image_path):
"""์ฒด์คํ ์ง์ ๊ณผ ๊ต์ฐจ์ ๊ฒ์ถ"""
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)
lines = cv2.HoughLines(edges, 1, np.pi/180, 100)
result = img.copy()
horizontal = []
vertical = []
if lines is not None:
for line in lines:
rho, theta = line[0]
angle = np.degrees(theta)
a = np.cos(theta)
b = np.sin(theta)
# ์ํ์ /์์ง์ ๋ถ๋ฅ
if 80 < angle < 100: # ์์ง
vertical.append((rho, theta))
elif angle < 10 or angle > 170: # ์ํ
horizontal.append((rho, theta))
# ๊ต์ฐจ์ ๊ณ์ฐ
intersections = []
for h_rho, h_theta in horizontal:
for v_rho, v_theta in vertical:
# ๋ ์ง์ ์ ๊ต์ฐจ์
A = np.array([
[np.cos(h_theta), np.sin(h_theta)],
[np.cos(v_theta), np.sin(v_theta)]
])
b = np.array([h_rho, v_rho])
try:
x, y = np.linalg.solve(A, b)
if 0 <= x < img.shape[1] and 0 <= y < img.shape[0]:
intersections.append((int(x), int(y)))
except:
pass
# ๊ทธ๋ฆฌ๊ธฐ
for x, y in intersections:
cv2.circle(result, (x, y), 5, (0, 0, 255), -1)
print(f"๊ต์ฐจ์ ์: {len(intersections)}")
cv2.imshow('Chessboard', result)
cv2.waitKey(0)
detect_chessboard_lines('chessboard.jpg')
๋ฌธ์ 2: ์์ด๋ฆฌ์ค ๊ฒ์ถ¶
๋ ์ด๋ฏธ์ง์์ ํ์ฑ ์์ ๊ฒ์ถํ์ธ์.
์ ๋ต ์ฝ๋
import cv2
import numpy as np
def detect_iris(image_path):
"""๋์์ ํ์ฑ ๊ฒ์ถ"""
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ๋ฐ๊ธฐ ๊ท ์ผํ
gray = cv2.equalizeHist(gray)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)
# ํ์ฑ ๊ฒ์ถ (์ด๋์ด ์)
circles = cv2.HoughCircles(
blurred,
cv2.HOUGH_GRADIENT,
dp=1,
minDist=100,
param1=100,
param2=25,
minRadius=20,
maxRadius=60
)
result = img.copy()
if circles is not None:
circles = np.uint16(np.around(circles))
# ๊ฐ์ฅ ํฐ ์ ์ ํ (ํ์ฑ)
for cx, cy, r in sorted(circles[0], key=lambda x: -x[2])[:1]:
# ํ์ฑ
cv2.circle(result, (cx, cy), r, (0, 255, 0), 2)
cv2.circle(result, (cx, cy), 2, (0, 0, 255), 3)
cv2.imshow('Iris', result)
cv2.waitKey(0)
detect_iris('eye.jpg')
๋ฌธ์ 3: ์ํ ๋๋ก ํ์งํ ๊ฒ์ถ¶
๋นจ๊ฐ์ ์ํ ๊ตํต ํ์งํ์ ๊ฒ์ถํ์ธ์.
์ ๋ต ์ฝ๋
import cv2
import numpy as np
def detect_red_signs(image_path):
"""๋นจ๊ฐ ์ํ ํ์งํ ๊ฒ์ถ"""
img = cv2.imread(image_path)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# ๋นจ๊ฐ์ ๋ง์คํฌ (HSV์์ ๋นจ๊ฐ์ 0๋์ 180๋ ๋ถ๊ทผ)
lower_red1 = np.array([0, 100, 100])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([160, 100, 100])
upper_red2 = np.array([180, 255, 255])
mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
red_mask = cv2.bitwise_or(mask1, mask2)
# ๋ชจํด๋ก์ง ์ฐ์ฐ
kernel = np.ones((5, 5), np.uint8)
red_mask = cv2.morphologyEx(red_mask, cv2.MORPH_CLOSE, kernel)
red_mask = cv2.morphologyEx(red_mask, cv2.MORPH_OPEN, kernel)
# ์ ๊ฒ์ถ
circles = cv2.HoughCircles(
red_mask,
cv2.HOUGH_GRADIENT,
dp=1.2,
minDist=50,
param1=50,
param2=30,
minRadius=20,
maxRadius=100
)
result = img.copy()
if circles is not None:
circles = np.uint16(np.around(circles))
for cx, cy, r in circles[0]:
cv2.circle(result, (cx, cy), r, (0, 255, 0), 3)
cv2.circle(result, (cx, cy), 3, (0, 0, 255), -1)
cv2.imshow('Red Signs', result)
cv2.imshow('Mask', red_mask)
cv2.waitKey(0)
detect_red_signs('traffic_sign.jpg')
์ถ์ฒ ๋ฌธ์ ¶
| ๋์ด๋ | ์ฃผ์ | ์ค๋ช |
|---|---|---|
| โญ | ์ง์ ๊ฒ์ถ | ๊ฑด๋ฌผ ์ฌ์ง์์ ์ํ/์์ง์ ๊ฒ์ถ |
| โญโญ | ๋์ ์ธ๊ธฐ | ๋์ ์ฌ์ง์์ ๊ฐ์์ ๊ธ์ก ๊ณ์ฐ |
| โญโญ | ๋ฌธ์ ๊ฒ์ถ | ๋ฌธ์ ๊ฒฝ๊ณ์ 4๊ฐ ๊ฒ์ถ |
| โญโญโญ | ์ฐจ์ ๊ฒ์ถ | ๋๋ก ์์์์ ์ค์๊ฐ ์ฐจ์ ๊ฒ์ถ |
| โญโญโญ | ๊ณ๊ธฐํ | ์๋ ๋ก๊ทธ ๊ฒ์ด์ง ๋๊ธ ์ฝ๊ธฐ |
๋ค์ ๋จ๊ณ¶
- 12_Histogram_Analysis.md - calcHist, equalizeHist, CLAHE