이번 영상에서는 OpenCV를 사용하여 파란공의 위치를 트래킹하여 영상에 그림을 그려보는 코드를 소개합니다. 











import cv2 as cv



def draw_ball_location(img_color, locations):
    for i in range(len(locations)-1):

        if locations[0] is None or locations[1] is None:
            continue

        cv.line(img_color, tuple(locations[i]), tuple(locations[i+1]), (0, 255, 255), 3)

    return img_color



cap = cv.VideoCapture(0)


list_ball_location = []
history_ball_locations = []
isDraw = True

while True:

    ret,img_color = cap.read()

    img_color = cv.flip(img_color, 1)


    img_hsv = cv.cvtColor(img_color, cv.COLOR_BGR2HSV)

    hue_blue = 120
    lower_blue = (hue_blue-20, 150, 0)
    upper_blue = (hue_blue+20, 255, 255)
    img_mask = cv.inRange(img_hsv, lower_blue, upper_blue)

    kernel = cv.getStructuringElement( cv.MORPH_RECT, ( 5, 5 ) )
    img_mask = cv.morphologyEx(img_mask, cv.MORPH_DILATE, kernel, iterations = 3)




    nlabels, labels, stats, centroids = cv.connectedComponentsWithStats(img_mask)



    max = -1
    max_index = -1

    for i in range(nlabels):

        if i < 1:
            continue

        area = stats[i, cv.CC_STAT_AREA]

        if area > max:
            max = area
            max_index = i


    if max_index != -1:


        center_x = int(centroids[max_index, 0])
        center_y = int(centroids[max_index, 1])
        left = stats[max_index, cv.CC_STAT_LEFT]
        top = stats[max_index, cv.CC_STAT_TOP]
        width = stats[max_index, cv.CC_STAT_WIDTH]
        height = stats[max_index, cv.CC_STAT_HEIGHT]


        cv.rectangle(img_color, (left, top), (left + width, top + height), (0, 0, 255), 5)
        cv.circle(img_color, (center_x, center_y), 10, (0, 255, 0), -1)

        if isDraw:
            list_ball_location.append((center_x, center_y))
       
        else:
            history_ball_locations.append(list_ball_location.copy())
            list_ball_location.clear()


    img_color = draw_ball_location(img_color, list_ball_location)

    for ball_locations in history_ball_locations:
        img_color = draw_ball_location(img_color, ball_locations)




    cv.imshow('Blue', img_mask)
    cv.imshow('Result', img_color)
   
    key = cv.waitKey(1)
    if key == 27: # esc
        break
    elif key == 32: # space bar
        list_ball_location.clear()
        history_ball_locations.clear()
    elif key == ord('v'):
        isDraw = not isDraw



작성 2020. 3. 26


포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
댓글로 알려주시면 빠른 시일내에 답변을 드리겠습니다.

여러분의 응원으로 좋은 컨텐츠가 만들어집니다.
지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 : )

유튜브 구독하기


