2016년 1월 24일 일요일

[C/C++] OpenCV 라이브러리로, 윤곽에 기반한 자동차 번호판 영역 추출 (License plates recognition)




Download link :






  5번째 게시물은 오픈소스 영상처리 라이브러리인 OpenCV의 활용을 정리했습니다.

3달만에 게시물을 올립니다. 작년에 블로그를 시작할 때는 댓글을 통한 관심사의

소통을 기대했으나.. 보통 주마다 50회 정도의 조회수만 기록하고 있습니다.

네이버 블로그로 시작해야 했나요 ㅎㅎ


  사실 진짜 이유는 사람들에게 알리는 것보다 저 스스로 공부해야할 것들이 훨씬 많다는 

걸 계속 느꼈습니다. 저는 먹고 보는 일상을 정리하는 것이 아닌, 아스팔트 같은 IT 글을

쓰고 싶었습니다. 그러다보니 한번 글을 쓰려고 소스코드와 이미지를 정리하면 2~3일이

걸리네요.

아무튼 이번 포스트가 누군가에겐 아스팔트가 되길 바랍니다.






  '영상처리', 글자 그대로 보면 매우 단순하고, 무얼하는건지 과정을 생각해보면

쉽게 떠오르지 않는 영역인 것 같습니다. 그 역할을 사람과 자동차에 빗대어

설명해볼까요? 단순히 '내가 운전하는 자동차'는 핸들과 스위치 같은 입력만 필요할

것입니다. 운전이란 것을 학습한 나 자신이 주는 입력을 자동차는 수동적으로

받아들일 뿐이죠. 하지만 '자율주행 자동차'라면 자동차는 카메라로 읽어들인

사진의 집합에서, 신호등이 '초록색'이라도 앞에서 '사람'이 툭 튀어나오면

알아서 멈춰서야 합니다. 자동차가 인공적으로 생각하는 수단을 프로그래밍으로

제공할 수 있어야 합니다.






  이번에 소개할 OpenCV는 그 역할을 제공해주는 C/C++ 라이브러리로, 인텔에서

시작되었습니다. 'Open'은 보통 오픈소스로 이루어지는 프로젝트에 붙고, 'CV'는

'Computer Vision'을 뜻합니다. 영상처리는 이미지 파일을 처리하기에 컴퓨터의

자원을 많이 소모하는데, 마이크로프로세서 제조업체인 인텔이 영상처리 영역을

성장시켰던 이유가 왠지 납득이 갑니다 ㅎㅎ.. Windows, Ubuntu와 같은 Linux,

Android에서도 동작하며, C/C++ 라이브러리지만 JAVA, Python 언어와 바인딩되었습니다.

'.NET' 프레임워크에서 C# 언어로 코딩할 수 있는 'EmguCV'라는 형제도 있습니다.

저는 우분투 리눅스에서 구현된 소스코드로 설명할 것이고, 설치 과정은 생략할

것입니다.


  블로그를 하면서 느낀 건데, 설치과정은 이미 다른 블로그가 너무 많아서 사람들이

검색해도 OpenCV 설치 같은 주제를 저와 함께 시작하기는 어렵다는걸 알았습니다 ㅎ

자동차 번호판 추출이라는 주제에 집중하겠습니다. 물론 그 과정을 설명하면서 기본

함수들의 설명도 뒤따릅니다. Windows에서 Visual Studio, Ubuntu에서 Qt,

라즈베리파이에선 G++ 컴파일러로만 개발하면서 개발환경 준비는 매우 단순함을 느낀

점도 있습니다. PC 우분투에서 구현했기에 Qt 프레임워크 위에서 개발했습니다.

사실 이 주제로 프로그래밍한 것은 2015년 전반기 때였습니다. 그런데 리눅스 커널 공부

도중에 소스코드가 들어있던 우분투 커널을 날려버렸습니다 ㅡ_ㅡ;;

작년에 했던 기억을 떠올리며 소스코드를 다시 구성했습니다. 






  Qt 프레임워크의 프로젝트 파일(.pro)에 OpenCV 라이브러리를 위한 컴파일 옵션을

추가해줍니다. 터미널 창에서 g++ 명령어로 컴파일 하셨을때 붙여주셨던 옵션입니다.







  전체 소스코드는 150줄 정도 밖에 안 됩니다. ㅎ 절차적인 과정 위주로 설명하기 위해

기능마다 함수로 나누거나 하지 않고 main() 함수에 모든 소스코드를 넣었습니다. 초기

부분은 OpenCV와 일반 C++ 입출력 패키지를 포함합니다. 'cstdlib' 패키지는 절대값을

구하는 'abs()' 함수가 딱 한 번 쓰이는데, 그것 때문에 포함했습니다. 그리고 아래에는

사용하는 일반 정수형, 실수형 변수들과 OpenCV 전용 변수들의 선언입니다. 특히

'Mat' 데이터 타입은 이미지 하나를 담는데 쓰이는 매트릭스로써 가장 일반적인

변수입니다. 'imread()' 함수를 통해 이미지 경로를 매개변수로 주어 Mat 변수인 'image'에

그림파일을 담습니다. 그리고 'imshow()' 함수를 통해 윈도우창 이름을 주고 띄워 봅니다.

이때 'waitKey(0)'라는 함수를 줘야지만, 그 구간에서 키보드 입력을 기다리며 멈춰줍니다.

waitkey(0)의 '0'는 밀리세컨드(ms) 변수입니다. '3000'을 주면 키보드 입력이 없을 때까지

3초를 기다려줍니다. 0을 주면 키보드 입력이 있을 때까지 끝없이 기다려줍니다.







  이렇게 그림파일 하나를 불러와 자동차 정면 이미지를 띄웠습니다. 혹시 차량 주인분이

말씀해주시면 사진은 바꾸도록 하겠습니다. 지금 목적은 '30루 2468'이라는 저 번호판

영역만을 떼어내는 것입니다. 그래야 저장용량도 적고, 광학 문자 인식 (OCR, Optical

Character Recognition)으로 이미지가 아닌 '30루 2468'을 문자 그 자체로 변환할 때

알맞기 때문이죠. 예로 보면,





  왼쪽과 같은 원본 이미지에서 번호판 영역을 추출하는데, 오른쪽처럼 번호판 영역만

남겨주지 않으면 OCR 후에 저장되는 문자열이 '70주 6279'가 아니라, 'CCTV 단속차량'이

저장될 수도 있겠죠? 'KIA'나 'SONATA'가 될 수도 있습니다. ㅎ 그래서 번호판 영역만

관심영역 (Region of Interest)으로 남겨주는 것이 여러모로 효율적입니다.




  포스트 제목에서도 알 수 있듯이, 저는 윤곽에 기반해 자동차 번호판 영역을 추출할

것입니다. 처음 시도는 색에 기반한 방법이었습니다. 하얀색 번호판에서 검은색 글씨를

찾는 프로그램을 구성했죠. 하지만..




  자동차 번호판은 통일되어 있는게 아니었습니다. 자동차도 하얀색 일 때는 하얀색

번호판의 검출률이 낮아지고, 저녁 석양 같은 빛의 영향에 이도 저도 아닌 색깔이

나오는 문제도 있었습니다. 번호판 색깔이 어떻든, 다른 색의 사이에서는 '경계선'

나타나고, 그것을 찾아주는 함수를 OpenCV 공식 홈페이지의 Reference manual에서 

찾을 수 있었습니다. 'findContours()' 함수가 그 역할을 해주었습니다. 우선 우리는

이제 색에 대한 고민이 줄어들었습니다 ㅎ findContours() 함수를 사용하기 전에

일단 이미지를 다듬는 '전처리' 과정을 적용합시다.




  전처리 과정은 빨강초록파랑으로 구성된 RGB 레벨 이미지를 Gray 레벨로

단순화시키고, 'Canny()'라는 가장자리(Edge) 검출 알고리즘을 적용하는 것입니다.

그럼 원본 컬러 이미지를 어떻게 회색 레벨로 바꿀 수 있을까요? 생각해보면

한 점마다 빨강, 초록, 파랑의 값을 모두 더해서 3으로 나눠주면 될 것 같습니다.

하지만 우리는 OpenCV라는 든든한 영상처리 라이브러리를 사용하고 있죠 ㅎ

'cvtColor()' 함수에 그레이 레벨로 바꾸고 싶은 이미지의 매트릭스를 넣고,

'CV_BGR2GRAY'를 인자로 주면 아래처럼 회색 레벨로 바꾸어줍니다. 만약 이렇게

바꾸는게 사용자 마음에 안 든다면 다운로드 받은 OpenCV의 내부 소스코드를

수정하거나 직접 만들 수도 있겠죠. 오픈소스의 힘입니다.





  회색 레벨로 단순화시킨 이미지에서 가장자리 (Edge)를 나타내 봅시다. 이것 역시

이미 OpenCV 라이브러리 안에 구현된 걸 사용해서 1줄입니다.







  Canny() 함수의 3번째 4번째 변수는 가장자리를 추출하는 '문턱값'(threshold)입니다. 이

값을 조정함으로써 흐릿한 엣지도 나타나게 할 수 있고 뚜렷한 엣지도 나타나지 않을 수

있습니다. 전처리 과정은 이것으로 마치지만, 사실 가장 힘든 구간이라고 생각합니다.

"소스코드 2줄로 끝나는데 뭐가 어려운거지?"라고 생각 하실 수도 있지만, 자동차가 찍힌

사진마다 이 수의 차이로 인해 잘 인식하던 번호판 영역이 나타나지 않을 수 있습니다.

15개의 자동차 이미지 중 10개가 잘 처리되고 5개가 그렇지 않은 상황에서, 5개를

인식시키기 위해 전처리 과정을 수정하면 원래 잘 처리되던 10개가 처리되지 않는 것이죠.

