😎 κ³΅λΆ€ν•˜λŠ” μ§•μ§•μ•ŒνŒŒμΉ΄λŠ” μ²˜μŒμ΄μ§€?

[v0.16]μ˜μƒμ²˜λ¦¬_이미지 λ’€ν‹€κΈ° λ³Έλ¬Έ

πŸ‘©‍πŸ’» IoT (Embedded)/Image Processing

[v0.16]μ˜μƒμ²˜λ¦¬_이미지 λ’€ν‹€κΈ°

μ§•μ§•μ•ŒνŒŒμΉ΄ 2022. 1. 7. 23:56
728x90
λ°˜μ‘ν˜•

220107 μž‘μ„±

<λ³Έ λΈ”λ‘œκ·ΈλŠ” 귀퉁이 μ„œμž¬λ‹˜μ˜ λΈ”λ‘œκ·Έλ₯Ό μ°Έκ³ ν•΄μ„œ κ³΅λΆ€ν•˜λ©° μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€>

https://bkshin.tistory.com/entry/OpenCV-14-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%92%A4%ED%8B%80%EA%B8%B0%EC%96%B4%ED%95%80-%EB%B3%80%ED%99%98-%EC%9B%90%EA%B7%BC-%EB%B3%80%ED%99%98?category=1148027 

 

OpenCV - 14. 이미지 λ’€ν‹€κΈ°(μ–΄ν•€ λ³€ν™˜, 원근 λ³€ν™˜)

이번 ν¬μŠ€νŒ…μ—μ„œλŠ” 이미지λ₯Ό λ’€νŠΈλŠ” 방법에 λŒ€ν•΄ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€. 이번 ν¬μŠ€νŒ… μ—­μ‹œ '파이썬으둜 λ§Œλ“œλŠ” OpenCV ν”„λ‘œμ νŠΈ(μ΄μ„Έμš° μ €)'λ₯Ό μ •λ¦¬ν•œ κ²ƒμž„μ„ λ°νž™λ‹ˆλ‹€. μ½”λ“œ: github.com/BaekKyunShin/OpenCV_Pr

bkshin.tistory.com

 

 

 

 

 

1. μ–΄ν•€ λ³€ν™˜ (Affine Transform)

: λ’€ν‹€κΈ°

 

martix = cv2.getAffineTransform(pts1, pts2)

: 이미지λ₯Ό 2μ°¨μ›μœΌλ‘œ λ’€νŠΈλŠ” λ³€ν™˜

: 3개의 μ’Œν‘œμΈ pts1이 pts2둜 μœ„μΉ˜κ°€ λ³€ν•œ 만큼 이미지λ₯Ό λ’€νŠΌλ‹€

- pts1 : λ³€ν™˜ μ „ μ˜μƒμ˜ μ’Œν‘œ 3개, 3 x 2 λ°°μ—΄

- pts2 : λ³€ν™˜ ν›„ μ˜μƒμ˜ μ’Œν‘œ 3개, 3 x 2 λ°°μ—΄

- matrix : λ³€ν™˜ ν–‰λ ¬ λ°˜ν™˜, 2 x 3 ν–‰λ ¬

# μ–΄ν•€ λ³€ν™˜
import cv2
import numpy as np
from matplotlib import pyplot as plt

file_name = 'img/hobbang2.jpeg'
img = cv2.imread(file_name)
rows, cols = img.shape[:2]

# ---β‘  λ³€ν™˜ μ „, ν›„ 각 3개의 μ’Œν‘œ 생성
pts1 = np.float32([[100, 50], [200, 50], [100, 200]])
pts2 = np.float32([[80, 70], [210, 60], [250, 120]])

# ---β‘‘ λ³€ν™˜ μ „ μ’Œν‘œλ₯Ό 이미지에 ν‘œμ‹œ
cv2.circle(img, (100,50), 5, (255,0), -1)
cv2.circle(img, (200,50), 5, (0,255,0), -1)
cv2.circle(img, (100,200), 5, (0,0,255), -1)

#---β‘’ 짝지은 3개의 μ’Œν‘œλ‘œ λ³€ν™˜ ν–‰λ ¬ 계산
mtrx = cv2.getAffineTransform(pts1, pts2)
#---β‘£ μ–΄ν•€ λ³€ν™˜ 적용
dst = cv2.warpAffine(img, mtrx, (int(cols*1.5), rows))

