ㅇ 프로젝트/(Toy)_project

openCV를 활용하여 동전을 검출해보자.

BrainKimDu 2022. 12. 26. 21:13

이 글은 

PinkLab의 PinkWink 강사님의 강의자료를 참고하여 작성되었습니다.

 


프로젝트에서 컨베이어 벨트를 통과하는 도형들을 검출해야한다.

 

YOLO를 사용하지 않고 openCV만을 사용하여 검출해주었으면 좋겠다고 하여 

 

공부 + 정리를 진행하려고한다.

 

 


이미지를 볼 때 갑자기 픽셀의 밝기값이 급격하게 변하는 부분을 추출하면

에지를 추출할 수 있다.

 

일단 사진 하나를 공유하고자한다.

 

쥬피터 상에서는 문제가 있으니

전체코드는 비쥬얼 스튜디오 코드로 실행해야한다.

 

import sys
import numpy as np
import cv2

img = cv2.imread('./coin.jpg', cv2.IMREAD_GRAYSCALE)
img.shape

사진이 엄청크다.

https://calculator-online.net/ko/ratio-calculator/

(36 : 27) 비율로 사진을 리사이징 하자.

720:540 으로

img = cv2.resize(img, dsize=(720, 540))
img.shape

 

그러면 사진을 한 번 보자.

 

import sys
import numpy as np
import cv2

img = cv2.imread('./coin.jpg', cv2.IMREAD_GRAYSCALE)

if img is None:
    print("Image load failed")
    sys.exit()

img = cv2.resize(img, dsize=(720, 540))

cv2.imshow('img', img)
cv2.waitKey()

cv2.destroyAllWindows()

쥬피터로 실행하면 안된다.

크기가 좋게 조절된 모습..

 

 

에지를 추출하는 방법은 쇼벨, 케니 등의 방법이있다.

그 중에서도 원을 검출하는 코드는 케니엣지와 허프변환을 사용한다.

 

케니 엣지 검출 방식은 다음과 같다.

import sys
import numpy as np
import cv2

img = cv2.imread('./coin.jpg', cv2.IMREAD_GRAYSCALE)

if img is None:
    print("Image load failed")
    sys.exit()

img = cv2.resize(img, dsize=(720, 540))
dst = cv2.Canny(img, 50, 150)

cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey()

cv2.destroyAllWindows()

 

다음처럼 변환이 가능하다.

 

케니 엣지의 경우 

이미지에 가우시안 필터링을 적용 -> 그레디언트계산 -> 비최대억제 -> 이중 임계값을 이용한 히스테리시스 에지 트레킹의 과정을 거친다.

가우시안 필터링 : 노이즈를 제거한다.

그레디언트 계산 :  그레디언트 크기 : 픽셀값의 변화량 / 그레디언트 방향 : 픽셀 값이 급격하게 증가하는 방향
(쇼벨 마스크로 계산)

비최대억제 : 하나의 에지가 여러 개의 픽셀로 표현되는 현상을 제거하기 위하여 국지적 최대인 픽셀만을 에지 픽셀로 설정

히스테리시스 에지 트레킹 : 두 개의 임계값 중에서 최대 임계값을 넘는 에지값(강한에지)를 최종에지로 선정하거나 강한에지와 연결되어 있는 픽셀만 최종 에지로 선정

 

일단 뭐 이론적인 내용은 나중에 더 찾아보는 거로 하고

케니 에지 검출 함수를 살펴보자.

cv2.Canny(image, threshold1, threshold2, edges=None, apertureSize=None, L2gradient=None)

apertureSize : 소벨 연산을 위한 커널의 크기

L2gradient Ture이면 L2 norm 사용 False이면 L1 norm 사용 (연산속도와 관련이 있다고 한다.)

 

 

 

 

케니에지 다음에 등장하는 건 허프변환이다.

직선을 검출할 수도 있고 원을 검출할 수도 있다.