숫자와의 시소 싸움입니다 ㅡ_ㅡ; 전처리 과정만큼 중요한 것이 일관된 환경에서

자동차 사진을 촬영하는 것이란 생각이 들었습니다.




  이제 findContours() 함수를 사용할 수 있습니다.





"윤곽은 Canny() 함수로 다 나왔는데, 왜 또 findContours() 함수로 윤곽을 구하는 거지?"

할 수 있습니다. Canny() 함수는 전처리 과정으로, 단순히 눈에 보이는 윤곽을 잡아줬을

뿐입니다. findContours() 함수를 통해 개개의 윤곽의 집합인 'contours' 배열을 접근할

수 있습니다. 아래에 'boundRect'는 사각형의 집합인데, findContours() 함수로 개개

윤곽을 배열로써 접근할 수 있기에 각 윤곽마다 사각형을 씌울 수 있게 된 것입니다.

각 함수들의 매개변수는 OpenCV Reference manual에서 예를 보고 그대로 가져온 것입니다.

변경해 보고 싶다면 메뉴얼에서 매개변수에 대한 설명을 읽고 더나은 매개변수로

바꿔보고 결과를 확인해야 합니다.






  반복문을 통해 발견된 윤곽의 개수만큼 반복적으로 자동차 번호판 숫자에 합당한 지

평가합니다. 저는 발견된 윤곽의 가로세로 비율, 크기의 상하한을 통해 잡다한 윤곽을

예외처리했습니다. 정제된 알맞은 윤곽들만을 'boundRect2'라는 사각형 배열에 다시

담았습니다. 그렇게 나온 윤곽에 사각형을 씌운 처리 결과는 아래와 같습니다.




  자동차 번호판 영역의 윤곽과 함께 유사한 조건의 윤곽들이 검출된 것을 볼 수 있습니다.

만약 정제과정을 넣지 않고 모든 윤곽을 처리하면 어떻게 될까요?




  전혀 번호판 영역의 윤곽에 포함되지 않을 것 같은 윤곽들도 포함되어 프로세서의

불필요한 연산을 증가시킵니다. 물론 지나치게 타이트하게 하면 사진마다 처리과정을

바꾸게 되는 악순환이 생길 수 있습니다.




  이제는 번호판 영역의 윤곽과 주변 배경의 윤곽을 구분시켜야할 차례입니다. 번호판

영역의 사각형과 다른 영역 사각형이 차이점은 무엇이라 할 수 있을까요? 그 차이점을

통해 그것들을 분리하려면 어떤 처리과정을 넣어야할까요?




  여기서부터는 제가 고안한 방법을 소개해드립니다. 저는 방법을 생각하다 머릿 속에

익숙한 게임 하나가 떠올랐습니다.




  뱀 게임 (snake game)입니다. 저 파란색 사각형, 즉 윤곽 하나하나를 뱀이라고 생각하고,

오른쪽으로 움직이면서 먹이 (친구)를 얼마만큼 먹는지 세어보는 규칙을 구상했습니다.

제가 키워주고 싶은 뱀은 바로 번호판 영역의 제일 왼쪽에 있는 윤곽입니다.




  이제부터 구현할 규칙은 저 뱀에게 매우 유리한 게임이어야 합니다. 우선 뱀들에게

순서를 부여해야 합니다. 버블정렬을 통해 X좌표에 따라 뱀들을 정렬시킵시다.





  뱀들은 오른쪽으로 기어가면서 자기가 가는 길 위에 놓인 친구만 먹을 수 있습니다.

이것은 각 사각형의 왼쪽 위의 꼭지점 좌표 ( tl().x, tl().y, [Top Left] )를 기울기를 구하는

수단으로 사용해서, 먹을 수 있는 친구와 그렇지 못한 친구로 구분함으로써 가능합니다.





  기울기가 1이라면, x좌표가 1증가할 때 y 좌표도 1증가하는 경우입니다. 거의 같은

가로줄에 있는 친구인지 판단하는 거라서, 기울기가 1인 것은 너무 너그럽습니다.

그래서 저는 기울기를 0.25로 주었습니다. 이렇게 하면,




  가장 배부른 뱀은 번호판 가장 왼쪽의 윤곽이 될 수 있습니다. 또한 x좌표 차이가 150

이상이라면 너무 멀리있으니 무시하도록 했습니다. 이렇게 하면 가능성 없는 친구는 먹이로

보지 않기 때문에 반복문이 일찍 깨져서 (break) 효율성이 높아집니다. 이런 작업들은

각 윤곽들이 x좌표에 따라 정렬되어 있기에 가능합니다.




  x좌표가 작은 순서대로 모든 뱀마다 얼마만큼의 친구를 먹었는지 경쟁합니다. 가장 많은

친구를 먹은 뱀의 순서를 'select' 변수에 저장하고, 그때 가장 멀리서 먹은 친구와 자신과의

거리를 번호판 폭을 가늠하는 변수 'plate_width'로 저장합니다. 가장 마지막에 먹은

친구와의 거리, 생각해보면 자동차 번호판의 폭의 길이와 비슷하겠죠?





  이제 확인해봅시다.




  'select' 변수를 통해 기억해뒀던 배부른 뱀은 빨간색 사각형으로 원본 이미지 위에

그렸습니다. 또한 plate width 또한 'line()' 함수를 통해 이미지 위에 나타냈습니다.




  유리하게 만들어주었던 윤곽이 선택되었습니다. 파란색 사각형은 빨간색 이전에 가장

배부른 뱀이었습니다. 이제 번호판 영역을 추출해봅시다.




  image라는 매트릭스 내부에 Rect() 함수를 써서 원하는 크기만큼 선택할 수 있습니다.

'Rect(a, b, c, d)' 형식인데, 'a, b' 좌표에서 x좌표로 'c'만큼, y좌표로 'd'만큼 잘라내겠다는

의미입니다. 조금 더하고 빼서 여유롭게 잘라내었습니다. 'imwrite()' 함수는 imread()

함수의 반대로서, 지정된 디렉터리에 이미지 파일을 저장하는 함수입니다.







  아래는 추출이 제대로 안 되는 대표적 경우인 '돌밭' (rocky)입니다. 돌이 그럴듯한 윤곽을

너무 많이 만들어서 번호판을 이겨버립니다. 저 돌들을 무시하려고 전처리를 까다롭게

하면 번호판의 검출률도 같이 낮아져버리죠. 생각해보면 처음 카메라를 설치하는 조건에서

저런 환경을 배제하는 것이 현명하다고 생각되네요.







  필요하다면 분리된 번호판에서 문자열을 출력하는 OCR 과정을 적용할 수 있습니다.

번호판 영역만 남기는 것도 전체 사진 한장을 남기는 것보다 효율적이지만, 문자열로

남기면 공간적으로 매우 효율적입니다. 물론 번호판 자체여야 유효한 경우도 있습니다.

불법주차 카메라에 찍혔던 경험이 있으신 분들은 문자로 번호판 사진을 함께 수신했던

기억이 있으실 겁니다. OCR 과정은 이미지 '7'이 모니터에도 보이는 숫자 데이터

'7'로 저장될 수 있도록 합니다. 저는 이 과정을 구글에서 제공한 'tesseract-ocr'

기본환경으로 구현했습니다. 물론 OCR 소스코드를 그대로 포함한 것이기에 속도와

정확성 면에서 아주 높은 성능을 기대하기는 어려웠습니다.  소스코드는 아래 링크에서

다운로드 받으실 수 있습니다.





  이 글을 더 길게 만들어야하는지 고민되고, 저만의 문자열 추출 알고리즘을 완성하지 않고

tesseract-ocr의 적용 과정을 그대로 설명하는 것이 좋은건지 모르겠습니다. 분명

tesseract-ocr은 구글이 개발자들에게 주는 든든한 선물 중 하나입니다. OCR은 좀 더

공부한 후에 설명해드릴만 하다면 별도의 포스트로 정리하도록 하겠습니다.


  이번 포스트의 주제인 자동차 번호판 영역 추출은 여기까지 설명하겠습니다.


(tesseract-ocr을 활용한 게시물을 올렸습니다.   _LINK)






