#---β‘€ κ²°κ³Ό 좜λ ₯
cv2.imshow('origin',img)
cv2.imshow('affin', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

μ–΄ν•€ λ³€ν™˜μœΌλ‘œ 이미지 λ’€ν‹€κΈ°

- μ–΄ν•€ λ³€ν™˜ μ „ν›„ 3개의 μ’Œν‘œλ§Œ 지정해주면 λ³€ν™˜ ν–‰λ ¬ κ°€λŠ₯

 

 

 

 

2. 원근 λ³€ν™˜ (Perspective Transform)

: 이미지λ₯Ό 3μ°¨μ›μœΌλ‘œ λ³€ν™˜

: 멀리 μžˆλŠ” 것은 μž‘κ²Œ 보이고, κ°€κΉŒμ΄ μžˆλŠ” 것은 크게 λ³΄μ΄λŠ” 게 원근법

: μ›κ·Όλ²•μ˜ 원리λ₯Ό μ μš©ν•΄ λ³€ν™˜ν•˜λŠ” 방식이 원근 λ³€ν™˜

 

mtrx = cv2.getPerspectiveTransform(pts1, pts2)
- pts1 : λ³€ν™˜ 이전 μ˜μƒμ˜ μ’Œν‘œ 4개, 4 x 2 λ°°μ—΄
- pts2 : λ³€ν™˜ 이후 μ˜μƒμ˜ μ’Œν‘œ 4개, 4 x 2 λ°°μ—΄
- mtrx : λ³€ν™˜ν–‰λ ¬ λ°˜ν™˜, 3 x 3 ν–‰λ ¬

- 원근 λ³€ν™˜μ€ λ³„λ„μ˜ ν•¨μˆ˜ cv2.warpPerspective() ν•¨μˆ˜μ“°κΈ°

# 원근 λ³€ν™˜ 
import cv2
import numpy as np

file_name = 'img/hobbang2.jpeg'
img = cv2.imread(file_name)
rows, cols = img.shape[:2]

#---β‘  원근 λ³€ν™˜ μ „ ν›„ 4개 μ’Œν‘œ
pts1 = np.float32([[0,0], [0,rows], [cols, 0], [cols,rows]])
pts2 = np.float32([[100,50], [10,rows-50], [cols-100, 50], [cols-10,rows-50]])

#---β‘‘ λ³€ν™˜ μ „ μ’Œν‘œλ₯Ό 원본 이미지에 ν‘œμ‹œ
cv2.circle(img, (0,0), 10, (255,0,0), -1)
cv2.circle(img, (0,rows), 10, (0,255,0), -1)
cv2.circle(img, (cols,0), 10, (0,0,255), -1)
cv2.circle(img, (cols,rows), 10, (0,255,255), -1)

#---β‘’ 원근 λ³€ν™˜ ν–‰λ ¬ 계산
mtrx = cv2.getPerspectiveTransform(pts1, pts2)
#---β‘£ 원근 λ³€ν™˜ 적용
dst = cv2.warpPerspective(img, mtrx, (cols, rows))

cv2.imshow("origin", img)
cv2.imshow('perspective', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

원근 λ³€ν™˜μœΌλ‘œ 이미지 λ’€ν‹€κΈ°

- λ„€ 개의 μ’Œν‘œλ‘œ 원근법 처럼 이미지 λ³€κ²½!

 

 

 

+) 원근 λ³€ν™˜μœΌλ‘œ 원근감 이미지λ₯Ό 평면 μ΄λ―Έμ§€λ‘œ λ³€κ²½ν•˜κΈ°

# λ§ˆμš°μŠ€μ™€ 원근 λ³€ν™˜μœΌλ‘œ λ¬Έμ„œ μŠ€μΊ” 효과 λ‚΄κΈ°
import cv2
import numpy as np

win_name = "scanning"
img = cv2.imread("img/Perspective1.jpg")
rows, cols = img.shape[:2]
draw = img.copy()
pts_cnt = 0
pts = np.zeros((4,2), dtype=np.float32)

def onMouse(event, x, y, flags, param):  #마우슀 이벀트 콜백 ν•¨μˆ˜ κ΅¬ν˜„ ---β‘  
    global  pts_cnt                     # 마우슀둜 찍은 μ’Œν‘œμ˜ 갯수 μ €μž₯
    if event == cv2.EVENT_LBUTTONDOWN:  
        cv2.circle(draw, (x,y), 10, (0,255,0), -1) # μ’Œν‘œμ— μ΄ˆλ‘μƒ‰ 동그라미 ν‘œμ‹œ
        cv2.imshow(win_name, draw)

        pts[pts_cnt] = [x,y]            # 마우슀 μ’Œν‘œ μ €μž₯
        pts_cnt+=1
        if pts_cnt == 4:                       # μ’Œν‘œκ°€ 4개 μˆ˜μ§‘λ¨ 
            # μ’Œν‘œ 4개 쀑 μƒν•˜μ’Œμš° μ°ΎκΈ° ---β‘‘ 
            sm = pts.sum(axis=1)                 # 4쌍의 μ’Œν‘œ 각각 x+y 계산
            diff = np.diff(pts, axis = 1)       # 4쌍의 μ’Œν‘œ 각각 x-y 계산

            topLeft = pts[np.argmin(sm)]         # x+yκ°€ κ°€μž₯ 값이 μ’Œμƒλ‹¨ μ’Œν‘œ
            bottomRight = pts[np.argmax(sm)]     # x+yκ°€ κ°€μž₯ 큰 값이 μš°ν•˜λ‹¨ μ’Œν‘œ
            topRight = pts[np.argmin(diff)]     # x-yκ°€ κ°€μž₯ μž‘μ€ 것이 μš°μƒλ‹¨ μ’Œν‘œ
            bottomLeft = pts[np.argmax(diff)]   # x-yκ°€ κ°€μž₯ 큰 값이 μ’Œν•˜λ‹¨ μ’Œν‘œ

            # λ³€ν™˜ μ „ 4개 μ’Œν‘œ 
            pts1 = np.float32([topLeft, topRight, bottomRight , bottomLeft])

            # λ³€ν™˜ ν›„ μ˜μƒμ— μ‚¬μš©ν•  μ„œλ₯˜μ˜ 폭과 높이 계산 ---β‘’ 
            w1 = abs(bottomRight[0] - bottomLeft[0])    # 상단 쒌우 μ’Œν‘œκ°„μ˜ 거리
            w2 = abs(topRight[0] - topLeft[0])          # ν•˜λ‹Ή 쒌우 μ’Œν‘œκ°„μ˜ 거리
            h1 = abs(topRight[1] - bottomRight[1])      # 우츑 μƒν•˜ μ’Œν‘œκ°„μ˜ 거리
            h2 = abs(topLeft[1] - bottomLeft[1])        # 쒌츑 μƒν•˜ μ’Œν‘œκ°„μ˜ 거리
            width = max([w1, w2])                       # 두 쒌우 κ±°λ¦¬κ°„μ˜ μ΅œλŒ€κ°’μ΄ μ„œλ₯˜μ˜ 폭
            height = max([h1, h2])                      # 두 μƒν•˜ κ±°λ¦¬κ°„μ˜ μ΅œλŒ€κ°’μ΄ μ„œλ₯˜μ˜ 높이
            
            # λ³€ν™˜ ν›„ 4개 μ’Œν‘œ
            pts2 = np.float32([[0,0], [width-1,0], 
                                [width-1,height-1], [0,height-1]])

            # λ³€ν™˜ ν–‰λ ¬ 계산 
            mtrx = cv2.getPerspectiveTransform(pts1, pts2)
            # 원근 λ³€ν™˜ 적용
            result = cv2.warpPerspective(img, mtrx, (width, height))
            cv2.imshow('scanned', result)
cv2.imshow(win_name, img)
cv2.setMouseCallback(win_name, onMouse)    # 마우슀 콜백 ν•¨μˆ˜λ₯Ό GUI μœˆλ„μš°μ— 등둝 ---β‘£
cv2.waitKey(0)
cv2.destroyAllWindows()

원근 λ³€ν™˜μœΌλ‘œ 평면 이미지 λ§Œλ“€κΈ°

- 점 찍으면 λ˜λŠ” κ±° μ•„λ‹Œκ°€μœ ...

- IndexError: index 4 is out of bounds for axis 0 with size 4 μ—λŸ¬λœ¬λ‹Ή..

 

 

 

 

3. μ‚Όκ°ν˜• μ–΄ν•€ λ³€ν™˜

1) OpenCVκ°€ μ œκ³΅ν•˜λŠ” κΈ°ν•˜ν•™μ  λ³€ν™˜μ€ 기본적으둜 μ‚¬κ°ν˜•μ΄ κΈ°μ€€