직선의 방정식을 파라미터 공간으로 변환하여 직선을 찾는 알고리즘이다.

 

 

그러면 위 사진에서 원을 한 번 검출해보자.

 

 

원을 검출하기 위해서는 threshold값을 구해야한다.

가장 많이 사용하는 방법은 트랙바로 구현을 하는 방법이 있다고 한다.

 

일단 원을 검출하는지 확인을 해봐야하니까

import sys
import numpy as np
import cv2

img = cv2.imread('./coin.jpg', cv2.IMREAD_GRAYSCALE)

if img is None:
    print("Image load failed")
    sys.exit()

img = cv2.resize(img, dsize=(720, 540))

# 노이즈 제거
blr = cv2.GaussianBlur(img, (0, 0), 1.0)

coin = cv2.HoughCircles(blr, cv2.HOUGH_GRADIENT, 1, 50, 150, 40, minRadius=20, maxRadius=80)

if coin is not None:
    print("detact")

cv2.imshow('img', img)
cv2.waitKey()
cv2.destroyAllWindows()

다음을 실행했을 때 터미널에 detact가 나오는지확인해보자.

 

원을 검출하는 것을 확인했다.

 

그러면 한 번 코드를 작성해보자.

 

주의 opencv 신버전부터는 코드가 바뀝니다.

cv2.circle(dst, (int(cx), int(cy)), int(radius), (0, 0, 255), 2, cv2.LINE_AA)

밑에 코드는 위와 같이 수정되어야합니다.

import sys
import numpy as np
import cv2

src = cv2.imread('./coin.jpg', cv2.IMREAD_GRAYSCALE)
# 이미지를 읽어온다.

if src is None:
    print('Image open failed!')
    sys.exit()
# 이미지가 없으면 실행하고 종료

src = cv2.resize(src, dsize=(720, 540))
# 이미지의 사이즈를 조절한다.

blr = cv2.GaussianBlur(src, (0, 0), 1.0)
# 이미지의 잡음을 제거한다.

# 트렉바를 정의한다.
def on_trackbar(pos):
    rmin = cv2.getTrackbarPos('minRadius', 'img')
    rmax = cv2.getTrackbarPos('maxRadius', 'img')
    th = cv2.getTrackbarPos('threshold', 'img')
    
    # 실질적인 허프변환이 시작되는 부분
    circles = cv2.HoughCircles(blr, cv2.HOUGH_GRADIENT, 1, 50, param1=120, param2=th, minRadius=rmin, maxRadius=rmax)
    # 반지름과 threshold를 조절하면서 확인해볼 분이다.

    dst = src.copy()
    # 이미지를 복사해서 dst에 저장한다.
    
    # 원을 검출할 때 실행된다.
    if circles is not None:
        for i in range(circles.shape[1]):
        # 검출된 원의 개수만큼 돌아서 원을 그린다.
            cx, cy, radius = circles[0][i]
            cv2.circle(dst, (cx, cy), radius, (0, 0, 255), 2, cv2.LINE_AA)
    cv2.imshow('img', dst)

# 트랙바 생성
cv2.imshow('img', src)
cv2.createTrackbar('minRadius', 'img', 0, 100, on_trackbar)
cv2.createTrackbar('maxRadius', 'img', 0, 150, on_trackbar)
cv2.createTrackbar('threshold', 'img', 0, 100, on_trackbar)
cv2.setTrackbarPos('minRadius', 'img', 10)
cv2.setTrackbarPos('maxRadius', 'img', 80)
cv2.setTrackbarPos('threshold', 'img', 40)
cv2.waitKey()
cv2.destroyAllWindows()

 

 

생각지 못한 문제가 발생한다.

동전을 찍었을때 그 모양이 타원일 경우 인식하지 않는다.

사진을 다시 찍어서 진행을 해보자.

 

 

원의 색을 변경시켰다.