댓글 92개:

  1. 안녕하세요 ! 번호판인식 코드를 공부하고있습니다.
    근데 올려주신 파일의 오류가 계속 뜨는데 어디서부터 해결해야 할까요??
    (133개의 오류가 뜨네요)

    답글삭제
  2. 안녕하세요. 결과출력창의 오류메시지를 확인해보셨나요? 133개의 오류라면

    OpenCV 라이브러리가 제대로 인식되지 못한 경우일 듯 합니다. 게시글처럼 우분투

    리눅스에서 개발하셨다면 > LIBS += `pkg-config opencv --cflags --libs`

    옵션을 G++ 컴파일 명령어나, Qt 프로젝트 파일(.pro)에 기입하셨는지 확인해

    보세요. 답변이 되었으면 좋겠네요.

    답글삭제
  3. 안녕하세요 올려주신 코드 정말 도움이 많이 되고있습니다.
    그런데 실행 해서 CANNY 까지 문제 없이 되는데 findcontours가 실행 되는곳에서 프로그램이 중단되고 설명도 안뜨는데 혹시 해결방법을 아시나요
    개발환경은 vs2015 opencv 3.1입니다.

    답글삭제
  4. 안녕하세요. CANNY까지 문제 없으시다면 Visual Studio와 OpenCV 간의

    개발환경 준비는 문제 없다고 봅니다. findContours() 함수에 알맞은

    매개변수가 채워지지 못한 것 같아요. 비주얼 스튜디오에서 소스코드 가장자리

    를 클릭하시면 빨간색 '중단점'이 생성됩니다. 문제가 있을 것 같은 부분

    근처에 중단점을 잡으시고 '디버그' 메뉴에서 '디버깅 시작 (F5)'으로

    실행하시면 마우스를 통해 일반 변수들의 실시간 변화를 확인하실 수 있습니다.

    contours나 hierarchy 변수들의 선언도 위와 같은지 봐주세요. 참고로 contours

    변수는 vector-vector-Point인데, Point는 OpenCV에서 점 하나의 X, Y좌표를

    담는 변수이고, 여기에 앞에 vector를 묶어주면 점의 집합, 즉 윤곽하나를

    나타냅니다. 또 여기에 다시 vector를 앞에 붙였으니 점 하나의 집합의 집합,

    즉 이미지 위에 보이는 윤곽'들'이 됩니다. 만약에 첫번째 윤곽의 세번째

    점의 좌표를 알고 싶다면,

    contours.at(0).at(2).x contours.at(0).at(2).y

    이렇게 확인할 수 있습니다. 디버깅 모드로 주변 변수들의 변화를 확인해서

    찾아가시면 될거같은데, 코멘트를 달면서 MFC에서 findContours() 함수를 쓰면

    에러가 난다는 얘기를 들은 기억이 갑자기 났습니다 -ㅅ-; 'OpenCV 1'의

    cvFindContours()는 괜찮은데 'OpenCV 2'의 findContours에서 오류가 난다는..

    혹시 콘솔환경에서도 돌려보시고 결과를 알려주시면 그 오류를 겪었다는

    지인에게 물어보겠습니다. 하루빨리 고민에서 벗어나시기를 바랍니다.

    답글삭제
  5. 답변 감사합니다.
    findContours(image2, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point());

    findContours(image2, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0,0));
    저 같은 경우엔 원래 console에서 실행 하고 있어서 다른 문제가 있나 찾아봤는데 findContours 마지막 파라미터를 Point()에서 Point(0,0) 으로 바꿔주었더니 실행 됐습니다.

    그리고 Bubble Sort에서 Out of Range오류가 발생해서
    for (int i = 0; i<boundRect2.size(); i++) {
    for (int j = 0; j<(boundRect2.size() - i -1 ); j++) {
    두번째 반복조건문 안에 (boundRect2.size() - i -1 ) 로 바꿔주었더니 남은 코드 전부 문제없이 실행 됐습니다.

    똑같은 문제 겪고 계신분들이 있다면 도움이 됐으면 좋겠네요.

    좋은 자료 올려주셔서 정말 감사합니다.

    답글삭제
    답글
    1. 와 저도 같은 문제를 겪고 있던 상황이었는데 덕분에 결과가 제대로 나왔습니다. 정말정말 감사합니다. 글 작성자님과 이 답변 덕분에 공부하는데 도움이 되었습니다.

      삭제
  6. 안녕하세요 OpenCV를 공부하고 있는 학생입니다. 다름이 아니라 코드를 보면서 공부하고 있는데 Original->Gray->Canny->Contours&Rectangles 여기에서 image를 띄운후에 bubble sort 부분에서 에러가 뜨는것 같더라구요! debug arrertion failed라고 뜨면서 include\vectorline:932에 vector subscript out of range라고 뜨네요 !! 버전이 안 맞아서 그런 것인가요??

    읽어주셔서 감사합니다!!
    그리고 정말로 코드가 도움이 많이 되요ㅠㅠ 정말 감사합니다.

    답글삭제
  7. 안녕하세요, Unknown님. 'vector subscript out of range' 오류는 저도 자주

    봅니다. 컴퓨터가 알고있는 벡터 갯수를 벗어난 영역을 물어봐서 컴퓨터가 일을

    그만둬버리는 경우입니다. 말씀하신 대로 모든 벡터에 하나하나 접근하는

    버블정렬 같은 경우에 잘 드러나요.


    예를들어 무지개로 7개 색의 이미지를 촬영해서 영상처리를 하는데, 컴퓨터는

    벡터배열 7개에 '빨 주 노 초 파 남 보'만 알고있는데, 프로그램에서 '8번째

    색깔의 RGB 성분중 Green 값은 얼마인가?' 라고 접근하게되면 뜨는 오류입니다.

    혹은 3번째 노랑색 값을 얻지못해서 NULL 상태인데 알아오라고 시킬때도

    나타납니다. 'boundRect2'의 'Size'를 확인하셔서 범위를 벗어나는지

    디버깅해보시면 좋을거같아요.


    그런데 지금보니 'refinery_count += 1'하는 연산과

    'boundRect2[refinery_count] = boundRect[i]'의 순서가 바껴있네요. -ㅅ-;

    Unknown님 덕분에 알았습니다.. Unknown님 에러도 저것 때문일 가능성이

    있어보이네요. 순서가 뒤바뀌어있어서 배열은 '0'번이 시작인데 1번부터

    채워지고 있네요. 그러면서 버블정렬에서는 0번부터 정렬하고 있습니다.

    아.. 위에 '원대연'님이 말씀해주신 문제가 이거였군요.

    답글삭제
  8. 뱀꼬리 잡는 로직에서 delta_x=abs(boundRect2[j].tl().x-boundRect2[i].tl().x) i가 몬지 모르겠어요 이쪽 뒤에 i가 툭 튀어나와서 이해가잘안대네요

    답글삭제
  9. 안녕하세요. 그 부분은 제가 지금 보아도 긴가민가할만큼 설명이 잘 안

    되어있네요. 소스코드를 펼쳐보니 이중 for문으로, i가 1씩 증가할 때마다

    j가 i 바로 다음 사각형에서 시작하고 있습니다. 버블정렬로 사각형들을

    x좌표에 따라 정렬했습니다. for문으로 i나 j값이 올라갈때마다 나오는

    사각형들은 무조건 '오른쪽'에 있겠죠? 뱀들은 오른쪽으로만 이동하면서

    자기와 기울기가 비슷한 걸 찾습니다. 즉 'i'번째 뱀이 오른쪽으로 'j'만큼

    이동해가면서 얼마나 많은 친구를 먹는지 카운트하는 구조입니다. '0번째 뱀은

    오른쪽으로 이동하면서 뱀 3마리, 7번째 뱀은 뱀 10마리를 먹었다.' 할 수

    있도록 이중 for문으로 확인하고 있어요. 물론 번호판 너비를 벗어난 'j'번째

    뱀을 만나면 너무 멀리 왔으니 break 문으로 반복을 끝내고, 다음 'i'번째

    뱀이 친구를 몇마리 먹어내는지 보는거죠. 설명이 됐으면 좋겠습니다.


    알고리즘을 보셔도 아시겠지만, 제가 영상처리 전문가가 아니라서 상당히

    원시적입니다 ㅠ 다만 OpenCV의 기본 도구랑 느낌을 전하고 싶어서 글을

    게시해 보았습니다.

    답글삭제
  10. 저 죄송한데 소스가 없어서요 ㅠㅠ

    답글삭제
  11. 안녕하세요, 익명님. 저는 방금 소스코드를 다운로드 받을 수 있었습니다.

    다운로드 링크를 클릭하시고 브라우저 상단의 '다운로드' 이모티콘을

    클릭해보세요. 읽어주셔서 감사합니다.

    답글삭제
  12. 안녕하세요
    Mat 타입은 예제를 찾기 어렵네요
    가장 큰 사각형 구해서 그리는 소스에서 코너 4개 꼭지점 4개 좌표를 구하고 싶습니다.
    도움부탁드립니다.
    #include
    #include

    using namespace cv;
    using namespace std;

    int main(int argc, char** argv)
    {
    //Mat src = imread(argv[1]);
    Mat src = imread("test2.jpg");

    int largest_area = 0;
    int largest_contour_index = 0;
    Rect bounding_rect;

    Mat thr;
    cvtColor(src, thr, COLOR_BGR2GRAY); //Convert to gray
    threshold(thr, thr, 125, 255, THRESH_BINARY); //Threshold the gray

    vector > contours; // Vector for storing contours

    findContours(thr, contours, RETR_CCOMP, CHAIN_APPROX_SIMPLE); // Find the contours in the image

    for (size_t i = 0; i< contours.size(); i++) // iterate through each contour.
    {
    double area = contourArea(contours[i]); // Find the area of contour
    //src면적이랑 area가 같으면 제외
    if (area > largest_area)
    {
    largest_area = area;
    largest_contour_index = i; //Store the index of largest contour
    bounding_rect = boundingRect(contours[i]); // Find the bounding rectangle for biggest contour
    }
    }
    //contours[largest_contour_index];//가장큰 사각형

    drawContours(src, contours, largest_contour_index, Scalar(0, 255, 0), 2); // Draw the largest contour using previously stored index.

    imshow("result", src);
    waitKey();
    return 0;
    }

    답글삭제
  13. 안녕하세요, 'victor Jo'님.

    기울기가 없는 사각형이라면 보여주신 코드로 문제 없을듯한데..

    질문하신 것을 보니 다양한 크기의 사각형이 기울어져 있는 이미지를

    말씀하신거라 생각합니다.


    그런 경우엔 'Rect' 자료형보다 유연한 'RotatedRect'가 있습니다.

    아래는 OpenCV 도큐먼트의 예제 프로그램 주소입니다. 수직, 수평이기에

    'rectangle()' 함수로 단번에 그리는 Rect와 달리, RotatedRect는

    콘투어를 감싸는 4개 꼭지점을 통해서 'line()' 함수로 모서리 4개를

    그려주는 차이점을 보실 수 있습니다.

    http://docs.opencv.org/3.0-rc1/de/d62/tutorial_bounding_rotated_ellipses.html


    가장 큰 윤곽을 고르시는건 소스코드를 보아 마련하신듯 해서

    RotatedRect를 말씀드렸습니다. 만약 찾아낸 사각형을 회전해서

    정위치시키는걸 원하신다면 OpenCV 'warpAffine()' 함수가 그 역할을

    제공해줍니다. 답변이 되셨길 바랍니다.

    답글삭제
  14. 감사합니다
    큰 도움이 됐습니다.

    답글삭제
  15. 죄송합니다

    이렇게 했는데
    RotatedRect bounding_rect2;
    bounding_rect2 = minAreaRect(Mat(contours[largest_contour_index]));
    Point2f rect_points[4]; bounding_rect2.points(rect_points);
    printf("ro1-%d,%d\n", rect_points[0].x, rect_points[0].y);
    printf("ro2-%d,%d\n", rect_points[1].x, rect_points[1].y);
    printf("ro3-%d,%d\n", rect_points[2].x, rect_points[2].y);
    printf("ro4-%d,%d\n", rect_points[3].x, rect_points[3].y);
    ---------------이런값이 나오네
    ro1--1610612736,0
    ro2--1610612736,1073741824
    ro3-0,0
    ro4--1073741824,0

    ----------------------이런값이 나와야하는데..
    0-34,467
    1-379,77
    2-413,463
    3-72,80

    답글삭제
  16. 안녕하세요.

    'Point2f' 자료형은 실수형으로 좌표를 갖고 있습니다.

    '%d' 대신 '%f' 해주시면 됩니다.

    답글삭제
    답글
    1. 답변감사합니다
      정말 큰 도움이 됐습니다
      볼수록 대단한 기능이 많더군요
      갈길이 머네요....

      삭제
    2. 안녕하세요, 'victor Jo'님.

      저도 Jo님 덕분에 블로그에 답글 기능이 있다는걸 알게 되었습니다.

      저는 '답글 삭제'가 제가 관리자기에 답글을 지워버리는 한 기능인줄

      알았는데.. 알고보니 '답글 달거나', '삭제'였네요 ㅎㅎ

      편안한 주말 보내시길 바랍니다.

      삭제
  17. 안녕하세요
    이번에 번호판 인식에 관련되서 공부를 하고 있는 학생입니다.
    글에 올려진대로 소스를 써가면서 실행해보고 있는데요
    drawContours 부분에서 계속 중단이 떠서 왜 그런지 알 수 있을까 합니다..

    답글삭제
    답글
    1. 안녕하세요.

      콘솔 프로그램으로 하셨다면 OpenCV에서 오류원인을 띄워줍니다.

      실제 어떤 오류인지 확인해봐야겠지만, 아마

      'drawContours()'에서 뜨는 오류라면.. 컴파일러가 그릴게 없는데

      그리라고 해서 파업한 경우일 확률이 큽니다. 제가 프로그래밍한

      부분에서 그 점을 야기할 수 있는 부분이 있어서, 방금 수정해서

      프로젝트를 다시 업로드했습니다. 샘플 이미지 10장으로 테스트했을 때는,

      번호판을 엉뚱한 곳에서 찾는 9번 이미지의 범위지정 오류를

      제외하고는 오류없이 동작하는 것을 확인했습니다.

      다시 다운로드해서 확인해보실 수 있나요?

      읽어주셔서 감사합니다.

      삭제
    2. 감사합니다 답글을 달고 다시한번 확인해보았는데 영상을 초기화하지 않아서 오류가 발생했던거 같습니다. 감사합니다.

      삭제
    3. 하나 더 여쭤봐도 될까요 .. bubble sort 부분을 확인하고 있는데 순서를 부여하는 부분은 오류가 발생하지 않는데 그 밑에 기울기를 구하는 수단 부분에서 vector subscript out of range 오류가 발생합니다. 벡터부분에서 문제가 있는건 알겠는데 정확히 어느부분에서 오류가 났고 수정을 어떻게 해야할지 모르겠습니다.

      삭제
    4. 'vector subscript out of range' 오류는 존재하지 않는 벡터

      인덱스를 가리킬 때, 나타납니다. 위에 '원대연'님이 말씀하신

      오류와 같다고 생각되는데 그 방법으로 적용해보는게 어떨까요.

      findContours() 함수로 처음 찾은 윤곽 벡터 데이터들은 문제없는데,

      정제하고 정렬하는 과정에서 인덱스가 더 추가된 것 같습니다.

      테스트하신 이미지 링크주시면 저도 테스트하면서 소스코드 문제점을

      알 수 있을 것 같습니다. 위에 제가 설명한 디버깅 방법을

      사용하시면 문제점이 무엇인지 바로 알 수 있습니다.

      삭제
  18. 안녕하세요 블로그 잘보았습니다. 글을 하나하나 읽었는데 발상하나하나가 초보 프로그래머인 저제가 보기엔 정말 대단하신분 같아요.
    저는 안드로이드를 개발하려고하고 있구요, 이미지에서 차량 번호판을 추출하는 프로그램을 만들어보고싶은데 문자인식까진 쉬워도 번호판을 추출하는게 여간힘든게 아니더라구요. 오랜시간 머리 짜매도 해결이 나지않아 이렇게 도움을 요청해봅니다. 안드로이드로 구현할수있는 자료를 볼 수 있는곳을 알고 계시면 알려주실 수 있을까요 부탁드립니다!

    답글삭제
    답글
    1. 안녕하세요, 'ro Ro'님.

      칭찬해주신 덕분에 새로 책 리뷰 글을 쓰고 있습니다 ㅎㅎ.

      다른 분들에게 알리고 싶어서 운영중인 블로그지만

      정말 컴퓨터, 물리학 전문가 분이 보신다면 '얘 왜 이렇게 나대지?'

      하는 수준입니다 ㅠ 벽에 가까워질 때마다 그 벽이 얼마나 높았는지

      실감하고 있습니다.


      게시글에서는 리눅스와 윈도우즈에서 범용적인 Qt 프레임워크를

      추천드렸는데, 사실 리눅스에 뿌리내린 안드로이드 플랫폼만

      보자면.. JAVA 언어로 빌드하는 '안드로이드 스튜디오'가

      가장 범용적인 개발환경입니다. 안드로이드 스튜디오에서의

      OpenCV 세팅과정은 아래 링크에 나와있네요.

      http://memostart.tistory.com/26

      OpenCV 홈페이지에서 Android용 OpenCV를 다운받는걸로 보입니다.

      삭제
  19. 안녕하세요 영상분할 관련하여 findContours 함수 에러 관련해서 블로그를 검색하다가 정보를 얻으려고 댓글 답니다. 위의 댓글들을 읽어보니
    MFC에서 findContours() 함수를 쓰면

    에러가 난다 라고 된 부분이 있는데 제가 그 문제를 해결하지 못해서 답변을 얻으려고 글을 남깁니다. 개발환경은 windows7 32bit visual studio 2013 , openCV 3.0 버전을 사용중입니다

    답글삭제
    답글
    1. 안녕하세요, '도리도리' 님.

      저는 그 문제를 직접 겪지는 않았는데, 에러를 경험하신

      경력자분들도 직접적인 해답을 찾지는 못하셨습니다.


      하지만 'OpenCV 1', 즉 C언어 구버전 OpenCV의 'cvFindContours()'

      함수를 써서 무리없이 프로젝트를 진행하셨습니다.

      'findContours()', 'cvFindContours()' 함수는 재료로 하는 자료형과

      사용법이 조금 다르지만 매개변수 등의 의미는 통합니다.

      findContours() 를 쓰는 부분만 cvFindContours() 함수로 대체해서

      이미지를 처리하고, cvFindContours()의 'CvSeq' 자료형을

      findContours()의 'vector-vector-Point'로 다시 변환하는

      방법이었습니다. 소스코드는 번거로워졌지만 프로젝트는 이 방법으로

      잘 진행되었습니다. 도움되셨길 바랍니다.

      삭제
  20. 공개해주신 포스팅은 잘 보았습니다. 문의사항이 있어서 글을 남깁니다.

    일반적인 안드로이드 갤럭시에서 도로 영상에서 찾는다면 차량번호를 검출한다면 물론 이미지 복잡도에 틀리겠지만, 그냥 일반 도로라면 시간이 어느 정도 소요될까요?

    답글삭제
    답글
    1. 감사합니다, '개발자'님.

      우선 의미있는 프로젝트하시는데, 제가 전문적인 경험이 없음을 먼저 밝혀두고 싶습니다.

      그럼에도 경험상 도움을 드리고 싶어 몇가지 변수를 정리해보았습니다.


      가장 큰 변수는 잘라낸 이미지에서 OCR 처리하는 시간일 것입니다.

      인텔 i-시리즈 정도 CPU를 가진 PC에서 처리한다면, 특징점을 찾아내어 번호판 영역을

      떼어내는 것은 검사분기를 아무리 늘려도 0.5초도 넘기 어렵다고 봅니다.

      프로젝트를 할 때도 가장 큰 문제는 떼어낸 번호판 영역에서 OCR 처리하는 시간이었습니다.

      위에 소개드린 구글 OCR은 작년 사용할 때 릴리즈 모드에서 2초 가까이 소모했던 기억이

      있습니다. 오픈소스라 끌어다쓰기에는 편하지만, 저희가 직접 만든게 아니기에 불필요한

      과정이 있을 수 있습니다. 예로 한국 번호판에서는 알파벳이 없는데 알파벳이 선택기준에

      포함 되고, 그렇다고 한글 버전을 선택하면 기계학습이 덜 된 신경망이기에 검출률이

      낮습니다. 인텔 PC에서도 그러하기에 말씀하신 갤럭시에서의 속도 문제가 염려됩니다.


      다음으로 이미지를 한번만 찍어 처리한다면, 번호판 영역을 못잡거나 숫자 '7'을 '9'로

      읽어들일 수 있습니다. 그래서 2~3차례 이미지를 획득해 문자 데이터를 비교해봐야할텐데,

      수차례 이미지를 획득하는 것은 한 번 처리의 배수나 되는 처리시간을 요구합니다. 여기서

      위에 말씀드린 OCR의 정확도, 그리고 이미지 자체의 사이즈가 1024x768 정도면 괜찮은데

      1920x1080 정도라면 갤럭시 CPU에는 부담스런 작업이 될 거라 생각합니다.


      그래서 구글 OCR을 배제하시고 자체적으로 잘라낸 사각형 영역 안의 문자가 '7'인지

      '9'인지 판단하는 알고리즘을 직접 고안하신다면 힘드시더라도 OCR 시간문제에 도움이

      되리라 보고, 이미지 사이즈는 촬영 거리에 따라 가장 낮은 단계로 하시는게 어떨까

      싶습니다. 감만으로는 시간을 말씀드리면 안 될 것 같아서 의견만 드리는 점

      양해 바랍니다.

      삭제
    2. 좋은 말씀 감사합니다. 많은 도움이 되었습니다. :)

      삭제
  21. 안녕하세요 소스를 보고 감탄한 학생입니다.
    openCV에 대해서는 아무것도 모르는데...
    만약 저렇게 이미지를 추출하면 저 이미지를 또 숫자 즉 Text로 추출이 가능한가요?
    가능하다면 어떤 방법이 있을까요?

    답글삭제
    답글
    1. 안녕하세요.

      게시글에서 말씀만 드린 'Tesseract-OCR'이 그 중 한 방법입니다.

      ' http://www.riss.kr/ '과 같은 국내논문 사이트에서 논문으로 소개하는

      자동차번호판 인식 알고리즘을 이해하고 프로그래밍으로 구현하는 방법도 있습니다.


      문자 사각형마다 특징점을 직접 찾아내는 알고리즘을 고안하는 것이

      정확도와 속도 모두에서 우수하다고 들었습니다.

      저는 논문을 따라가기가 쉽지 않았는데, 시간적 여유가 충분하다면

      여러 시행착오를 정리한 자동차 번호판 추출 관련 논문을 읽는게

      결과물 완성도에 가장 좋다고 봅니다.

      (교수님께서 우수한 논문 중에 'perfect matrix' 방법을 소개해주신게 기억나네요.)

      삭제
  22. 안녕하세요.
    영상처리를 공부하고있는 학생입니다.
    개발자님이 주신코드와 설명대로 라즈베리파이3에서 실행을 해보았습니다.
    다행히 다른 부분에서는 오류없이 진행되었지만 관리자님이 올려주신 사진외의 제가 찍은 사진에서는 번호판 추출이 안되는데 이건 뭐가문제인지 설명해주실수있나요?

    답글삭제
    답글
    1. 안녕하세요, 익명님.

      촬영환경 변화에 따른 2가지 차이점이 추측됩니다.


      첫째로 자동차 번호판 숫자에 대한 크기 제한입니다. 소스코드에서 보시다시피

      번호판 영역 숫자에 대한 가로/세로 비율, 사이즈에 대한 제한을 두었습니다.

      질문자님이 촬영한 이미지에서는 필터링되는 조건일 수 있습니다.


      둘째로 자동차 정면 이미지가 아닌 측면에서 찍은 영상을 사용하셨나요?

      위 소스코드에는 기울어진 번호판(평행사변형)에 대한 보정이 포함되어 있지 않습니다.


      무엇보다 질문자님께서 waitKey(0) 함수를 사용해 의심점마다 중간 결과물을

      확인하시는게 정확합니다. 아이디어만 담긴 번호판 추출 알고리즘을 살펴보시면

      거기에 어떤 보완점이나 안전장치가 빠졌는 지도 떠오르실 겁니다.

      삭제
  23. 안녕하세요
    실행을 해보려고하는데
    Debug error R6010()에러가 났습니다.
    어떻게하면 좋을지 조언같은거 있으시면 부탁드립니다 ㅠ

    답글삭제
    답글
    1. 안녕하세요~ 답변이 늦어 죄송합니다.

      http://kanais2.tistory.com/41

      위의 'Kanasis'님의 블로그에서 보여지는 것처럼,

      해당 'Mat' 데이터타입을 벗어난 픽셀에 접근할 때 나타나는 에러입니다.

      저 에러를 방지하려면 위 샘플 프로젝트에서 영역을 벗어난 번호판 영역,

      즉 번호판을 못 찾은 경우이겠죠? 그 변두리 영역을 캡쳐하려할 때

      '번호판은 중앙에 있으니 변두리에 배부른 뱀이 포착되면 이건 실패다'라고

      예외처리해야합니다. 저는 프로젝트에서 그런 예외처리를 하지 않았습니다 ㅠ

      제가 인터넷에서 프로젝트를 찾아다닐 때 느낀건데, 완변한 샘플보다

      엉성하더라도 내가 원하는 딱 그 기능만 설명해주는 게시물이 더 필요하더군요.

      어떻게 말씀드려도 변명이지만.. 알맹이만 전달하려고 했습니다. ^-^.

      삭제
  24. in cv::cvarrToMat이라는 에러가 나는데 어떤 설치가 안되서 그런건가요??

    답글삭제
    답글
    1. 안녕하세요.

      정확한 오류 타입을 확인해야할 것 같은데요.

      비주얼 스튜디오의 추가 디렉토리, 추가 라이브러리에

      소스코드와 '.lib' 파일 경로 설정이 올바른지

      확인하시면 좋을 것 같습니다.

      삭제
  25. 힘들게 올리신만큼 꼼꼼히 보겠습니다.

    답글삭제
    답글
    1. 안녕하세요.

      소개드린 방법은 견고하지는 못하지만

      윤곽이 잡히는 참고라도 되었으면 좋겠습니다.

      삭제
  26. 안녕하세요 저도 차량 번호판 인식 프로그램을 제작중인데
    참고할만한 좋은 제공에 일단 감사에인사드립니다!
    다름이 아니라 canny까지는 별탈없이 진행되었는데
    findcontours 진행함에 있어서 오류가 발생하네요 ㅠㅠ
    cv::Rect_::tl ,cv::Rect_::br 함수 호출에 인수 목록이 없다고 뜨네요 ㅠㅠ
    &cv::Rect_::tl ,&cv::Rect_::tl를 사용하여 멤버에 대한 포인터를 만들라고뜨네요
    무슨 문제일까요?ㅠㅠ

    답글삭제
    답글
    1. 안녕하세요.

      우선 엉성한 코드지만 참고라도 되셨으면 하는 바람입니다.

      부분적으로 바인딩이 안 된 문제라면,

      말씀하신 'cv::Rect_'는 'opencv2\core' 디렉토리 안에

      'types.hpp'라는 헤더파일 안에 들어있습니다.


      http://docs.opencv.org/3.1.0/d2/d44/classcv_1_1Rect__.html

      위 레퍼런스 소개글에서 요구하는 형태의 매개변수 타입으로

      넣으셨는지 확인하시면 좋을 것 같습니다.

      삭제
  27. 안녕하세요. 좋은 강좌 우선 감사드립니다. 저도 한번 실행해보려는데, 역시 문제가 생겨서요. findContours 후에, approxPolyDP 에서 오류가 나네요. 디버깅해 본 결과로는, 벡터의 사이즈가 0이거나, 아주큰값(이상한값들)이 있을경우에 문제가 되는거 같습니다.
    예외처리를 해야하나요? 해야한다면 어느부분에서 어떤방법으로 가능한지 궁금합니다. 답변 부탁드립니다.ㅠ

    답글삭제
    답글
    1. 안녕하세요.

      우선 읽어주셔서 감사합니다.

      그 부분은 openCV 공식 레퍼런스에서 그대로 가져운 부분이었습니다.

      http://docs.opencv.org/2.4/doc/tutorials/imgproc/shapedescriptors/bounding_rects_circles/bounding_rects_circles.html


      'approxPolyDP()' 함수는 다각형의 곡률을 특정한 정확도로 근사하는 함수네요.

      http://docs.opencv.org/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html#approxpolydp

      위의 레퍼런스를 보니 3번째 매개변수 'epsilon'이 그 정확도를 결정하는 변수라고 합니다.

      원본과 근사치의 최대 거리라고 하네요.

      저는 튜토리얼에서 그대로 가져와 1로 주었는데요,


      http://stackoverflow.com/questions/17716255/whats-wrong-with-this-opencv-code

      위의 질답을 보니 개개 윤곽마다 유동적으로 0.02를 곱해서 주는 것도 가능하군요.

      정확도를 한번 변경해보시고, 혹시 제가 첨부한 이미지 그대로를 사용하셨다면

      openCV를 상위 버전으로 옮겨보시는 것도 좋을 것 같습니다.

      삭제
    2. 답변 정말 감사드립니다. 일단 함수가 사용하는 정확도나 그런것과는 무관한것 같습니다. contours 벡터는 벡터를 원소로 하는 벡터인데, 반복문을 돌면서, approxPolyDP 함수로 들어가면 죽어버리네요. 이건 함수의 결과가 제대로 나오느냐와는 무관하게 함수에 들어가는 파라미터가 잘못되었다고 나옵니다. 에러는 R6010입니다. 이 에러를 찾아보았으나, 이 경우에 해당되는 뽀족한 답이 될만한건 못찾겠더군요. contours 벡터를 보면, 그안에 벡터들이 많이 있는데 사이즈가 0인 경우가 죽고, 사이즈가 0인경우를 필터링시켰더니, 사이즈가 4290947393 이런크기의 벡터들도 있더군요. 이경우 안의 x,y값들이 음수인경우가 대부분입니다. 이역시 죽습니다.

      그래서 findContours 함수가 혹시 잘못된 결과를 가져오는게 아닌가 싶었는데, 같은 이미지로 테스트해봤을때, OpenCV 공식홈페이지에 있는 예제에서는 또 문제가 안생기더라구요. 이부분만 통과하면, 올려주신 원본의 내용을 거의 그대로 확인해볼수 있을것 같은데 ㅠ 어쨋든 답변 정말 감사드립니다. 조금 오래된 게시물이라서 답변을 안해주실거라 생각했거든요. 이 부분에서 오류가 난다하더라도 지금도 상당히 많은 도움이 되었습니다.

      삭제
  28. 잘보고 있습니다. 혹시 번호판 인식부분을 공부하고 교통표지판을 인식하는 코드로 바꿔서 작성해도 될런지요.. 된다면 도전 해보게요.. 조언 부탁드립니다.

    답글삭제
    답글
    1. 안녕하세요.

      활용해주신다면 오히려 제가 감사드립니다ㅎ 자율주행 프로젝트를 구상하고 계신가요?

      기하학적인 기호 인식은 익명님만의 전략으로 준비하시더라도 속도제한 표지판 등의

      정갈한 숫자는 기본 OCR 적용으로도 충분히 써먹으실 수 있을 것 같습니다.

      추가로 기울기에 대한 전처리 보정이 필요할 거 같네요. 그런데 차량 번호판처럼

      교통표지판도 비율이 정해진 꼭지점을 가지기에, 원본 이미지에서 표지판을

      찾아낸다면 (이거 어렵겠네요. 원형, 삼각형, 팔각형 등 모양이 다양하네요..)

      정면에서 보듯 변환하는 것은 고군분투하지 않아도 될 듯 합니다.


      OpenCV는 BSD 라이센스로, 소스코드 공개의무가 없고 상업적 사용에 대해서도

      자유롭습니다. 물론 OpenCV에서 발생하는 문제에 대해 그들이 책임지지도 않습니다.

      익명님의 프로젝트가 잘되면 언젠가 저도 그 편리를 즐길 날이 오겠죠.

      삭제
  29. 안녕하세요
    opencv 자동차 번호판 검출 예제를 살펴보던 중에 공부할 수 있는 자료를 볼 수 있어서 정말 감사드립니다.
    다름이 아니라 작성자님 코드로 연습하던 중에
    findContours()에서 carLicensePlate_1.exe has triggered a breakpoint. 문제가 발생하는데 hierarchy size가 262151712가 문제가 되는 거 같습니다.
    opencv3.0.0 / visual studio 2013을 쓰고 있는데 문제 해결 조언을 구할 수 있을까요? ㅠ

    답글삭제
    답글
    1. 안녕하세요, 익명님.

      위의 'Junseok Oh'님께서 말씀하신 것과 비슷한 문제인 것 같습니다.

      저도 거기에 대해 고민해봤지만 실마리를 잡을 수 없었습니다.

      익명님이 별도의 이미지로 테스트하셨다면 링크나 저의 메일( mind3002@gmail.com )로

      이미지 파일을 보내주실 수 있으신가요?


      논리적으로는 거기서 터질 이유를 감잡을 수 없어서, 우선은 다른 버젼의

      OpenCV로 실행해보시는 동안 저는 그 오류가 뜬 상황에서 분석해보고 싶네요.

      삭제
  30. 안녕하세요 우선 좋은 자료 정말 감사드립니다

    vs2015에서 findContours()부분이 계속 오류나길래 cvFindContours()로 바꿨는데 문제는 CvSeq를 어떻게 vector-vector-point로 convert하나요??

    답글삭제
    답글
    1. 안녕하세요~

      'cvGetSeqElem()' 함수로 가능합니다.


      for (int i = 0; i < 10; i++)
      cv::Point *pt = (cv::Point*)cvGetSeqElem(cvseq_contour, i);


      여기서 'cvseq_contour'가 'cvSeq *' 자료형입니다.

      삭제
    2. 답변 감사드립니다

      그런데 죄송하지만 제가 벡터 구조를 잘 몰라서 그러는데 Point*를 어떻게 vector-vector-point 구조에 대입하나요??

      삭제
    3. 삽입은 vector 컨테이너의 push_back() 함수를 쓸 수 있겠네요.

      vector는 OpenCV 뿐만이 아닌 C++ 자체의 STL입니다.

      우선 vector에 대해 살펴보시는 것도 좋겠습니다. ㅎ

      vector를 쓰시는게 익숙하지 않으시다면 배열을 그때그때 동적할당해서

      사용하는 것도 가능합니다. vector는 크기가 가변적이라서 편하죠.

      vector의 기본 개념은 이 게시물의 4번째 댓글에서 말씀드렸습니다.

      우선은 cvGetSeqElem() 함수로 추출해서 push_back() 함수로 집어넣을

      수 있음을 답변드립니다.

      삭제
  31. 안녕하세요. 좋은 자료 올려주셔서 감사드립니다.

    이번에 번호판 인식이라는 영상처리를 이용해서 다른 프로그램들이랑 연결해서 작품을 하나 만들라고 공부중인 학생입니다.

    그런데 제가 영상처리라든가 Opencv는 한번도 써보지 못했는데요. 이번기회에 공부를 해서 할라고 하니 많이 힘들더군요.

    우선 프로그램 설치하거나 연동 하는데서 많이 막히는거 같습니다.
    라즈베리파이를 이용해서 영상처리를 하고 싶은데 qt가 리눅스 상에서는 안깔리는거 같더군요<-제가 못하는거 같은느낌.. 그래서 window 상에서라도 프로그램이 돌아가는지 확인 하기 위해서 qt를 컴퓨터 window에 설치 후 코딩을 돌려봤는데

    -------------------------------------------------------------------
    16:07:10: Running steps for project license_plate...
    16:07:10: Starting: "C:\Qt\5.9\mingw53_32\bin\qmake.exe" C:\Users\user\Desktop\license_plate_160729\license_plate\license_plate.pro -spec win32-g++ "CONFIG+=debug" "CONFIG+=qml_debug"
    Cannot find file: C:\Users\user\Desktop\license_plate_160729\license_plate\license_plate.pro.
    16:07:10: The process "C:\Qt\5.9\mingw53_32\bin\qmake.exe" exited with code 2.
    Error while building/deploying project license_plate (kit: Desktop Qt 5.9.0 MinGW 32bit)
    When executing step "qmake"
    16:07:10: Elapsed time: 00:00.
    ---------------------------------------------------------------------

    이런식의 에러가 뜨더라구요 그럼 qmkae를 qt를 window에 깔고 opencv이가 없어서 라이브러리가 없다고 뜨는거 같은데 작성자님께서 주신 오픈코드를 라즈베리에서 사용할하면 어떻게 깔아야되나요 ? 인터넷에서 공유되는것들 왠만해서는 다해봤는데 어떻게 하는건지 잘모르겠어서 질문드리니다. 괜찮으시면 따로 이메일로 라도 연락 드릴수 있을까요 ?

    답글삭제
    답글
    1. 안녕하세요.

      저도 라즈베리에서는 Qt를 안 쓰고 터미널 창에서 G++ 컴파일러로 빌드했습니다.

      올린 소스코드는 Qt에서 작업했지만 보시다시피 Qt 패키지를 사용하지 않았습니다.

      PC 우분투에서 Qt 프레임워크를 쓴 이유는 단지 같은 리눅스기에 개발환경 준비의

      유사한 점을 누리기 위해서였습니다. 일단 Qt라서 편하고 OpenCV, TesseractOCR 등의

      설치과정이 비슷하기 때문이죠. 그리곤 작성한 소스코드를 USB에 담아 라즈베리에

      옮겨서 거기서 G++ 명령어로 빌드한 것 뿐입니다. 작성한 소스코드가 길지 않아서

      이렇게 했는데요, 라즈베리파이에서 Qt를 바로 쓰지 못하는 것은 CPU 아키텍처의 명령어

      차이때문입니다. 라즈베리에서 Qt를 사용하기 위해 찾아보신 방법들은 아마

      크로스 컴파일링을 위한 방법일 겁니다. PC 우분투, 즉 인텔 x86/x64 아키텍처에서

      Qt를 돌려 빌드한 실행 프로그램은 ARM 아키텍처의 라즈베리에서 돌릴 수 없습니다.

      크로스 컴파일링은 PC에서 Qt 프레임워크로 '편하게' 소스코드만 작성하고,

      빌드하는 그 순간만 x86/x64 아키텍처가 아닌 ARM 아키텍처를 위해 '빠르게' 빌드하는

      겁니다. (라즈베리보다 PC에서 빌드하는게 훨씬 빠르겠죠?)

      이렇게해서 생성된 프로그램은 정작 빌드한 PC 우분투에서는 사용못해도

      라즈베리로 옮기면 '빌드없이' 바로 실행할 수 있는 것이죠. 성능이 낮고

      명령어 아키텍처가 다른 임베디드 기기를 타겟으로 할때 이렇게 크로스 컴파일링이

      쓰입니다. 저는 크로스 컴파일링이 아닌 단순하게 소스코드만 옮겨서 라즈베리에서 다시

      빌드한 것이죠 ㅎㅎ.


      이제 g++ 컴파일러 빌드에 대해 말씀드리고자 하는데..

      우선 질문자 분께서 기회가 있으시다면 리눅스 프로그래밍 강의를 들으셨으면 좋겠네요.

      저는 리눅스 프로그래밍 강의를 듣기 전까지는 인텔 갈리레오 보드를 사놓고도 반년 가까이

      서랍에 박아뒀습니다. 단지 그 임베디드보드를 쓸 생각만 했고 리눅스를 배울 생각은

      안 했었거든요 ㅎㅎ. 운전면허 없이 차부터 사버린 거죠.. 리눅스를 배우면

      비주얼 스튜디오, Qt와 같은 프레임워크들의 편리함에 가려진 컴퓨터의 신경과 혈관을

      볼 수 있습니다. 혹시 학교 강의에서 듣기 어렵다면 KOCW, OLC 와 같은 사이트에서

      동영상 강의를 접하실 수 있습니다. 저또한 리눅스를 배우지 않고 리눅스에서 돌아가는

      프로그램을 만들기에는 다른 사람의 설명을 들어도 이해하기 어려움을 느꼈었습니다.


      중요한 것을 말씀드리고자 글이 너무 길어졌네요 ㅎㅎ. 우선 구글에서 OpenCV

      설치과정을 검색해서 우분투에 OpenCV 라이브러리를 설치하셔야합니다. 위 게시글에서

      OpenCV 설치과정은 생략했습니다. 그런 다음 Qt 프레임워크는 필수적이지는 않지만

      PC에서 편리하게 개발하기 위해 설치할 수 있구요, Qt나 이클립스 등의 프레임워크가

      없다면 PC에서도 라즈베리처럼 터미널에서 G++ 명령어로 빌드합니다.

      이렇게 PC에서 테스트한 소스코드를 USB에 담아 라즈베리파이에 옮긴다음

      터미널창을 띄웁니다. 가져온 소스코드가 있는 창으로 디렉터리를 잡고

      g++ `pkg-config --cflags --libs opencv` main.cpp -o main

      이렇게 입력해서 라즈베리에서 실행되는 프로그램을 생성할 수 있습니다.

      설명은 이렇게 드리지만, 작성자 분이 리눅스 강의를 먼저 들으시고,

      급하지 않으시다면 크로스 컴파일링 과정도 익혀보시는게 꽃길임을

      다시 한번 말씀드립니다 ^-^.

      삭제
  32. 웹캠이나 동영상에 있는 번호판을 인식하고 싶은데 어떻게 수정하면 좋을까요

    답글삭제
    답글
    1. 안녕하세요~

      OpenCV는 웹캠 카메나라, 동영상으로부터 이미지를 입력받는 함수가 잘 마련되어 있습니다.

      아래 게시물 링크에 카메라, 동영상 파일로부터 캡쳐하는 방법이 담겨있네요.

      http://opencv-srf.blogspot.kr/2011/09/capturing-images-videos.html


      웹캠을 넘어 산업용 고해상도 카메라의 경우, 해당 업체에서

      비싼 자사 카메라에 맞는 라이브러리 소스코드도 함께 제공합니다.

      그런 경우는 해당업체의 이미지 파일을 OpenCV 'Mat' 자료형으로 변환하는 방법이

      인터넷에 잘 정리되어 있습니다.

      삭제
  33. 우연히 읽게 되었는데, 글이 매우 이해하기 내용이 충실해서 저에게 많은 도움이 되었습니다.
    감사합니다.

    답글삭제
    답글
    1. 읽어주셔서 감사드립니다, jilossoju님.

      저는 전문가분들의 안목을 가지지 못했기에, 읽어주시는 분들이

      소화시키기 수월한 글을 쓰는데 최대한 초점을 집중하고 있습니다 ㅎㅎ.


      10여개의 게시물을 올리고 자원이 고갈되어버린 저 스스로의 능력이 아쉽습니다.

      지금 공부하는 내용이 다른사람에게 의미있는 덩어리가 된다면 또다른 테마로

      찾아뵙고 싶네요.

      삭제
  34. 안녕하세요 소스 잘보았습니다! 분석하며 공부중인데요 초보라 모르는부분이 많아서 댓글 남기게 되엇습니다ㅜ
    Findcontours 함수의 매개변수들의 의미가 궁금합니다 ㅜ 메뉴얼에서 봣는데 영어라 감이 안잡히더라구요..

    또 vetor>contours_poly 부분이랑 vector부분이 boundrect이라는 배열을 통해 사각형에 접근하는 소스인가요ㅜㅜ? 답변 부탁드립니다 감사합니당

    답글삭제
    답글
    1. 안녕하세요, 답변이 늦었네요.

      vector는 C++ STL에 내장된 가변적인 배열이라고 보시면 되겠습니다.

      findContours() 함수에 윤곽추출이 가능한 이미지(8비트 싱글채널, 그레이)를 던져주고

      그 아웃풋인 contours에 윤곽들을 담아내는 거죠.

      contours는 vector-vector-Point 타입인데요, 점을 벡터 집합으로 묶으니

      하나의 윤곽이 되겠죠? 근데 여기 또 벡터를 씌웠으니 윤곽들을 관리하는

      데이터가 되었습니다. 이것을 for문 안에서 같은 타입인 contours_poly에

      필요한 정밀도로 근사시켜서 옮기고, 동시에 boundingRect()를 통해

      contours_poly의 윤곽에서 하나씩 사각형을 만들 데이터를 뽑아내는 것이죠.


      findContours() 함수의 의미있는 매개변수라면..

      3번째 'mode'와 4번째 'method'가 있겠네요.

      모드는 윤곽을 담는 구조를 결정하는데, 윤곽이 겹칠때 바깥의 윤곽만 선택하기와

      같은 옵션(CV_RETR_EXTERNAL)을 줄 수 있구요.

      메서드는, 윤곽이 점으로 이루어져 있잖아요?

      윤곽을 저장할 때 모든 점의 좌표 데이터를 가지고 있을 건지 (CV_CHAIN_APPROX_NONE)

      점의 흐름이 꺾이는 부분만 근사해서 가지고 있을 건지 (CV_CHAIN_APPROX_SIMPLE)

      등을 결정하는 듯 보입니다.

      삭제
  35. 안녕하세요.. 최근 opencv에 관심을 갖고 공부하던 중 님의 게시물에 많은 도움을 얻었습니다.
    헌데 다른분들과 마찬가지로 findContours() 에서 에러가 발생하네요. 여러 댓글을 참조하여 cvFindContours()으로 바꿔보려하는데 어떤식으로 해야하는지 감이 잡히질 않습니다. 올려주신 소스를 기준으로 어떻게 바꿔야하는지 도움좀 부탁드립니다.ㅠㅠ

    답글삭제
    답글
    1. 안녕하세요.

      흐릿한 기억에 findContours() 함수가 적용 안 되었던 문제는 MFC에서 일어났었습니다.

      익명님도 현재 MFC에서 findContours() 함수를 적용중이신가요?

      다루는 변수가 다르지만 두 윤곽찾기 함수의 기능은 같습니다.

      cvFindContours() 함수를 예제와 함께 깔끔하게 정리하신 분의 블로그 주소는

      http://lueseypid.tistory.com/archive/20130113 입니다.

      이 외에도 cvFindContours() 함수를 설명하는 블로그는 많네요.

      위 소스코드의 최종적인 처리에는 윤곽을 감싸는 사각형이 쓰였습니다.

      vector-vector-Point 타입을 Rect로 바꿔서 기울기 연산에 활용한 것을

      CvSeq* 타입을 CvRect로 바꿔보시면 기능적으로 같다고 봅니다.


      CvSeq* seq;
      vector boxes;
      CvRect boundbox;

      /* cvFindContours() 이후 */

      for(; seq; seq = seq->h_next) {
      boundbox = cvBoundingRect(seq);
      boxes.push_back(boundbox);
      }

      이런 식인 것 같습니다.

      삭제
  36. 안녕하세요. 작성하신 글 잘 봤습니다.
    혹시 사진의 각도가 살짝 측면이여도 번호판 따오기가 되는지 궁금합니다.
    샘플 사진이 있는데 한번 봐주실수 있나요? 이메일 알려주시면 보내겠습니다.

    답글삭제
    답글
    1. 안녕하세요, hyunjin kim 님.

      소개드린 소스코드에는 회전보정이 포함되어 있지 않습니다.

      기울기 보정 자체는 OpenCV warpaffine() 함수로 간단히 구현됩니다.

      문제는 얼마만큼 기울어져 있는지, 그 '기울기 각도'를

      얼마만큼 정밀하게 구할 수 있는지에 달려있습니다.

      warfaffine()함수는 우리가 전해준 기울기만큼 보정시키는 도구일 뿐인데요..

      번호판 사각형이 기울어져 있을 때 상하모서리나 좌우모서리에서 선을 따서

      기울기를 알아내는 방법 등이 있습니다.

      측면에서의 관측 보정에는 warpperspective() 함수가 더 적합해보이네요.

      삭제
  37. 올리신 자동차 번호판 검출에 대해서 질문드릴게있어서 이렇게 드립니다.
    우선 덕분에 그나마 진행이 많이 되었습니다. 현재 opencv를 이용해 졸업작품을 진행중인 학생입니다.
    그런데 궁금한점이 버블정렬전단계까지는 안되는부분이 있었지만 고치고 또 고쳐서 성공했습니다. 그래서 파란색 사각형을 잡는것까지 성공했습니다.
    그런데 이제부터가 문제입니다. 버블정렬 다음부터 안됩니다.
    우선 버블정렬을 시작하는 for문과 그다음에 있는 for문이 있는데, 첫번째 for문에는 i가 조건문에서 선언이 되있는 상태인데, 그다음에 있는 for문에는 값으로 들어가있어서 i가 선언이 안되있다는 에러가 우선 뜹니다. 그래서 main에 선언을 해보기도 했지만 그것도 안됬고, 혹시나 두번째 문제가 있는 for문을 버블정렬 for문에 넣어보기도 했지만 그것도 디버그에러가 떴습니다. 어떤문제인지 시원하게 알려주세요ㅠㅠㅠㅠㅠㅠ

    답글삭제
    답글
    1. 안녕하세요. 답변이 너무 늦어 이미 답을 찾으셨겠네요..

      for문에서 i에 대한 오류는 질문자님이 분기문과 반복문에서 변수의 변화흐름을

      천천히 따라가보시면 해결될 것입니다. 무언가 놓치는게 없고

      개발툴이 알려준 오류 메시지를 모두 처리했는데도 반복된다면

      만에 하나, 개발 프레임워크 문제일 수도 있겠습니다.


      디버깅 방법에 대해 잠시 인터넷을 살펴보는 것이 제일 좋겠습니다.

      문제가 생기는 영역에 빨간 점으로 디버깅 구간을 잡고

      F5를 눌러 디버깅 모드로 실행하시면 그 구간에서 변수의 흐름을 읽으실 수 있습니다.

      삭제
  38. vecter out of range 라는 에러가 발생합니다. 위에서 저와 똑같이 버블정렬에서 에러가발생하는거같은데 그에 대해서 boundRect2의 size를 확인하라고 하셧는데 그부분에서 어떻게 해야하는지 좀 더 자세히 설명해주실수는 없나요ㅠㅠㅠㅠsize확인하는 부분부터ㅠㅠㅠㅠ

    답글삭제
    답글
    1. 안녕하세요.

      버블정렬에서 제가 놓치고 있는 부분이 있는 것 같습니다. 고쳤다고 생각했는데..

      비주얼 스튜디오를 사용중이신가요? 디버깅 방법에 대해 잠시 인터넷을 살펴보는 것이

      제일 좋겠습니다. 문제가 생기는 영역에 빨간 점으로 디버깅 구간을 잡고, F5를 눌러

      디버깅 모드로 실행하시면 그 구간에서 변수의 변화흐름을 읽을 수 있습니다.

      이 엉성한 게시물보다 구글에서 디버깅 방법을 검색해 체험해두시는게

      훨씬 영양가 높으리라 확신합니다 ㅠㅠ..

      삭제
  39. //Bubble Sort accordance with X-coordinate.단계 까지는 무리위에 질문들을 보면서 수정 하였습니다.//Shake moves to right, for eating his friend. 부분과 // FInd the most full snake.부분 에서는 i가 선언되지 않은 변수라고뜨는데 i를 어떻게 설정 해야 하나요? 아니면 Bubble부분 에서 for문의 범주를 바꿔 줘야하나요?

    답글삭제
  40. //Bubble Sort accordance with X-coordinate.
    for (int i = 0; i < boundRect2.size();i++)
    {
    for (int j = 0;j < (boundRect2.size() - i - 1);j++)
    {
    if (boundRect2[j].tl().x > boundRect2[j + 1].tl().x)
    {
    temp_rect = boundRect2[j];
    boundRect2[j] = boundRect2[j + 1];
    boundRect2[j + 1] = temp_rect;
    }
    }

    }
    count = 0;
    //Shake moves to right, for eating his friend.

    for (int j = i + 1;j < boundRect2.size();j++)
    {
    delta_x = abs(boundRect2[j].tl().x - boundRect2[i].tl().x);

    if (delta_x > 200)
    break;

    delta_y = abs(boundRect2[j].tl().y - boundRect2[i].tl().y);
    //If delta length is 0, it causes a divide-by-zero error.
    if (delta_x == 0)
    {
    delta_x = 1;
    }
    if (delta_y == 0)
    {
    delta_y = 1;
    }

    gradient = delta_y / delta_x;
    cout << gradient << endl;

    if (gradient < 0.25)
    {
    count += 1;
    }
    }


    // FInd the most full snake.
    if (count > friend_count)
    {
    select = i;
    friend_count = count;
    rectangle(image3, boundRect2[select].tl(), boundRect2[select].br(), Scalar(255, 0, 0), 1, 8, 0);
    plate_width = delta_x;
    }


    //Drawing most full snake friend on the image.
    rectangle(image3, boundRect2[select].tl(), boundRect2[select].br(), Scalar(0, 0, 255), 2, 8, 0);
    line(image3, boundRect2[select].tl(), Point(boundRect2[select].tl().x + plate_width, boundRect2[select].tl().y), Scalar(0, 0, 255), 1, 8, 0);

    imshow("rectangles on original", image3);

    waitKey(0);


    Bubble 부분 부터 짠 코딩내역 입니다.

    답글삭제
    답글
    1. 안녕하세요, 임상현님.

      말씀하신 'i'는

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

      로 시작해서 정제된 전체 뱀들을 하나씩 평가하는 변수입니다.

      배부른 뱀 찾기도 이 반복문 안에서 진행되는데요,

      그런데 이 분기 안에서 i를 못 찾는다고 한다면 컴파일러가 for문 시작변수를

      다른 반복문 안에서 인지 못하는 상황이네요.

      main() 함수 영역의 변수로 선언해 보실래요?

      같은 언어, 다른 컴파일러 사이의 이질감이 이정도까지였는지..

      삭제
  41. 소스코드에 대한 집착에서 구해주셔서 감사합니다.

    들러주신 분들은 간단한 OpenCV 예제와 배부른뱀 알고리즘을 보기위한 것인데,

    말씀해주신 STL sort() 함수를 쓰면 되었는데 왜 현명한 답을 못 찾았던 걸까요.

    한걸음 물러나서 문제를 보게 해주셔서 감사드립니다 ^-^.

    답글삭제
  42. 안녕하세요 공부를 하다가 findContour 에러가 발생하여 검색을 하다보니
    seq 형태로 바꾸면 된다길래 시도했더니 에러가 없이 진행은 되었습니다
    근데 seq 형태를 vector 형태로 바꾸고 싶은데 이부분은 어떻게 해야되나요??

    vector ret;
    vector > contours;

    CvMemStorage* storage = cvCreateMemStorage(0);
    CvSeq* contour = 0;
    CvMat arr = img;
    cvFindContours(&arr, storage, &contour, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    //cv::findContours(img, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
    //int numContours = contours.size();
    //for (int i = 0; i<numContours; i++)
    //{
    // int count = contours[i].size();
    // for (int j = 0; j<count; j++)
    // {
    // ret.push_back(contours[i][j]);
    // }
    //}

    return ret;

    주석 부분은 원래 사용해서 findcontour 부분에 에러가 났던 코드입니다
    도움주시면 감사하겠습니다

    답글삭제
    답글
    1. 안녕하세요.

      댓글 중에 비슷한 질문을 찾았습니다.

      현재 페이지에서 'cvGetSeqElem'을 검색해보시겠어요?

      해당 함수를 사용해 Seq 자료형을 Point 자료형으로 추출 후

      push_back() 함수 등으로 vector-vector-Point 타입에 삽입할 수 있습니다.

      삭제
  43. 좋은글 감사합니다ㅠ

    답글삭제
    답글
    1. 안녕하세요.

      정말 재밌는 컴퓨터, 함께 즐겨요.

      삭제
  44. 잘 읽었습니다. 좋은 포스팅 올려주셔서 감사합니다.

    답글삭제
    답글
    1. 안녕하세요.

      의미있는 시간을 드려서 저도 즐겁습니다.

      삭제
  45. 안녕하세요.
    opencv를 이용해 차량번호인식하는 걸 공부하려고 하는데, 혹시 책 추천좀 가능하실까요?

    답글삭제
  46. 혹시 라즈베리파이 에서도 작동이 가능할까요??

    답글삭제
  47. 컴맹이지만 이프로그램은 갓고싶다

    답글삭제
  48. 혹시 파일좀 받을 수 있을까요

    답글삭제
  49. 안녕하세요
    글 보면서 opencv 에 대해서 공부하고 있습니다 감사합니다
    질문이 하나 있는데요,
    cvtColor의 인자로 CV_BGR2GRAY는 식별자가 정의 되어있지 않다고 해서
    COLOR_BGR2GRAY로 인자를 넣어보니 같은 작업을 하게 되는거 같은데
    혹시 CV_BGR2GRAY, COLOR_BGR2GRAY 두 인자의 차이점을 알 수 있을까요?? 그냥 COLOR_BGR2GRAY를 써도 상관 없는건가요??

    답글삭제
  50. 안녕하세요 안드로이드 개발자인데 아무것도 모를 때 무작정 검색으로 이곳을 몇 번이나 들어왔었는데 처음엔 몰라서 너무 어려운 내용이라 제대로 읽지도 못했었는데 한달이 지난 뒤 지금은 어느정도 이해할수있는 수준까지 왔습니다..ㅠㅠㅠ(아직도 다 이해한건아니지만..) 좋은 자료 정리해주셔서 감사하다고 인사드리고 갑니다 감사합니다~

    답글삭제
  51. 위의 코드를 debug모드로 돌리면 debug assertion failed라는 오류가 떴었는데, 버블소트 부분에서 j의 초기값을 0 대신 1로 준 후, j와 j+1에 해당하는 값들을 j-1과 j로 바꿔주면 해결됩니다!
    초보자도 이해하기 쉽게 좋은 코드와 자세한 설명 올려주셔서 감사합니다, 개발자님!
    개발자님 가족 모두 만수무강하시고 돈 많이 버셨으면 좋겠어요.

    답글삭제
  52. 위에 vector subscript out of range 오류 뜨시는 분들 계셨는데,

    작성자님의 코드중 버블 소트하는 부분의 루프 조건문을 수정해서 해결했어요.

    int j=0; j<(boundRect2.size() - (i+1)); j++ 이런식으로요!

    답글삭제
  53. [아래로 _ ]: [C/C++] Opencv 라이브러리로, 윤곽에 기반한 자동차 번호판 영역 추출 (License Plates Recognition) >>>>> Download Now

    >>>>> Download Full

    [아래로 _ ]: [C/C++] Opencv 라이브러리로, 윤곽에 기반한 자동차 번호판 영역 추출 (License Plates Recognition) >>>>> Download LINK

    >>>>> Download Now

    [아래로 _ ]: [C/C++] Opencv 라이브러리로, 윤곽에 기반한 자동차 번호판 영역 추출 (License Plates Recognition) >>>>> Download Full

    >>>>> Download LINK

    답글삭제
  54. Debug Assertion Failed!

    Expression: vector subscript out of range 이라는 오류가 발생했습니다. 수정을 어떻게 해야할까요? 개발공부를 이제 막 시작해서 벡터라는 개념이 조금 어렵습니다.

    답글삭제