2) μ‚Όκ°ν˜• λͺ¨μ–‘μ˜ λ³€ν™˜

  • 1. μ–΄ν•€ λ³€ν™˜ μ „ μ‚Όκ°ν˜• μ’Œν‘œ 3개λ₯Ό μ •ν•œλ‹€.
  • 2. μ–΄ν•€ λ³€ν™˜ ν›„ μ‚Όκ°ν˜• μ’Œν‘œ 3개λ₯Ό μ •ν•œλ‹€.
  • 3. λ³€ν™˜ μ „ μ‚Όκ°ν˜• μ’Œν‘œλ₯Ό κ°μ‹ΈλŠ” μ™Έμ ‘ μ‚¬κ°ν˜• μ’Œν‘œλ₯Ό κ΅¬ν•œλ‹€.
  • 4. λ³€ν™˜ ν›„ μ‚Όκ°ν˜• μ’Œν‘œλ₯Ό κ°μ‹ΈλŠ” μ™Έμ ‘ μ‚¬κ°ν˜• μ’Œν‘œλ₯Ό κ΅¬ν•œλ‹€.
  • 5. κ³Όμ • 3, 4의 μ‚¬κ°ν˜• μ˜μ—­μ„ 관심 μ˜μ—­(ROI, regison of interest)으둜 μ§€μ •ν•œλ‹€.
  • 6. κ³Όμ • 5의 관심 μ˜μ—­μ„ κΈ°μ€€μœΌλ‘œ λ³€ν™˜ μ „, ν›„μ˜ μ‚Όκ°ν˜• μ’Œν‘œλ₯Ό λ‹€μ‹œ κ³„μ‚°ν•œλ‹€.
  • 7. κ³Όμ • 6의 λ³€ν™˜ μ „ μ‚Όκ°ν˜• μ’Œν‘œλ₯Ό λ³€ν™˜ ν›„ μ‚Όκ°ν˜• μ’Œν‘œλ‘œ μ–΄ν•€ λ³€ν™˜ν•΄μ£ΌλŠ” λ³€ν™˜ 행렬을 κ΅¬ν•œλ‹€.
  • 8. κ³Όμ • 7μ—μ„œ κ΅¬ν•œ λ³€ν™˜ν–‰λ ¬μ„ μ μš©ν•΄ μ–΄ν•€ λ³€ν™˜μ„ ν•œλ‹€.
  • 9. κ³Όμ • 8μ—μ„œ λ³€ν™˜λœ 관심 μ˜μ—­μ—μ„œ κ³Όμ • 2의 μ‚Όκ°ν˜• μ’Œν‘œλ§Œ λ§ˆμŠ€ν‚Ήν•œλ‹€.
  • 10. κ³Όμ • 9μ—μ„œ κ΅¬ν•œ 마슀크λ₯Ό μ΄μš©ν•΄μ„œ μ–΄ν•€ λ³€ν™˜ν•œ 이미지와 원본 이미지λ₯Ό ν•©μ„±ν•œλ‹€.