조금이라도 타원이 되는 경우 잘 검출하지 못한다.

거기다가 동전의 모양(?) 홈(?) 때문에 색이 급격히 변화한다고 인식하고 있을 수도 있다.

 

근데 분명히 나는 원으로 잘 보이게 찍었는데 

왜 타원이 되어버릴까

그 문제는 나중에 생각하고

 

일단.. 이진화를 진행해야겠다는 생각이 들었다.

적응형 이진화 코드를 사용하여 이진화를 진행해보자.

 

 

일단 이미지처리가 잘되고 있는지 확인하기 위해서는 히프변환을 제외하고 봐야하겠지

import sys
import numpy as np
import cv2

img = cv2.imread('./coin2.jpg', cv2.IMREAD_GRAYSCALE)

if img is None:
    print("Image load failed")
    sys.exit()

img = cv2.resize(img, dsize=(720, 540))
# 노이즈 제거
blr = cv2.GaussianBlur(img, (0, 0), 1.0)

dst = cv2.adaptiveThreshold(blr, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 201, 5)


cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

 

이대로 히프변환을 해버리자.

import sys
import numpy as np
import cv2

src = cv2.imread('./coin2.jpg', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image open failed!')
    sys.exit()

src = cv2.resize(src, dsize=(720, 540))
blr = cv2.GaussianBlur(src, (0, 0), 1.0)
bina = cv2.adaptiveThreshold(blr, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 201, 5)

def on_trackbar(pos):
    rmin = cv2.getTrackbarPos('minRadius', 'img')
    rmax = cv2.getTrackbarPos('maxRadius', 'img')
    th = cv2.getTrackbarPos('threshold', 'img')
    circles = cv2.HoughCircles(bina, cv2.HOUGH_GRADIENT, 1, 50 , param1=120, param2=th, minRadius=rmin, maxRadius=rmax)

    dst = src.copy()
    if circles is not None:
        for i in range(circles.shape[1]):
            cx, cy, radius = circles[0][i]
            cv2.circle(dst, (int(cx), int(cy)), int(radius), (255, 0, 0), 2, cv2.LINE_AA)
    cv2.imshow('img', dst)

# 트랙바 생성
cv2.imshow('img', src)
cv2.createTrackbar('minRadius', 'img', 0, 100, on_trackbar)
cv2.createTrackbar('maxRadius', 'img', 0, 150, on_trackbar)
cv2.createTrackbar('threshold', 'img', 0, 100, on_trackbar)
cv2.setTrackbarPos('minRadius', 'img', 10)
cv2.setTrackbarPos('maxRadius', 'img', 80)
cv2.setTrackbarPos('threshold', 'img', 40)
cv2.waitKey()
cv2.destroyAllWindows()

이정도 까지는 잡아내는데, 적절한 값을 찾기가 너무 어렵다.

 

 

일단 내가 생각한 첫 번째 문제는

이미지를 resizing하면서 타원으로 만들어져 버리는 문제

아까와 같은 방식으로 이미지의 비율을 출력하고

그에 맞게 줄여야겠다.

(하긴 사진을 바꾸고 사이즈 조절을 했으니까)

 

2408 : 2566

900:959로 변경하자

 

 

이진화를 적용하지 않은 코드에 한 번 적용을 해보자.

코드는 중복되니까 일단 가린다.

더보기
import sys
import numpy as np
import cv2

src = cv2.imread('./coin2.jpg', cv2.IMREAD_GRAYSCALE)
# 이미지를 읽어온다.

if src is None:
    print('Image open failed!')
    sys.exit()
# 이미지가 없으면 실행하고 종료

src = cv2.resize(src, dsize=(900, 959))
# 이미지의 사이즈를 조절한다.

blr = cv2.GaussianBlur(src, (0, 0), 1.0)
# 이미지의 잡음을 제거한다.

# 트렉바를 정의한다.
def on_trackbar(pos):
    rmin = cv2.getTrackbarPos('minRadius', 'img')
    rmax = cv2.getTrackbarPos('maxRadius', 'img')
    th = cv2.getTrackbarPos('threshold', 'img')
    
    # 실질적인 허프변환이 시작되는 부분
    circles = cv2.HoughCircles(blr, cv2.HOUGH_GRADIENT, 1, 50, param1=120, param2=th, minRadius=rmin, maxRadius=rmax)
    # 반지름과 threshold를 조절하면서 확인해볼 분이다.

    dst = src.copy()
    # 이미지를 복사해서 dst에 저장한다.
    
    # 원을 검출할 때 실행된다.
    if circles is not None:
        for i in range(circles.shape[1]):
        # 검출된 원의 개수만큼 돌아서 원을 그린다.
            cx, cy, radius = circles[0][i]
            cv2.circle(dst, (int(cx), int(cy)), int(radius), (255, 0, 0), 2, cv2.LINE_AA)
    cv2.imshow('img', dst)

# 트랙바 생성
cv2.imshow('img', src)
cv2.createTrackbar('minRadius', 'img', 0, 100, on_trackbar)
cv2.createTrackbar('maxRadius', 'img', 0, 150, on_trackbar)
cv2.createTrackbar('threshold', 'img', 0, 100, on_trackbar)
cv2.setTrackbarPos('minRadius', 'img', 10)
cv2.setTrackbarPos('maxRadius', 'img', 80)
cv2.setTrackbarPos('threshold', 'img', 40)
cv2.waitKey()
cv2.destroyAllWindows()

정확게 모두다 잡아낸다.

 

 

이번에는 이진화가 꼭 필요한지 확인을 해봐야 하니까

이진화를 적용한 코드에도 리사이징을 해보자.

더보기
import sys
import numpy as np
import cv2

src = cv2.imread('./coin2.jpg', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image open failed!')
    sys.exit()

src = cv2.resize(src, dsize=(900, 959))
blr = cv2.GaussianBlur(src, (0, 0), 1.0)
bina = cv2.adaptiveThreshold(blr, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 201, 5)

def on_trackbar(pos):
    rmin = cv2.getTrackbarPos('minRadius', 'img')
    rmax = cv2.getTrackbarPos('maxRadius', 'img')
    th = cv2.getTrackbarPos('threshold', 'img')
    circles = cv2.HoughCircles(bina, cv2.HOUGH_GRADIENT, 1, 50 , param1=120, param2=th, minRadius=rmin, maxRadius=rmax)

    dst = src.copy()
    if circles is not None:
        for i in range(circles.shape[1]):
            cx, cy, radius = circles[0][i]
            cv2.circle(dst, (int(cx), int(cy)), int(radius), (255, 0, 0), 2, cv2.LINE_AA)
    cv2.imshow('img', dst)

# 트랙바 생성
cv2.imshow('img', src)
cv2.createTrackbar('minRadius', 'img', 0, 100, on_trackbar)
cv2.createTrackbar('maxRadius', 'img', 0, 150, on_trackbar)
cv2.createTrackbar('threshold', 'img', 0, 100, on_trackbar)
cv2.setTrackbarPos('minRadius', 'img', 10)
cv2.setTrackbarPos('maxRadius', 'img', 80)
cv2.setTrackbarPos('threshold', 'img', 40)
cv2.waitKey()
cv2.destroyAllWindows()

 

오히려 정확도가 떨어지는 것으로 보인다.

 

그러니 이진화는 굳이 필요가 없다.

 

 

일단 검출은 완료했고, 그러면 

detection된 반지름을 출력하게 만들어서 

조건문으로 현재 동전이 얼마인지 리턴하게 만들자.

 

일단 적절한 파라미터값을 찾았다면 setTrackbarPos를 수정하면된다.

 

일단 처음에는 반지름을 동전위에 알려주게 만들자.

다음의 코드를 추가합니다.

cv2.putText(dst, str(radius), org=(cx, cy), 
                        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                        color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
import sys
import numpy as np
import cv2

src = cv2.imread('./coin2.jpg', cv2.IMREAD_GRAYSCALE)
# 이미지를 읽어온다.

if src is None:
    print('Image open failed!')
    sys.exit()
# 이미지가 없으면 실행하고 종료

src = cv2.resize(src, dsize=(900, 959))
# 이미지의 사이즈를 조절한다.

blr = cv2.GaussianBlur(src, (0, 0), 1.0)
# 이미지의 잡음을 제거한다.

# 트렉바를 정의한다.
def on_trackbar(pos):
    rmin = cv2.getTrackbarPos('minRadius', 'img')
    rmax = cv2.getTrackbarPos('maxRadius', 'img')
    th = cv2.getTrackbarPos('threshold', 'img')
    
    # 실질적인 허프변환이 시작되는 부분
    circles = cv2.HoughCircles(blr, cv2.HOUGH_GRADIENT, 1, 50, param1=120, param2=th, minRadius=rmin, maxRadius=rmax)
    # 반지름과 threshold를 조절하면서 확인해볼 분이다.

    dst = src.copy()
    # 이미지를 복사해서 dst에 저장한다.
    
    # 원을 검출할 때 실행된다.
    if circles is not None:
        for i in range(circles.shape[1]):
        # 검출된 원의 개수만큼 돌아서 원을 그린다.
            cx, cy, radius = circles[0][i]
            cv2.circle(dst, (cx, cy), radius, (255, 0, 0), 2, cv2.LINE_AA)
            cv2.putText(dst, str(radius), org=(cx, cy), 
                        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                        color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
    cv2.imshow('img', dst)

# 트랙바 생성
cv2.imshow('img', src)
cv2.createTrackbar('minRadius', 'img', 0, 100, on_trackbar)
cv2.createTrackbar('maxRadius', 'img', 0, 150, on_trackbar)
cv2.createTrackbar('threshold', 'img', 0, 100, on_trackbar)
cv2.setTrackbarPos('minRadius', 'img', 10)
cv2.setTrackbarPos('maxRadius', 'img', 80)
cv2.setTrackbarPos('threshold', 'img', 40)
cv2.waitKey()
cv2.destroyAllWindows()

 

그러면 다음처럼 반지름이 표시됩니다.

 

이 상태에서 각 동전이 얼마짜리인가 판단을 해봅시다.

10원은 40~46

50원은 52~55 

100원은 57 ~ 62 

500원은 63~68 까지로 범위를 잡으면

 

 

 

다음처럼 코드를 수정합니다.

cx, cy, radius = circles[0][i]
            cv2.circle(dst, (int(cx), int(cy)), int(radius), (255, 0, 0), 2, cv2.LINE_AA)
            if radius >= 40 and radius <= 46:
                cv2.putText(dst, "10", org=(int(cx), int(cy)), 
                        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                        color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
            
            elif radius >= 52 and radius <= 55:
                cv2.putText(dst, "50", org=(int(cx), int(cy)), 
                        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                        color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
            
            elif radius >= 57 and radius <= 62:
                cv2.putText(dst, "100", org=(int(cx), int(cy)), 
                        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                        color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
            
            elif radius >= 63 and radius <= 68:
                cv2.putText(dst, "500", org=(int(cx), int(cy)), 
                        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                        color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
import sys
import numpy as np
import cv2

src = cv2.imread('./coin2.jpg')
# 이미지를 읽어온다.

if src is None:
    print('Image open failed!')
    sys.exit()
# 이미지가 없으면 실행하고 종료

src = cv2.resize(src, dsize=(900, 959))
# 이미지의 사이즈를 조절한다.

gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
blr = cv2.GaussianBlur(gray, (0, 0), 1.0)
# 이미지의 잡음을 제거한다.

# 트렉바를 정의한다.
def on_trackbar(pos):
    rmin = cv2.getTrackbarPos('minRadius', 'img')
    rmax = cv2.getTrackbarPos('maxRadius', 'img')
    th = cv2.getTrackbarPos('threshold', 'img')
    
    # 실질적인 허프변환이 시작되는 부분
    circles = cv2.HoughCircles(blr, cv2.HOUGH_GRADIENT, 1, 50, param1=120, param2=th, minRadius=rmin, maxRadius=rmax)
    # 반지름과 threshold를 조절하면서 확인해볼 분이다.

    dst = src.copy()
    # 이미지를 복사해서 dst에 저장한다.
    
    # 원을 검출할 때 실행된다.
    if circles is not None:
        for i in range(circles.shape[1]):
        # 검출된 원의 개수만큼 돌아서 원을 그린다.
            cx, cy, radius = circles[0][i]
            cv2.circle(dst, (int(cx), int(cy)), int(radius), (255, 0, 0), 2, cv2.LINE_AA)
            if radius >= 40 and radius <= 46:
                cv2.putText(dst, "10", org=(int(cx), int(cy)), 
                        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                        color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
            
            elif radius >= 52 and radius <= 55:
                cv2.putText(dst, "50", org=(int(cx), int(cy)), 
                        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                        color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
            
            elif radius >= 57 and radius <= 62:
                cv2.putText(dst, "100", org=(int(cx), int(cy)), 
                        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                        color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
            
            elif radius >= 63 and radius <= 68:
                cv2.putText(dst, "500", org=(int(cx), int(cy)), 
                        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                        color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
    cv2.imshow('img', dst)

# 트랙바 생성
cv2.imshow('img', src)
cv2.createTrackbar('minRadius', 'img', 0, 100, on_trackbar)
cv2.createTrackbar('maxRadius', 'img', 0, 150, on_trackbar)
cv2.createTrackbar('threshold', 'img', 0, 100, on_trackbar)
cv2.setTrackbarPos('minRadius', 'img', 10)
cv2.setTrackbarPos('maxRadius', 'img', 80)
cv2.setTrackbarPos('threshold', 'img', 40)
cv2.waitKey()
cv2.destroyAllWindows()

 

성공적으로 동전을 잡아내는 모습.

 

그러나 여기까지는 일단 내 사진에 맞는 코드를 작성했다.

범용성을 높히기 위해서는 사진을 비율로서 접근해야한다.

그러면 지금처럼 수치상으로 접근하는 것이 아니라

비율로서 접근을 해야한다.

근데 그렇게 접근하면 100원을 원으로 검출하는게 아니라 100원을 100원이라고 정확히 인지할 줄 알아야한다.

그래야 이게 100원이니까 다른 동전의 비율은 어떻게 될 것이다 유추가 가능할 것이니

 

 

일단 그건 나중에 생각하고 일단

실시간으로 넣어보고

정확하게 잡을 때까지 사용자가 높이를 조정한다면 괜찮지 않을까..

가지고 있는 카메라를 일단 준비하자.

 

 

가장 기초적인 카메라 구동 소스코드이다.

import sys
import numpy as np
import cv2



cap = cv2.VideoCapture(0)

if not cap.isOpened():
    print("열리지 않아요")
    sys.exit()


while True:
    ret, frame = cap.read()

    if not ret:
        break

    cv2.imshow('frame', frame)
    key = cv2.waitKey(1)

    if key == 27:
        break


cap.release()
cv2.destroyAllWindows()

 

이를 방금 만든 코드와 합체하면된다.

 

 

트랙바는 이제 필요없으니 파라미터 값을 지정하고

다음처럼 코드를 수정하자

import sys
import numpy as np
import cv2

src = cv2.imread('./coin2.jpg')
# 이미지를 읽어온다.

if src is None:
    print('Image open failed!')
    sys.exit()
# 이미지가 없으면 실행하고 종료

src = cv2.resize(src, dsize=(900, 959))
# 이미지의 사이즈를 조절한다.

gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
blr = cv2.GaussianBlur(gray, (0, 0), 1.0)
# 이미지의 잡음을 제거한다.


# 실질적인 허프변환이 시작되는 부분
circles = cv2.HoughCircles(blr, cv2.HOUGH_GRADIENT, 1, 50, param1=120, param2=40, minRadius=10, maxRadius=80)
# 반지름과 threshold를 조절하면서 확인해볼 분이다.

dst = src.copy()
# 이미지를 복사해서 dst에 저장한다.

# 트랙바 생성
cv2.imshow('img', src)
  
# 원을 검출할 때 실행된다.
if circles is not None:
    for i in range(circles.shape[1]):
    # 검출된 원의 개수만큼 돌아서 원을 그린다.
        cx, cy, radius = circles[0][i]
        cv2.circle(dst, (int(cx), int(cy)), int(radius), (255, 0, 0), 2, cv2.LINE_AA)
        if radius >= 40 and radius <= 46:
            cv2.putText(dst, "10", org=(int(cx), int(cy)), 
            fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
            color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
            
        elif radius >= 52 and radius <= 55:
            cv2.putText(dst, "50", org=(int(cx), int(cy)), 
            fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
            color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
            
        elif radius >= 57 and radius <= 62:
            cv2.putText(dst, "100", org=(int(cx), int(cy)), 
            fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
            color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
            
        elif radius >= 63 and radius <= 68:
            cv2.putText(dst, "500", org=(int(cx), int(cy)), 
            fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
            color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)

    cv2.imshow('img', dst)
    


cv2.waitKey()
cv2.destroyAllWindows()

 

 

 

 

그리고 다음처럼 실시간에서 동작하게 합치면 된다.

 

import sys
import numpy as np
import cv2

cap = cv2.VideoCapture(0)
if not cap.isOpened():
    print("열리지 않아요")
    sys.exit()

while True:
    ret, frame = cap.read()
    if not ret:
        break

    #src = cv2.resize(frame, dsize=(900, 959))
    # 이미지의 사이즈를 조절한다.

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    blr = cv2.GaussianBlur(gray, (0, 0), 1.0)
    # 이미지의 잡음을 제거한다.


    # 실질적인 허프변환이 시작되는 부분
    circles = cv2.HoughCircles(blr, cv2.HOUGH_GRADIENT, 1, 50, param1=120, param2=40, minRadius=10, maxRadius=80)
    # 반지름과 threshold를 조절하면서 확인해볼 분이다.

    dst = frame.copy()
    # 이미지를 복사해서 dst에 저장한다.

    cv2.imshow('img', frame)
    
    # 원을 검출할 때 실행된다.
    if circles is not None:
        for i in range(circles.shape[1]):
        # 검출된 원의 개수만큼 돌아서 원을 그린다.
            cx, cy, radius = circles[0][i]
            cv2.circle(dst, (int(cx), int(cy)), int(radius), (255, 0, 0), 2, cv2.LINE_AA)
            if radius >= 40 and radius <= 46:
                cv2.putText(dst, "10", org=(int(cx), int(cy)), 
                fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
                
            elif radius >= 52 and radius <= 55:
                cv2.putText(dst, "50", org=(int(cx), int(cy)), 
                fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
                
            elif radius >= 57 and radius <= 62:
                cv2.putText(dst, "100", org=(int(cx), int(cy)), 
                fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
                
            elif radius >= 63 and radius <= 68:
                cv2.putText(dst, "500", org=(int(cx), int(cy)), 
                fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
                color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)

    cv2.imshow('img', dst)
    key = cv2.waitKey(1)

    if key == 27:
        break
    


cap.release()
cv2.destroyAllWindows()

 

 

 

 

다음음 원이 아니라 사각형을 검출하게 해보겠다.