제가 쓴 책도 한번 검토해보세요.

  1. Han 2020.05.28 04:27

    안녕하세요. 공부하면서 유툽 자주 보는 구독자 입니다.
    항상 올려주시는 영상 감사히 보고 있습니다.

    다름이 아니라 해당 코드를 파이참에서 실행하면

    cv.imshow('Result', img_color)
    cv2.error: OpenCV(4.2.0) C:\projects\opencv-python\opencv\modules\highgui\src\window.cpp:376: error: (-215:Assertion failed) size.width>0 && size.height>0 in function 'cv::imshow'

    [ WARN:0] global C:\projects\opencv-python\opencv\modules\videoio\src\cap_msmf.cpp (674) SourceReaderCB::~SourceReaderCB terminating async callback


    이 에러가 뜨면서 윈도우 창이 열렸다가 바로 꺼집니다 ㅜㅜ

    현재 제 opencv버전은 4.2.0이고 파이썬은 3.7.7입니다.
    (버전 문제인지 해당 코드의 cv2.flip()이 존재하지 않는다 하여 그 부분만 주석처리 했습니다. 화면을 안 뒤집고 사용하려구요)

    테스트 코드로 혹시 웹캠이 아예 작동하지 않나, 확인해봤는데
    videocapture(), read()를 사용해 동영상 촬영 시 imshow()가 정상적으로 실행됩니다...

    어떤 문제점이 있는지 궁금해서 댓글 남깁니다.

    답글 달아주시면 감사하겠습니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.28 05:43 신고

      img_color를 출력하는 imshow에서 입력영상이 잘못되었다고 에러가 났습니다. 확인해보세요

    • Han 2020.05.29 01:56

      답변 정말 감사합니다!!

      이상하게 draw_ball_location() 만 다녀오면 저 오류가 떠서 함수를

      # 기존
      def draw_ball_location(img_color, locations):
      for i in range(len(locations) - 1):
      if locations[0] is None or locations[1] is None:
      continue

      cv.line(img_color, tuple(locations[i]), tuple(locations[i+1]), (0, 255, 255), 3)

      return img_color

      # 수정
      def draw_ball_location(img_color, locations):
      for i in range(len(locations) - 1):
      if locations[0] is None or locations[1] is None:
      continue

      cv.line(img_color, tuple(locations[i]), tuple(locations[i+1]), (0, 255, 255), 3)

      #return img_color

      이렇게 바꿔서 처리하니까 잘 동작합니다!

      다시 한 번 조언 정말 감사드립니다.

    • Han 2020.05.30 22:39

      webnautes님께.

      혹시 이 코드를 제 깃 repo에 저장해도 될까요? 코드 상단에 출처를 명시하겠습니다!

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.30 22:59 신고

      네 그러셔도 됩니다~

    • Han 2020.05.31 04:08

      감사합니다~

  2. 다승 2020.05.28 11:31

    재밌게 잘 봤습니다.

    응용해서 그레이 스케일에서 흰 공만 검출하려고 하는데요. (배경은 비교적 어두움) 몇 번 실패했네요 ㅠㅠ

    어떻게 하면 할 수 있을까요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.28 11:56 신고

      이진화시 사용할 파라미터만 잘 조정하면 될듯합니다. 트랙바를 추가하여 테스트하며 값을 바꾸어보세요

  3. 다승 2020.05.30 17:56

    답변 감사합니다. 하지만 제가 많이 부족해서 아직 해결이 안 됐네요.

    https://www.youtube.com/watch?v=bZQllT4BdiY

    이런 흑백의 초고속 영상에서 공이 손을 떠날 때 시간을 리턴하려고 합니다.

    올려주신 걸 보고 차영상(배경제거)를 한 뒤 원을 검출하려고 했는데 해결이 잘 안되네요.

    img_hsv = cv.cvtColor(img_color, cv.COLOR_BGR2GRAY)
    img_mask = cv.inRange(img_hsv, 250, 255)
    kernel = cv.getStructuringElement( cv.MORPH_ELLIPSE , ( 5, 5 ) )

    차영상에서 몸통도 움직여서 그쪽에 잡히는 경우가 있어서요. (이건 엄밀히 타원도 아닌데 잡혀서ㅠㅠ)

    혹시 도움 좀 받을 수 있을까요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.30 18:01 신고

      hough circle을 사용해보세요. 아래 링크에 설명되어 있습니다

      검출속도가 느리기때문에 프레임수를 낮춰서 해야 할겁니다

      https://webnautes.tistory.com/949

  4. LEE 2020.05.31 15:54

    안녕하세요 ! 저 궁금한 점이 있어서 문의 드립니다 !
    다름이 아니고, 원의 원점 좌표를 PUT TEXT를 사용하여 화면에 나타내고 싶은데, 아무리 해도 안되네요ㅜㅜ
    원점 좌표를 나타내려면 어떻게 해야할지 궁금합니다ㅜ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.31 15:57 신고

      다음 코드 바로 전에 좌표출력하는걸 넣어보세요


          cv.imshow('Result', img_color)

    • LEE 2020.05.31 16:00

      cv.putText(img_color, '{}, {}', (center_x, center_y), cv.FONT_HERSHEY_PLAIN, 3, (0, 255, 255), 2)

      이런식으로 넣으려고 했는데, 원점 좌표는 계속 변하다보니까 어떻게 해야하는지 잘 모르겠습니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.31 16:09 신고

      원을 따라다니며 좌표를 출력하고 있나요? 좌표를 고정해놓고 출력해도 될듯한데요

    • LEE 2020.05.31 16:30

      네네 ! 바로 수정해서 고정했습니다 !
      감사합니다 !

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.31 16:31 신고

      다행입니다

  5. len 2020.06.23 19:29

    카메라에 공을 잡지 않았는데 자기 마음대로 인식하고 선을 그어요... 왜 그런지 알 수 있나요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.06.23 20:21 신고

      포스트의 코드는 색기반으로 물체를 인식하여 해당 물체의 위치에 그림을 그려줍니다.

      색기반이다보니 조명등의 영향으로 엉뚱한 물체가 인식되어 오동작할 수 있습니다.

  6. 하루하루 2020.08.11 15:59

    안녕하세요! 항상 감사히 보고 있습니다.

    videocapture(0)를 사용해서 실시간(동영상)으로 검출하는게 아닌

    1초마다 사진을 찍어, 이미지데이터를 띄우는 방법은 없을까요 ?

    감사합니다!

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.08.11 17:23 신고

      영상처럼 보이지만 실제론 이미지 한장씩 가져오는 것입니다.

    • BlogIcon 하루하루 2020.08.11 17:28

      답변 감사드립니다.

      동영상이 이미지를 연속적으로 가져온다는 것은 알고 있습니다.

      연속적으로 받아들여지는 이미지 양이 많아 제가 작성하고 있는 코드와는 맞지 않아, 이미지를 1초에 1개씩만 가져오고 싶습니다. 방법이 있을까요?

      감사합니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.08.11 17:35 신고

      영상가져올때마다 인덱스를 증가시키고 %연산자를 사용하여 특정 n번째마다 영상을 사용하면 될듯합니다

  7. liberty67 2020.09.28 22:54

    항상 정보 감사합니다.
    위 예제에서 draw_ball_location 함수가 튜플을 사용하고 있는데 C++에서 구현하는 경우는 어떤 자료형을 사용해야 할까요??
    제가 파이썬과 C++가 익숙하지 않아 질문드립니다.

  8. liberty67 2020.09.29 11:42

    답변 감사드립니다.
    draw_ball_location(Mat img_color, vector<int> locations) {
    for (int i = 0; i < locations.size(); i++) {
    line(img_color, Point(locations[i],locations[i+1]), Point(locations[i+2],locations[i+3]), Scalar(0,255,0),2);
    }
    저는 이렇게 함수를 만들어봤는데 벡터로 line함수를 이용할 수 있을까요?
    제가 아직 초보라 미숙한 부분이 많습니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.29 15:43 신고

      볼 위치는 x, y로 얻을 수 있을텐데 어떤것을 그리려고 하나요

    • BlogIcon liberty67 2020.09.29 15:48

      위 예제와 동일한 기능을 하는 C++ 코드를 작성중입니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.29 15:52 신고

      아.. 공이 지나간 궤적을 그리는 부분이군요.. Point 클래스 타입으로 vector를 사용하세요. 그러면 x좌표 y좌표로 이루어진 파이썬의 튜플과 유사하게 작성가능합니다

    • BlogIcon liberty67 2020.09.29 22:30

      Point 클래스 타입을 vector로 사용하라는 말씀이 정확히 어떤 의미인지 모르겠는데 혹시 자세히 알려주실수 있으신가요??

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.29 23:24 신고

      링크를 참고하세요

      https://stackoverflow.com/a/15965755

    • BlogIcon liberty67 2020.09.30 00:44

      답변 정말 감사드립니다!!

    • BlogIcon liberty67 2020.09.30 13:20

      즐거운 추석 연휴 보내시길 바랍니다.

      for ball_locations in history_ball_locations:
      img_color = draw_ball_location(img_color, ball_locations)

      위 예제에 있는 코드인데 이 코드는 어떤 역할을 하는건가요???

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.30 13:33 신고

      감사합니다. 즐거운 추석보내세요.. 지금까지 공이 지난 경로를 다 그려줍니다

    • BlogIcon liberty67 2020.09.30 13:40

      답변빠르게 주셔서 정말 감사합니다.
      한가지 더 여쭤보고싶습니다.
      def draw_ball_location(img_color, locations):
      for i in range(len(locations)-1):

      if locations[0] is None or locations[1] is None:
      continue

      cv.line(img_color, tuple(locations[i]), tuple(locations[i+1]), (0, 255, 255), 3)

      return img_color

      이 코드에서

      if locations[0] is None or locations[1] is None:
      continue

      코드는 필수적으로 작성되어야 하는 코드인가요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.30 13:41 신고

      예외처리 입니다. 테스트하다가 문제 발생하여 추가한 걸로 기억납니다.

    • BlogIcon liberty67 2020.09.30 14:01

      https://runestone.academy/runestone/books/published/cpp4python/AtomicData/AtomicData.html
      링크에서 파이썬의 None은 C++의 nullptr와 유사하다고 나오는데 예외처리 시 nullptr를 사용할 수 있을까요???

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.30 14:18 신고

      좌표 넘겨준 타입에 맞춰해야 합니다

    • BlogIcon liberty67 2020.09.30 14:40

      Mat draw_ball_location(Mat& img_color, vector<Point> locations) {

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

      if (locations.at(0) == Point(0,0) || locations.at(1) == Point(0,0))
      continue;

      line(img_color, locations.at(i), locations.at(i+1), Scalar(0,255,0),2);
      }

      return img_color;
      }
      예제를 참고하여 제가 작성해본 함수입니다. 프로그램 실행 후 파란물체를 캡처했을 때 abort() has been called 메시지와 함께 프로그램이 종료됩니다. 혹시 예외처리나 line함수 사용에 문제가 있는 상황일까요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.30 14:47 신고

      디버그해서 좌표들을 확인해보세요

    • BlogIcon liberty67 2020.09.30 15:27

      계속해서 귀찮게 해드려 정말 죄송합니다..
      for ball_locations in history_ball_locations:
      img_color = draw_ball_location(img_color, ball_locations)
      예제의 코드를 다음과 같이 작성해밨습니다.
      for (Point ball_locations : history_ball_location) {
      img_result = draw_ball_location(img_color, ball_locations);
      }
      제가 만든 draw_ball_location 함수는 두번째 아규먼트로 vector<Point>를 받고 있습니다. 해결방법을 모르겠는데 혹시 어떻게 해결할 수 있을까요??

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.30 18:13 신고

      다음처럼 바꾸어보세요

      for (int i = 0; i < locations.size()-1; i++) {

+ Recent posts