x, y, w, h = cv2.boudingRect(pts)

: μ‚Όκ°ν˜• μ’Œν‘œλ₯Ό κ°μ‹ΈλŠ” μ™Έμ ‘ μ‚¬κ°ν˜• μ’Œν‘œλ₯Ό κ΅¬ν•˜κΈ°
- pts : λ‹€κ°ν˜• μ’Œν‘œ
- x, y, w, h = μ™Έμ ‘ μ‚¬κ°ν˜•μ˜ μ’Œν‘œμ™€ 폭과 높이

 

cv2.fillConvexPoly(img, pts, color, lineTypes)

: κ³Όμ • 9의 마슀크λ₯Ό κ΅¬ν•˜κΈ°
- img : μž…λ ₯ 이미지
- pts : λ‹€κ°ν˜• μ’Œν‘œ
- color : λ‹€κ°ν˜•μ„ μ±„μšΈ 색상
- lineType(optional) : μ„  그리기 μ•Œκ³ λ¦¬μ¦˜ 선택 ν”Œλž˜κ·Έ

# μ‚Όκ°ν˜• μ–΄ν•€ λ³€ν™˜
import cv2
import numpy as np

img = cv2.imread("img/taekwonv1.jpg")
img2 = img.copy()
draw = img.copy()

# λ³€ν™˜ μ „,ν›„ μ‚Όκ°ν˜• μ’Œν‘œ ---β‘ 
pts1 = np.float32([[188,14], [85,202], [294,216]])
pts2 = np.float32([[128,40], [85,307], [306,167]])

# 각 μ‚Όκ°ν˜•μ„ μ™„μ „νžˆ κ°μ‹ΈλŠ” μ‚¬κ°ν˜• μ’Œν‘œ κ΅¬ν•˜κΈ° ---β‘‘
x1,y1,w1,h1 = cv2.boundingRect(pts1)
x2,y2,w2,h2 = cv2.boundingRect(pts2)

# μ‚¬κ°ν˜•μ„ μ΄μš©ν•œ κ΄€μ‹¬μ˜μ—­ μ„€μ • ---β‘’
roi1 = img[y1:y1+h1, x1:x1+w1]
roi2 = img2[y2:y2+h2, x2:x2+w2]

# κ΄€μ‹¬μ˜μ—­μ„ κΈ°μ€€μœΌλ‘œ μ’Œν‘œ 계산 ---β‘£
offset1 = np.zeros((3,2), dtype=np.float32)
offset2 = np.zeros((3,2), dtype=np.float32)
for i in range(3):
    offset1[i][0], offset1[i][1] = pts1[i][0]-x1, pts1[i][1]-y1
    offset2[i][0], offset2[i][1] = pts2[i][0]-x2, pts2[i][1]-y2

# 관심 μ˜μ—­μ„ 주어진 μ‚Όκ°ν˜• μ’Œν‘œλ‘œ μ–΄ν•€ λ³€ν™˜ ---β‘€
mtrx = cv2.getAffineTransform(offset1, offset2)
warped = cv2.warpAffine( roi1, mtrx, (w2, h2), None, \
                        cv2.INTER_LINEAR, cv2.BORDER_REFLECT_101)

# μ–΄ν•€ λ³€ν™˜ ν›„ μ‚Όκ°ν˜•λ§Œ 골라 λ‚΄κΈ° μœ„ν•œ 마슀크 생성 ---β‘₯
mask = np.zeros((h2, w2), dtype = np.uint8)
cv2.fillConvexPoly(mask, np.int32(offset2), (255))

# μ‚Όκ°ν˜• μ˜μ—­λ§Œ λ§ˆμŠ€ν‚Ήν•΄μ„œ ν•©μ„± ---⑦
warped_masked = cv2.bitwise_and(warped, warped, mask=mask)
roi2_masked = cv2.bitwise_and(roi2, roi2, mask=cv2.bitwise_not(mask))
roi2_masked = roi2_masked + warped_masked
img2[y2:y2+h2, x2:x2+w2] = roi2_masked

# 관심 μ˜μ—­κ³Ό μ‚Όκ°ν˜•μ— μ„  κ·Έλ €μ„œ 좜λ ₯ ---⑧
cv2.rectangle(draw, (x1, y1), (x1+w1, y1+h1), (0,255,0), 1)
cv2.polylines(draw, [pts1.astype(np.int32)], True, (255,0,0), 1)
cv2.rectangle(img2, (x2, y2), (x2+w2, y2+h2), (0,255,0), 1)
cv2.imshow('origin', draw)
cv2.imshow('warped triangle', img2)
cv2.waitKey(0)
cv2.destroyAllWindows()

μ‚¬κ°ν˜•, μ‚Όκ°ν˜•μœΌλ‘œ μ–΄ν•€ λ³€ν™˜

- μ™• μ‹ κΈ°ν•˜κ΅°

 

 

 

 

μžΌλ”°

 

728x90
λ°˜μ‘ν˜•
Comments