Skip to content
Home » Opencv 번호판 인식 C++ | 4 시간 만에 Opencv C ++ 배우기 | 3X 프로젝트 포함 | 컴퓨터 시각 인식 192 개의 새로운 답변이 업데이트되었습니다.

Opencv 번호판 인식 C++ | 4 시간 만에 Opencv C ++ 배우기 | 3X 프로젝트 포함 | 컴퓨터 시각 인식 192 개의 새로운 답변이 업데이트되었습니다.

당신은 주제를 찾고 있습니까 “opencv 번호판 인식 c++ – 4 시간 만에 OPENCV C ++ 배우기 | 3x 프로젝트 포함 | 컴퓨터 시각 인식“? 다음 카테고리의 웹사이트 sk.taphoamini.com 에서 귀하의 모든 질문에 답변해 드립니다: https://sk.taphoamini.com/wiki. 바로 아래에서 답을 찾을 수 있습니다. 작성자 Murtaza’s Workshop – Robotics and AI 이(가) 작성한 기사에는 조회수 1,827,651회 및 좋아요 13,622개 개의 좋아요가 있습니다.

opencv 번호판 인식 c++ 주제에 대한 동영상 보기

여기에서 이 주제에 대한 비디오를 시청하십시오. 주의 깊게 살펴보고 읽고 있는 내용에 대한 피드백을 제공하세요!

d여기에서 4 시간 만에 OPENCV C ++ 배우기 | 3x 프로젝트 포함 | 컴퓨터 시각 인식 – opencv 번호판 인식 c++ 주제에 대한 세부정보를 참조하세요

This is an OpenCV C++ course that will teach you everything you need to know to get started. This course is based on my previous OpenCV Python course that now has more than a million views and 98.8% positive feedback. Like before we are going to learn the basics that include Processing images videos webcams and finding shapes colors, humans, and vehicle number plates. We will also have 3 example projects that will cover all the basics we have learned. C++ is an excellent language for implementation and creating real-world products. so having this advanced computer vision skill on your CV will really make it stand out from the competition. And Don’t worry if you are a beginner we will go step by step right from the installation up to creating exciting projects, And our main focus will be practical implementation so we will skip all the boring theory stuff.
Course Link – Code + Files:
https://www.computervision.zone/courses/opencv-cv/
Premium Courses:
✔️ Computer Vision Game Development Course:
https://bit.ly/3ttLZ2s
✔️ Computer Vision with Arduino Course:
https://bit.ly/3wzLB4m
✔️ Advanced Drone Programming Course:
https://bit.ly/3qs3v5g
✔️ Learn to Build Computer Vision Mobile Apps:
https://bit.ly/3uioY1J
✔️ Jetson Nano Premium Course:
https://bit.ly/3L8uIlF
Follow Me:
Facebook Group: https://bit.ly/3irDcb7
Discord: https://bit.ly/3JvyxAM
Facebook Page: https://bit.ly/3IvpU7W
Instagram : https://bit.ly/3NdGME3
Website: https://bit.ly/3ICFTS0
Github: https://bit.ly/3woU6PS
Product Links:
Recommend Webcam for Computer Vision: https://amzn.to/2MNtVKZ
Budget Webcam: https://amzn.to/2ZP47Ug
Computer Vision Robot Arm : https://amzn.to/3L1YacX
Cheap Drone for OpenCV: https://amzn.to/2TZpsJy
DC Motors + Wheels + Chassis: https://amzn.to/2SCZon3
DC Motors + Wheels: https://amzn.to/2QeEusw
Arduino UNO: https://amzn.to/3Jwpz6h
Motor Driver: https://amzn.to/35grl6x
Battery: https://amzn.to/2Fadc0c
Raspberry Pi 4 Best Starter Kit: https://amzn.to/3JvnEz1
Raspberry Pi Recommended Battery: https://amzn.to/2C0I9pl
My Setup:
Mouse: https://amzn.to/3tsx3BR
Mechanical Keyboard: https://amzn.to/3JyVV0q
Normal Keyboard: https://amzn.to/3L325WJ
GPU: https://amzn.to/3NdzmjW
CPU: https://amzn.to/3wsmhgI
SSD: https://amzn.to/3wzY7AS
MIC: https://amzn.to/3D43TMk
Camera: https://amzn.to/36yvl90
3D Printer: https://amzn.to/3ipWNZ4
Sim Race: https://amzn.to/3IqfvKJ
#ComputerVision
#OpenCV
#CVZone
Time Stamps:
00:00 Intro
1:24 Introduction to Images
3:48 Installation – Windows
11:51 Installation – Mac
22:05 Chapter 1 – Read Images Videos and Webcams
35:23 Chapter 2 – Basic Functions
50:21 Chapter 3 – Resize and Crop
58:31 Chapter 4 – Drawing Shapes and Text
01:11:07 Chapter 5 – Warp Perspective
01:22:40 Chapter 6 – Color Detection
01:37:17 Chapter 7 – Shapes/Contour Detection
02:14:52 Chapter8 – Face Detection
02:22:21 Project 1 – Virtual Painter
03:02:52 Project 2 – Document Scanner
03:46:14 Project 3 – License Plate Detector

opencv 번호판 인식 c++ 주제에 대한 자세한 내용은 여기를 참조하세요.

[C/C++] OpenCV 라이브러리로, 윤곽에 기반한 자동차 번호판 …

사진마다 이 수의 차이로 인해 잘 인식하던 번호판 영역이 나타나지 않을 수 있습니다. 15개의 자동차 이미지 중 10개가 잘 처리되고 5개가 그렇지 …

+ 여기에 보기

Source: mind3002.blogspot.com

Date Published: 12/16/2022

View: 2642

OpenCV 자동차 번호판 인식 – velog

자동차 번호판 인식 with OpenCV 1. … pytesseract : 글자 인식 (사진에서 번호판 숫자 추출) … THRESH_BINARY_INV, blockSize=19, C=9 ) …

+ 여기에 자세히 보기

Source: velog.io

Date Published: 9/24/2021

View: 1435

OpenCV 기반 번호판 인식 (2. 번호판 문자 인식)

문자 의 윤곽 을 가 져 와 야 하기 때문에 윤곽 의 알고리즘 은 흰색 픽 셀 을 찾 습 니 다. b. 윤곽 찾기; c. 윤곽 이 문자 인지 확인 하고 규격 이 너무 작 거나 너비 가 …

+ 자세한 내용은 여기를 클릭하십시오

Source: intrepidgeeks.com

Date Published: 1/18/2022

View: 8337

[C++] 5장. SVM 및 신경망 을 사용한 번호판 인식 – 네이버 블로그

Mastering OpenCV with Practical Computer Vision Projects 라는 책의 5장 내용입니다. 이 장에서는 Automatic Number Plate Recognition (ANPR)을 …

+ 더 읽기

Source: m.blog.naver.com

Date Published: 5/24/2022

View: 9132

차량번호 인식 Source 모음

http://mind3002.blogspot.kr/2016/01/cc-opencv-license-plates-recognition.html C/C++ OpenCV 라이브러리로, 윤곽에 기반한 자동차 번호판 영역 …

+ 여기에 자세히 보기

Source: creativeprm.tistory.com

Date Published: 11/17/2021

View: 2097

OpenCV 자동차 번호판 문자열 인식 – 다음블로그

OpenCV를 사용하여 자동차 번호판 인식을 위한 예제코드를 살펴보자, 자동차 번호판 … ‘C:/Users/Ysc/AppData/Local/Tesseract-OCR/tesseract.exe’

+ 여기에 표시

Source: blog.daum.net

Date Published: 8/9/2021

View: 1163

번호판인식 영상처리 – 개발일지

OpenCV 자동차 번호판 인식. 자동차 번호판 인식 with … THRESH_BINARY_INV, blockSize=19, C=9 ) img_thresh = cv2.adaptiveThreshold( gray …

+ 여기에 표시

Source: supersfel.tistory.com

Date Published: 2/15/2022

View: 6624

[C#] 자동차 번호판 인식 프로그램 with Tesseract-OCR …

안녕하세요 열코입니다! OpenCV로 영상처리 및 패턴인식을 공부하는 동안 가장 기본적인 자동차 번호판 인식 프로그램을 간단하게 제작해 보았습니다.

+ 여기에 자세히 보기

Source: yeolco.tistory.com

Date Published: 11/18/2021

View: 7176

주제와 관련된 이미지 opencv 번호판 인식 c++

주제와 관련된 더 많은 사진을 참조하십시오 4 시간 만에 OPENCV C ++ 배우기 | 3x 프로젝트 포함 | 컴퓨터 시각 인식. 댓글에서 더 많은 관련 이미지를 보거나 필요한 경우 더 많은 관련 기사를 볼 수 있습니다.

4 시간 만에 OPENCV C ++ 배우기 | 3x 프로젝트 포함 | 컴퓨터 시각 인식
4 시간 만에 OPENCV C ++ 배우기 | 3x 프로젝트 포함 | 컴퓨터 시각 인식

주제에 대한 기사 평가 opencv 번호판 인식 c++

  • Author: Murtaza’s Workshop – Robotics and AI
  • Views: 조회수 1,827,651회
  • Likes: 좋아요 13,622개
  • Date Published: 2020. 12. 13.
  • Video Url link: https://www.youtube.com/watch?v=2FYm3GOonhk

OpenCV 자동차 번호판 인식

자동차 번호판 인식 with OpenCV

1. 라이브러리 호출

import cv2 import numpy as np import matplotlib . pyplot as plt import pytesseract plt . style . use ( ‘dark_background’ )

사용한 라이브러리들은 총 4가지이다.

사용 용도는 다음과 같다.

cv2 : Opencv

numpy : 복잡한 수치계산

matplotlib : 시각화

pytesseract : 글자 인식 (사진에서 번호판 숫자 추출)

2. Read Input Image

2번째 단계에서는 이미지를 불러온 후 너비, 높이, 채널의 값을 저장한다.

matplotlib을 이용해 정상적으로 불러왔는지 출력해보고 저장된 너비, 높이, 채널을 확인한다.

img_ori = cv2 . imread ( ‘car2.png’ ) height , width , channel = img_ori . shape plt . figure ( figsize = ( 12 , 10 ) ) plt . imshow ( img_ori , cmap = ‘gray’ ) print ( height , width , channel )

높이가 223, 너비가 594, 채널이 3 (RGB 이므로 3)인 것을 알 수 있다.

출력을 할 때 cmap을 gray로 설정했음에도 육안으로는 원본 사진과 큰 차이를 볼 순 없었다.

3. Convert Image to Grayscale

위에서는 gray로 출력만 해보았을 뿐 실제로 변환한 것은 아니다.

3번째 단계에서는 opencv의 cvtColor 메소드를 이용해 RGB를 GRAY로 변환한다.

변환하는 방법은 2가지이다.

gray = cv2 . cvtColor ( img_ori , cv2 . COLOR_BGR2GRAY ) plt . figure ( figsize = ( 12 , 10 ) ) plt . imshow ( gray , cmap = ‘gray’ )

hsv = cv2 . cvtColor ( img_ori , cv2 . COLOR_BGR2HSV ) gray = hsv [ : , : , 2 ] plt . figure ( figsize = ( 12 , 10 ) ) plt . imshow ( gray , cmap = ‘gray’ )

위 두 코드의 동작은 완전히 동일하므로 마음에 드는 것으로 작성하면 된다.

출력 결과는 아래와 같다.

4. Adaptive Thresholding

Thresholding을 해주기 전에 가우시안 블러를 해주는 것이 번호판을 더 잘 찾게 만들어 줄 수 있다.

가우시안 블러는 사진의 노이즈를 없애는 작업이다.

가우시안 블러를 적용해야하는 이유는 아래 4-1에서 설명한다.

그럼 먼저 Thresholding을 살펴보자.

Thresholding 이란 지정한 threshold 값을 기준으로 정하고

이보다 낮은 값은 0, 높은 값은 255로 변환한다. 즉 흑과 백으로만 사진을 구성하는 것이다.

이걸 해주는 이유는 5번째 단계에서 Contours를 찾으려면 검은색 배경에 흰색 바탕이어야 한다.

또 육안으로 보기에도 객체를 더 뚜렷하게 볼 수 있다.

아래 Thresholding을 적용한 사진을 보면 이해가 쉽다.

img_blurred = cv2 . GaussianBlur ( gray , ksize = ( 5 , 5 ) , sigmaX = 0 ) img_blur_thresh = cv2 . adaptiveThreshold ( img_blurred , maxValue = 255.0 , adaptiveMethod = cv2 . ADAPTIVE_THRESH_GAUSSIAN_C , thresholdType = cv2 . THRESH_BINARY_INV , blockSize = 19 , C = 9 )

4-1. Gaussian Blur 비적용 / 적용 비교

Thresholding 적용을 보았으니 가우시안 블러를 사용하는 이유를 알기위해

적용했을 때와 적용하지 않았을 때를 출력해본다.

img_thresh = cv2 . adaptiveThreshold ( gray , maxValue = 255.0 , adaptiveMethod = cv2 . ADAPTIVE_THRESH_GAUSSIAN_C , thresholdType = cv2 . THRESH_BINARY_INV , blockSize = 19 , C = 9 )

plt . figure ( figsize = ( 20 , 20 ) ) plt . subplot ( 1 , 2 , 1 ) plt . title ( ‘Threshold only’ ) plt . imshow ( img_thresh , cmap = ‘gray’ ) plt . subplot ( 1 , 2 , 2 ) plt . title ( ‘Blur and Threshold’ ) plt . imshow ( img_blur_thresh , cmap = ‘gray’ )

왼쪽이 가우시안 블러를 적용하지 않은 사진, 오른쪽이 적용한 사진이다.

언뜻보기엔 큰 차이를 못느낄 수 있지만 번호판 밑부분을 보면 좀 더 검은색 부분이 많아졌다.

5. Find Contours

Contours란 동일한 색 또는 동일한 강도를 가지고 있는 영역의 경계선을 연결한 선이다.

findContours()는 이런 Conturs들을 찾는 opencv 메소드이다.

위 메소드는 검은색 바탕에서 흰색 대상을 찾는다.

그래서 4번째 단계에서 Thresholding을 해주고 가우시안 블러를 적용해준 것이다.

그런데 공식문서에는 findCountours의 리턴 값으로

image, contours, hierachy 이렇게 3개가 나온다고 나와있지만

현재 첫번째 리턴 값인 image가 사라진 듯하다.

그래서 contours와 로 리턴을 받았다. hierachy는 쓸 일이 없어 로 받음

사진의 윤곽선을 모두 딴 후 opencv의 drawContours() 메소드로

원본사진이랑 크기가 같은 temp_result란 변수에 그려보았다

contours , _ = cv2 . findContours ( img_blur_thresh , mode = cv2 . RETR_LIST , method = cv2 . CHAIN_APPROX_SIMPLE ) temp_result = np . zeros ( ( height , width , channel ) , dtype = np . uint8 ) cv2 . drawContours ( temp_result , contours = contours , contourIdx = – 1 , color = ( 255 , 255 , 255 ) ) plt . figure ( figsize = ( 12 , 10 ) ) plt . imshow ( temp_result )

Contours를 찾아서 그린 결과를 볼 수 있다.

6. Prepare Data

원본 사진과 동일한 크기에다가 찾은 Countours들의 좌표를 이용해

사각형 형태로 그려본다. 동시에 딕셔너리를 하나 만들어 contours들의 정보를 저장한다.

temp_result = np . zeros ( ( height , width , channel ) , dtype = np . uint8 ) contours_dict = [ ] for contour in contours : x , y , w , h = cv2 . boundingRect ( contour ) cv2 . rectangle ( temp_result , pt1 = ( x , y ) , pt2 = ( x + w , y + h ) , color = ( 255 , 255 , 255 ) , thickness = 2 ) contours_dict . append ( { ‘contour’ : contour , ‘x’ : x , ‘y’ : y , ‘w’ : w , ‘h’ : h , ‘cx’ : x + ( w / 2 ) , ‘cy’ : y + ( h / 2 ) } ) plt . figure ( figsize = ( 12 , 10 ) ) plt . imshow ( temp_result , cmap = ‘gray’ )

찾은 모든 Contours들을 사격형 형태로 볼 수 있다.

이제 번호판 글자인 것 같은 Contours들을 추려내야한다.

많은 방법이 있겠지만 단순히 생각해서

번호판의 숫자들을 손글씨처럼 다 다르지 않고 일정한 비율을 가진다.

때문에 이 비율을 이용하면 대충은 번호판 같은 contours들을 추려낼 수 있다.

아래 코드에서는 최소 비율을 0.25와 최대 비율을 1.0으로 설정한 후

contours의 너비와 높이를 이용해 비율을 구하고

우리가 정한 기준에 맞는 contours들만 따로 저장하였다.

MIN_AREA = 80 MIN_WIDTH , MIN_HEIGHT = 2 , 8 MIN_RATIO , MAX_RATIO = 0.25 , 1.0 possible_contours = [ ] cnt = 0 for d in contours_dict : area = d [ ‘w’ ] * d [ ‘h’ ] ratio = d [ ‘w’ ] / d [ ‘h’ ] if area > MIN_AREA \ and d [ ‘w’ ] > MIN_WIDTH and d [ ‘h’ ] > MIN_HEIGHT \ and MIN_RATIO < ratio < MAX_RATIO : d [ 'idx' ] = cnt cnt += 1 possible_contours . append ( d ) temp_result = np . zeros ( ( height , width , channel ) , dtype = np . uint8 ) for d in possible_contours : cv2 . rectangle ( temp_result , pt1 = ( d [ 'x' ] , d [ 'y' ] ) , pt2 = ( d [ 'x' ] + d [ 'w' ] , d [ 'y' ] + d [ 'h' ] ) , color = ( 255 , 255 , 255 ) , thickness = 2 ) plt . figure ( figsize = ( 12 , 10 ) ) plt . imshow ( temp_result , cmap = 'gray' ) 위 사진은 추려낸 contours들이다. 번호판 위치에 contours들이 선별된 걸 볼 수 있지만 전혀 관련 없는 영역의 contours들도 저장되었다. 이제 더 기준을 강화하여 번호판 글자들을 찾아야한다. 8번째 단계에서는 남은 contours 중에 확실하게 번호판을 찾기 위해 기준을 강화한다. 번호판의 특성을 고려했을 때 세울 수 있는 기준은 아래와 같다. 번호판 Contours의 width와 height의 비율은 모두 동일하거나 비슷하다. 번호판 Contours 사이의 간격은 일정하다. 최소 3개 이상 Contours가 인접해 있어야한다. (대한민국 기준) 이 특성들을 고려하여 아래와 같이 코드를 작성한다. 최종적으로 얻어야 할 것은 번호판에 대한 후보군이다. MAX_DIAG_MULTIPLYER = 5 MAX_ANGLE_DIFF = 12.0 MAX_AREA_DIFF = 0.5 MAX_WIDTH_DIFF = 0.8 MAX_HEIGHT_DIFF = 0.2 MIN_N_MATCHED = 3 def find_chars ( contour_list ) : matched_result_idx = [ ] for d1 in contour_list : matched_contours_idx = [ ] for d2 in contour_list : if d1 [ 'idx' ] == d2 [ 'idx' ] : continue dx = abs ( d1 [ 'cx' ] - d2 [ 'cx' ] ) dy = abs ( d1 [ 'cy' ] - d2 [ 'cy' ] ) diagonal_length1 = np . sqrt ( d1 [ 'w' ] ** 2 + d1 [ 'h' ] ** 2 ) distance = np . linalg . norm ( np . array ( [ d1 [ 'cx' ] , d1 [ 'cy' ] ] ) - np . array ( [ d2 [ 'cx' ] , d2 [ 'cy' ] ] ) ) if dx == 0 : angle_diff = 90 else : angle_diff = np . degrees ( np . arctan ( dy / dx ) ) area_diff = abs ( d1 [ 'w' ] * d1 [ 'h' ] - d2 [ 'w' ] * d2 [ 'h' ] ) / ( d1 [ 'w' ] * d1 [ 'h' ] ) width_diff = abs ( d1 [ 'w' ] - d2 [ 'w' ] ) / d1 [ 'w' ] height_diff = abs ( d1 [ 'h' ] - d2 [ 'h' ] ) / d1 [ 'h' ] if distance < diagonal_length1 * MAX_DIAG_MULTIPLYER \ and angle_diff < MAX_ANGLE_DIFF and area_diff < MAX_AREA_DIFF \ and width_diff < MAX_WIDTH_DIFF and height_diff < MAX_HEIGHT_DIFF : matched_contours_idx . append ( d2 [ 'idx' ] ) matched_contours_idx . append ( d1 [ 'idx' ] ) if len ( matched_contours_idx ) < MIN_N_MATCHED : continue matched_result_idx . append ( matched_contours_idx ) unmatched_contour_idx = [ ] for d4 in contour_list : if d4 [ 'idx' ] not in matched_contours_idx : unmatched_contour_idx . append ( d4 [ 'idx' ] ) unmatched_contour = np . take ( possible_contours , unmatched_contour_idx ) recursive_contour_list = find_chars ( unmatched_contour ) for idx in recursive_contour_list : matched_result_idx . append ( idx ) break return matched_result_idx result_idx = find_chars ( possible_contours ) matched_result = [ ] for idx_list in result_idx : matched_result . append ( np . take ( possible_contours , idx_list ) ) temp_result = np . zeros ( ( height , width , channel ) , dtype = np . uint8 ) for r in matched_result : for d in r : cv2 . rectangle ( temp_result , pt1 = ( d [ 'x' ] , d [ 'y' ] ) , pt2 = ( d [ 'x' ] + d [ 'w' ] , d [ 'y' ] + d [ 'h' ] ) , color = ( 255 , 255 , 255 ) , thickness = 2 ) plt . figure ( figsize = ( 12 , 10 ) ) plt . imshow ( temp_result , cmap = 'gray' ) 출력 결과는 번호판으로 추정되는 후보군이며 원본과 비교했을 때 번호판 부분임을 확인할 수 있다. 9. Rotate Plate Images 현재 우리 사진은 자동차가 정방향에서 찍혔기 때문에 번호판이 가지런하지만 대부분의 사진에서는 번호판이 기울어진 경우가 많을 것이다. 때문에 pytesseract를 이용하여 번호판 글자를 인식하기 위해 번호판 부분을 정방향으로 만들어 줄 필요가 있다. 9번째 단계에서는 해당 작업을 수행한다. 먼저 단계에서 얻은 모든 후보군에 대해 Affine Transform을 적용한다. 이후 번호판 부분만 Crop 하여 출력한다. 코드가 길기 때문에 Github 코드 참고! 보면 2개의 후보군이 출력된 걸 볼 수 있고 모두 정방향으로 잘 보인다. 10. Another Thresholding 하지만 만약 9번째 단계에서 번호판 Contours 가 없었을 때를 대비하여 10번째 단계에서는 처음에 선별되지 못한 Contours에 대해서도 후보군을 추린다. 로직은 위에서 했던 것과 동일하다. longest_idx , longest_text = - 1 , 0 plate_chars = [ ] for i , plate_img in enumerate ( plate_imgs ) : plate_img = cv2 . resize ( plate_img , dsize = ( 0 , 0 ) , fx = 1.6 , fy = 1.6 ) _ , plate_img = cv2 . threshold ( plate_img , thresh = 0.0 , maxval = 255.0 , type = cv2 . THRESH_BINARY | cv2 . THRESH_OTSU ) contours , _ = cv2 . findContours ( plate_img , mode = cv2 . RETR_LIST , method = cv2 . CHAIN_APPROX_SIMPLE ) plate_min_x , plate_min_y = plate_img . shape [ 1 ] , plate_img . shape [ 0 ] plate_max_x , plate_max_y = 0 , 0 for contour in contours : x , y , w , h = cv2 . boundingRect ( contour ) area = w * h ratio = w / h if area > MIN_AREA \ and w > MIN_WIDTH and h > MIN_HEIGHT \ and MIN_RATIO < ratio < MAX_RATIO : if x < plate_min_x : plate_min_x = x if y < plate_min_y : plate_min_y = y if x + w > plate_max_x : plate_max_x = x + w if y + h > plate_max_y : plate_max_y = y + h img_result = plate_img [ plate_min_y : plate_max_y , plate_min_x : plate_max_x ]

11. Find Chars

이제 11번째 단계에서는 추린 후보군을 이용하여 글자를 찾는다.

pytesseract를 사용해야 하는데 몇 가지 사전 준비가 필요하다.

먼저 tesseract를 다운받는다.

공식 다운로드 사이트 : 공식 사이트에서 다운받으면 2시간 가량 소요된다;;

시간을 단축하고 싶다면 어떤 분이 분할 압축하여 올려 놓은 아래 깃에 가서 다운받자.

tesseract 분할 압축 Git tesseract를 다운받은 경로를 기억해야한다.

나는 하드디스크(D 드라이브)에 다운받았다. 오른쪽 사이트에서 trained data 데이터를 다운받는다 -> trained data 다운받은 tesseart 경로에 tessdata에 trained data를 옮겨준다. 아래 코드를 pytesseract.image_to_string() 메소드 위에 추가한다.

pytesseract.pytesseract.tesseract_cmd = ‘본인 tesseract 경로/tesseract.exe’

이렇게 하면 pytesseract를 사용할 준비를 마쳤다.

이제 아래와 같이 코드를 작성 후 실행하고 결과를 출력한다.

img_result = cv2 . GaussianBlur ( img_result , ksize = ( 3 , 3 ) , sigmaX = 0 ) _ , img_result = cv2 . threshold ( img_result , thresh = 0.0 , maxval = 255.0 , type = cv2 . THRESH_BINARY | cv2 . THRESH_OTSU ) img_result = cv2 . copyMakeBorder ( img_result , top = 10 , bottom = 10 , left = 10 , right = 10 , borderType = cv2 . BORDER_CONSTANT , value = ( 0 , 0 , 0 ) ) pytesseract . pytesseract . tesseract_cmd = ‘D:/tesseract/tesseract.exe’ chars = pytesseract . image_to_string ( img_result , lang = ‘kor’ , config = ‘–psm 7 –oem 0’ ) result_chars = ” has_digit = False for c in chars : if ord ( ‘가’ ) <= ord ( c ) <= ord ( '힣' ) or c . isdigit ( ) : if c . isdigit ( ) : has_digit = True result_chars += c print ( result_chars ) plate_chars . append ( result_chars ) if has_digit and len ( result_chars ) > longest_text : longest_idx = i plt . subplot ( len ( plate_imgs ) , 1 , i + 1 ) plt . imshow ( img_result , cmap = ‘gray’ )

2개의 후보군을 문자로 변환하였을 때 위에건 이상한 글자가 출력되었고

아래건 정확하게 출력된 걸 확인할 수 있다.

코드에서 특수문자 나오거나 이상한 문자가 나올 경우 걸러주는 코드를 작성해줬기 때문에

최종적으로는 아래의 후보가 최종 번호판으로 선정된다.

12. Result

이제 우리는 최종 번호판 좌표를 얻었으니 원본 이미지에 cv2.rectangle() 메소드를 이용해

사각형을 그린 후 출력을 하면 끝난다.

아래와 같이 코드 작성 후 출력해보자.

info = plate_infos [ longest_idx ] chars = plate_chars [ longest_idx ] print ( chars ) img_out = img_ori . copy ( ) cv2 . rectangle ( img_out , pt1 = ( info [ ‘x’ ] , info [ ‘y’ ] ) , pt2 = ( info [ ‘x’ ] + info [ ‘w’ ] , info [ ‘y’ ] + info [ ‘h’ ] ) , color = ( 255 , 0 , 0 ) , thickness = 2 ) cv2 . imwrite ( chars + ‘.jpg’ , img_out ) plt . figure ( figsize = ( 12 , 10 ) ) plt . imshow ( img_out )

번호판이 아주 잘 인식되어 텍스트로 변환되었고

주변에 rectangle도 잘 나온 것을 확인할 수 있다.

OpenCV 기반 번호판 인식 (2. 번호판 문자 인식)

/* charSlicer.cpp */ #include #include using namespace std; using namespace cv; // bool charDetection(Mat rect) { float error = 0.35; const float width_height = 45.0 / 77.0; float char_width_height = (float)rect.cols / (float)rect.rows; float min_value = 0.2; float max_value = width_height*(1 + error); float min_height = 20; float max_height = 30; int pixels = countNonZero(rect); float area = rect.cols*rect.rows; float ratio = pixels / area;// return ratio<0.8&&char_width_height>min_value&&char_width_height < max_value &&rect.rows >= min_height && rect.rows <= max_height; } void plateChar(Mat srcImg) { //1. , 60 0, 255 Mat plateImg; threshold(srcImg, plateImg, 60, 255, CV_THRESH_BINARY_INV); //imshow(" ", plateImg); //2. ,findContours , clone Mat contoursImg = plateImg.clone(); vector > contours;// , /*void findContours(InputOutputArray Img, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())*/ findContours(contoursImg, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); cout << " :" << contours.size() << endl; //3. , Mat oriImg = imread("plateOri.jpg"); for (int i = 0; i < contours.size(); i++) { drawContours(oriImg, contours, i, Scalar(0, 255, 0), 1);// Rect rect = boundingRect(contours[i]);// rect.height += 1;// rect.width += 1; /*void rectangle(Mat& img, Rect rec, const Scalar& color, int thickness=1, int lineType=8, int shift=0 )*/ rectangle(oriImg, rect, Scalar(0, 0, 255), 1);// Mat charImg(plateImg, rect);// rect if (charDetection(charImg))// rect { cout << " width--height: " << rect.width << "--" << rect.height << endl; rectangle(oriImg, rect, Scalar(255, 0, 0), 1);// //imshow(" ", charImg); //imwrite(to_string(i) + ".jpg", charImg); } } imshow(" ", oriImg); } /* charClassification.cpp */ #include #include using namespace std; using namespace cv; using namespace ml; const int HORIZONTAL = 1; const int VERTICAL = 0; const int numChar = 30; const char strCharacters[numChar] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', }; // Mat projectedHistogram(Mat img, int flag) { int num = (flag) ? img.rows : img.cols;// , Mat phist = Mat::zeros(1, num, CV_32F);// , 0 for (int i = 0; i < num; i++) { Mat data = (flag) ? img.row(i) : img.col(i); phist.at (i) = countNonZero(data); } double min, max; /*void minMaxLoc(InputArray src, double* minVal, double* maxVal=0, Point* minLoc=0, Point* maxLoc=0, InputArray mask=noArray())*/ minMaxLoc(phist, &min, &max);// 0 if (max > 0) { /*src.converTo(dst, type, scale, shift)*/ phist.convertTo(phist, -1, 1.0 / max, 0);// return phist; } } // Mat featureMat(Mat img, int size) { Mat hhist = projectedHistogram(img, HORIZONTAL); Mat vhist = projectedHistogram(img, VERTICAL); Mat lowImg; resize(img, lowImg, Size(size, size));// // = + + int numCols = hhist.cols + vhist.cols + lowImg.cols*lowImg.cols; Mat featMat = Mat::zeros(1, numCols, CV_32F); // , , int idx = 0; for (int i = 0; i < vhist.cols; i++) { featMat.at (idx) = vhist.at (i); idx++; } for (int i = 0; i < hhist.cols; i++) { featMat.at (idx) = hhist.at (i); idx++; } for (int i = 0; i < lowImg.cols; i++) { for (int j = 0; j < lowImg.rows; j++) { featMat.at (idx) = (float)lowImg.at (i, j); idx++; } } return featMat; } int trainANN(Mat trainMat, Mat classesMat, int hnn, Mat featMat) { //1. , i j , (i,j)=1 Mat classesData; classesData.create(trainMat.rows, numChar, CV_32FC1); for (int i = 0; i < classesData.rows; i++) { for (int j = 0; j < classesData.cols; j++) { if (j == classesMat.at (i)) classesData.at (i, j) = 1; else classesData.at (i, j) = 0; } } Ptr trainData = TrainData::create(trainMat, ROW_SAMPLE, classesData); //2. ANN Ptr ann = ANN_MLP::create(); //3. /* setLayerSizes(InputArray _layer_sizes); */ Mat layerSizes(1, 3, CV_32SC1); layerSizes.at (0) = trainMat.cols;// layerSizes.at (1) = hnn;// layerSizes.at (2) = numChar;// =10 +20 ann->setLayerSizes(layerSizes); //4. /* setActivationFunction(int type, double param1 = 0, double param2 = 0); */ ann->setActivationFunction(ANN_MLP::SIGMOID_SYM, 1, 1); //5. /*samples – ; layout – “ ” ROW_SAMPLE “ ” COL_SAMPLE; result – */ /* train(InputArray samples,int layout,InputArray results)*/ ann->train(trainData); //6. , Mat result(1, numChar, CV_32FC1); ann->predict(featMat, result); //7. Point maxLoc; double maxVal; minMaxLoc(result, 0, &maxVal, 0, &maxLoc); return maxLoc.x; } void annClassifier(Mat charImg) { //1. int height = charImg.rows; int width = charImg.cols; cout << " width--height: " << width << "--" << height << endl; Mat warpMat = Mat::eye(2, 3, CV_32F); int max = (height>width) ? height : width; warpMat.at (0, 2) = max / 2 – width / 2;// , warpMat.at (1, 2) = max / 2 – height / 2;// , Mat warpImg(max, max, charImg.type()); warpAffine(charImg, warpImg, warpMat, warpImg.size()); imshow(” “, warpImg); cout << " width--height: " << warpImg.cols << "--" << warpImg.rows << endl; //3. Mat resizeImg; resize(warpImg, resizeImg, Size(20, 20)); imshow(" ", resizeImg); cout << " width--height: " << resizeImg.cols << "--" << resizeImg.rows << endl; //4. FileStorage fs; fs.open("OCR.xml", FileStorage::READ); Mat trainMat, classesMat; fs["TrainingDataF5"] >> trainMat; fs[“classes”] >> classesMat; cout << " : " << trainMat.rows << "--" << trainMat.cols << endl; //5. 20+20+25 Mat featMat = featureMat(resizeImg, 5); cout << " : " << trainMat.rows << "--" << trainMat.cols << endl; //6. int index = trainANN(trainMat, classesMat, 10, featMat); cout << " : "<< index << endl; cout << " : "<< strCharacters[index] << endl; } /* main.cpp */ #include #include #include "plateDetection.h" using namespace std; using namespace cv; int main(int argc, int ** argv) { /*1. */ Mat carImg = imread("car.jpg"); imgProcess(carImg); /*2. */ Mat plateImg = imread("plate.jpg", 0); SVMClassifier(plateImg); /*3. */ Mat plateImg = imread("plate.jpg", 0); plateChar(plateImg); /*4. */ Mat charImg = imread("5.jpg", 0); imshow(" ", charImg); annClassifier(charImg); waitKey(0);// return 0; } 이 내용에 흥미가 있습니까? 현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다: OpenCV 이미지 처리 - 그림에 테두리 추가 텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오. CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다. 3) 자동차 번호판 문자 절단a. 한도 값 필터, CV 사용THRESH_BINARY 매개 변 수 는 흰색 값 을 검은색 으로 바 꾸 고 검은색 값 을 흰색 으로 바 꾸 어 한도 값 출력 반전 을 실현 합 니 다. 문자 의 윤곽 을 가 져 와 야 하기 때문에 윤곽 의 알고리즘 은 흰색 픽 셀 을 찾 습 니 다.b. 윤곽 찾기;c. 윤곽 이 문자 인지 확인 하고 규격 이 너무 작 거나 너비 가 정확 하지 않 은 구역 을 제거 합 니 다.문 자 는 45 / 77 의 너비 와 높이 로 0. 35 의 오 차 를 허용 합 니 다.만약 에 한 구역 의 면적 이 80% (픽 셀 이 0 보다 큰 80%) 를 넘 으 면 이 구역 은 문자 가 아 닌 검은색 블록 이 라 고 생각 합 니 다.4) 자동차 번호판 문자 분류a. 예측 문자 의 특징 을 추출 하여 문자 특징 매트릭스 (수평 과 수직 누적 직사 도, 저 해상도 이미지 5 * 5) 를 만 들 고 M 열의 행렬 을 만 듭 니 다. 행렬 의 모든 줄 의 열 은 특징 값 수평 누적 직사 도 구조의 특징, 수직 방향 구조의 특징, 이미지 구조의 특징 을 낮 게 구분 합 니 다).b. 인공 신경 망 ANN 분류.모든 지식:1. Ostu 방법 은 최대 유형 간 차 방법 (대진 법) 이 라 고도 부 르 는데 전체 이미지 의 직사 도 특성 을 통계 하여 전체 한도 값 T 의 자동 선택 을 실현 한다. 그 알고리즘 절 차 는 다음 과 같다.1) 먼저 이미지 의 직사 도 를 계산 하고 이미지 의 모든 픽 셀 점 을 0 ~ 255 총 256 개의 빈 에 따라 각 빈 의 픽 셀 점 수량 을 통계 합 니 다.2) 귀 일 화 된 직사 도 는 모든 빈 의 픽 셀 점 수 를 총 픽 셀 점 으로 나 눌 것 입 니 다.3) i. 분류의 한도 값, 즉 그 레이스 케 일 급 을 나타 내 고 0 부터 교체 합 니 다.4) 귀 일 화 된 직사 도 를 통 해 0 ~ i 그 레이스 케 일 급 픽 셀 (픽 셀 값 이 이 범위 의 픽 셀 을 전경 픽 셀 이 라 고 가정) 이 전체 그림 에서 차지 하 는 비율 w0 을 통계 하고 전경 픽 셀 의 평균 그 레이스 케 일 u0 을 통계 합 니 다.i ~ 255 그 레이스 케 일 급 픽 셀 (픽 셀 값 이 이 범위 의 픽 셀 을 배경 픽 셀 이 라 고 가정) 이 전체 그림 에서 차지 하 는 비율 w1 을 통계 하고 배경 픽 셀 의 평균 그 레이스 케 일 u1 을 통계 합 니 다.5) 전경 픽 셀 과 배경 픽 셀 의 분산 g = w0 * w1 * (u0 - u1) (u0 - u1) 을 계산 합 니 다.6) i++;4) 로 이동 하여 i 가 256 일 때 까지 교체 가 끝 납 니 다.7) 최대 g 에 해당 하 는 i 값 을 이미지 의 전역 한도 값 T 로 한다.2. OpenCV 이미지 의 깊이 와 채널: CV(S|U|F)CS = 기호 정형 U = 부호 없 는 정형 F= 부동 소수점 형CV_8UC1 8 비트 부호 없 는 단일 채널 행렬 을 말 합 니 다.CV_32FC 2 는 32 비트 부동 소수점 형 2 채널 행렬 을 말한다.그 중에서 통 로 는 점 마다 몇 개의 수 를 저장 할 수 있 는 지 를 나타 내 는데 RGB 컬러 그림 의 모든 픽 셀 점 은 세 개의 값, 즉 세 개의 채널 이 있다.그림 의 깊이 는 각 값 이 몇 자리 에 저장 되 는 지 를 나타 내 는 정밀도 문제 이 고 일반 그림 은 8bit (비트) 이면 깊이 는 8 이다.참고 자료:1. 제5 장 SVM 과 신경 망 을 바탕 으로 하 는 자동차 번호판 인식2. 모 성운 등 은 'OpenCV 3 프로 그래 밍 입문' 을 편찬 했다.3. SVM 과 신경 망 을 기반 으로 한 번호판 인식 CSDN 시리즈 블 로그:https://blog.csdn.net/u010429424/article/details/75322182https://blog.csdn.net/zhazhiqiang/article/details/211905214. OpenCV 의 신경 망:https://www.cnblogs.com/xinxue/archive/2017/06/27/5789421.html5. 이미지 처리 중 에 뮬 레이 션 변환:https://blog.csdn.net/bytekiller/article/details/478037536. 직사 도 누적:https://blog.csdn.net/tkp2014/article/details/401515157. Ostu 대진 법:https://blog.csdn.net/ap1005834/article/details/51452516

[C++] 5장. SVM 및 신경망 을 사용한 번호판 인식

Mastering OpenCV with Practical Computer Vision Projects 라는 책의 5장 내용입니다.

이 장에서는 Automatic Number Plate Recognition (ANPR)을 만드는 데 필요한 단계를 소개합니다.

예를 들어, IR 카메라, 고정 된 자동차 위치, 조명 조건 같은 기반을 이용해서 진행 할 수 있는 다양한 접근법과 기술들이 있습니다.

모호한 빛 상태, 그리고 비평 행 위치 및 자동차 번호판의 왜곡 같은 미미한 고려점을 가지고 자동차로부터 2-3 미터 사이에서 찍은 사진의 자동차 번호판을 감지 할 수 있는 ANPR 애플리케이션을 만드는 것을 진행 할 수 있습니다.

.

이 장의 주요 목적은 이미지 세분화와 피쳐 추출, 패턴 인식 기본 및 두 가지 중요한 패턴 인식 알고리즘인 벡터 머신(Support Vector Machines) 및 인공 신경망(Artificial Neural Networks )을 소개 하는 것입니다.

이 장에서 다음 부분들을 다루도록 하겠습니다 :

• ANPR

• Plate detection

• Plate recognition

ANPR 소개

자동 번호 플레이트 인식 (ANPR)은 자동 ALPR (License-Plate Recognition), AVI (자동 차량 식별) 또는 자동차 판 인식 (CPR)로 알려져 있으며, 광학 인식(Optical

Character Recognition (OCR) ) 과 문자 인식 (OCR) 및 세그먼트 화 및 차량 등록 번호판을 읽는 감지 같은 방법들을 사용합니다

ANPR 시스템의 최상의 결과는 적외선 (IR) 카메라로 얻을 수 있는데 이는, 탐지 및 OCR 세그먼트 화를 위한 세분화 단계가 쉽고 정리되어 있으며, 오류를 최소화 할 수 있기 때문입니다.

이것은 입사각은 반사각과 동일하다는 빛의 법칙에 그 이유를 찾을 수 있습니다; 우리는 평면 거울과 같은 매끄러운 표면을 보았을 때 이 기본 반사 법칙을 알 수 있습니다.

다음 그림에서도 볼 수 있듯이, 소스광원으로 되 반사 되는 빛을 일으키는 수 천개의 작은 반구로 덮여있는 물질로 만들어진 표지판의 표면으로 인해 – 번호판의 대다수는 복고반사(retro-reflection)라고 불리는 특별한 성질을 가집니다

OpenCV 자동차 번호판 문자열 인식

※파이선 코딩 초보자를 위한 텐서플로우∙OpenCV 머신 러닝 2차 개정판 발행

http://blog.daum.net/ejleep1/1175

OpenCV를 사용하여 자동차 번호판 인식을 위한 예제코드를 살펴보자, 자동차 번호판 문자열 인식을 위한 알고리듬이 많겠지만 그 중에서도 간단한 번호판의 4 코너를 찾는 알고리듬을 사용하기로 한다. 아울러 차량의 측면에서 번호판을 찍되 그 문자열의 원근이 그다지 심하지 않은 경우를 대상으로 하자.

특히 차량 속도가 빠른 도로에서 사용할 수 있기 위해서는 코드의 알고리듬이 단순하며 처리 속도가 빠를수록 유리할 것이다. 이 예제는 아래의 url 사이트 내용을 참조하였다.

License Plate Recognition using OpenCV in Python

License Plate Recognition using OpenCV in Python

헤더 영역에서 필요한 라이브러리는 cv2, imutils, numpy, Image 및 pytesseract 이다. 마지막의 pytesseract 는 반드시 사용자가 그 경로를 정확히 알아 지정해 주어야 한다.

이미지 파일을 읽어서 파일의 해상도를 확인 출력 후 다시 이미지의 가로 해상도 값을 500 으로 설정하자. 비전 Edge 처리를 위해서 흑백영상으로 변환하고 널은 면적에 나타나는 컬러 노이즈를 제거할 수 있도록 blurring 필터 작업을 실행한다. 3-~200 사이의 밝기가 변하는 구간을 대상으로 Canny Edge 처리한다.

아래 사진의 Canny Edge 처리 결과를 살펴보자. 특히 어둔 부분은 Edge 처리가 되지 않는다. 자동차 번호판은 차량 시동 거는 시점에서 항상 번호판 등이 켜지므로 주야에 상관 없이 밝기가 밝은 영역으로서 Canny Edge 처리가 가능하다.

번호판을 인식하기 위해서는 Canny Edge 처리된 이미지를 대상으로 Contour(폐곡선)들을 찾아서(find) 모조리 작도(draw)한다.

몇 개의 Contour 가 발견될지는 알 수 없으나 의외로 그 수는 많다. 그 중에서 경험적으로 면적 기준으로 큰 순서대로 30개를 작도하도록 한다. 물론 번호판도 포함되어 있어야 한다.

30개의 Contour를 대상으로 정확하게 4개의 코너를 가지는 Edge Contoure를 찾는 간결한 알고리듬이다.

찾아낸 사각형 Contour를 Crop 작업에 의해서 잘라내고 마지막으로 Tesseract-OCR 라이브러리를 사용하여 문자열을 추출한다.

자동차 번호판 문자열 추출 작업에서 차량 번호판을 카메라로 찍는 각도에 따라 번호판 문자열에 원근감이 생길 수도 있는데 이런 경우에는 Tesseract 루틴으로 문자열 추출이 불가능하다는 점에 유의하자.

#Canny_car_plate_01.py

import imutils

import cv2

import numpy as np

import pytesseract

from PIL import Image

pytesseract.pytesseract.tesseract_cmd = ‘C:/Users/Ysc/AppData/Local/Tesseract-OCR/tesseract.exe’

image = cv2.imread(“carplate7.jpg”)

(h, w, d) = image.shape

print(“width={}, height={}, depth={}”.format(w, h, d))

cv2.imshow(“Image”, image)

img = imutils.resize(image, width=500 )

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #convert to grey scale

gray = cv2.bilateralFilter(gray, 11, 17, 17) #Blur to reduce noise

edged = cv2.Canny(gray, 30, 200)

cv2.imshow(“Canny”,edged)

cnts,new = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

img1=img.copy()

cv2.drawContours(img1,cnts,-1,(0,255,0),3)

cv2.imshow(“img1”,img1)

cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:30]

screenCnt = None #will store the number plate contour

img2 = img.copy()

cv2.drawContours(img2,cnts,-1,(0,255,0),3)

cv2.imshow(“img2”,img2) #top 30 contours

count=0

idx=7

# loop over contours

for c in cnts:

# approximate the contour

peri = cv2.arcLength(c, True)

approx = cv2.approxPolyDP(c, 0.018 * peri, True)

if len(approx) == 4: #chooses contours with 4 corners

screenCnt = approx

x,y,w,h = cv2.boundingRect(c) #finds co-ordinates of the plate

new_img=img[y:y+h,x:x+w]

cv2.imwrite(‘./’+str(idx)+’.png’,new_img) #stores the new image

idx+=1

break

#draws the selected contour on original image

cv2.drawContours(img, [screenCnt], -1, (0, 255, 0), 3)

cv2.imshow(“Final image with plate detected”,img)

Cropped_loc=’./7.png’ #the filename of cropped image

cv2.imshow(“cropped”,cv2.imread(Cropped_loc))

text=pytesseract.image_to_string(Cropped_loc,lang=’kor’, config=”) #converts image characters to string

print(“Number is:” ,text)

cv2.waitKey(0)

cv2.destroyAllWindows()

번호판인식 영상처리

728×90

드론에 관해서도 올릴게 굉장히 많다.. 처음 조립하는 납땜부터 시작해서 모든과정을 함께 했기에 천천히 적어야 하지만 우선 오늘은 번호판 인식하는 과정을 해봤으니 간단히 정리해봐야 겠다.

https://velog.io/@mactto3487/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-OpenCV-%EC%9E%90%EB%8F%99%EC%B0%A8-%EB%B2%88%ED%98%B8%ED%8C%90-%EC%9D%B8%EC%8B%9D

이분의 개발블로그에서 내용을 참조해서 공부했다.. 직접 번호판을 해보고싶으면 여길 들어가 보시면 아주 친절하게 되어있다..!

크게 11단계로 나누어서 진행된다..

1. 라이브러리 호출

2. 이미지와 높이,너비 측정

3. GRAY 변환

4. 가우시안 블러처리

오른쪽이 가우시안 블러처리를 한 것

5. contour 찾기 _cotour < 동일한 색 또는 강도를 가진 경계선 6. 사각형으로 만들기 7. 번호판글자인듯한 사각형 추려내기 8. 그 중에서 확실한 번호판후보 찾기 ( 사각형간 관계까지 생각) 9.번호판을 똑바로 하기 10. 후보군이 없을때를 대비! 11. 번호판 추출(빨간박스로) 이번학기에 영상신호처리를 들으면서 배운내용이 꽤 나와서 내용자체를 이해하는데는 크게 어렵지 않았다. 위에 간단히 적힌것처럼 어느정도 번호판의 부분을 잘 체크하는 것을 알수있었다!! 그런데.. 출력결과를 텍스트로본걸 보면 가 를 7로 인식한다던지, 화질이 조금 나쁘면 바로 이상하게 번호판을 인식했다. 프로젝트에서 드론으로 번호들을 촬영할 것이기에 화질 및 번호가 저렇게 선명히 못찍는다는 생각을 했기에 고민을 빠졌는데.. 팀원들과 고민고민한 결론은 저 빨간박스로 번호판인듯한 부분을 찾아내는것은 잘하니, 빨간 박스로 번호판을 잡아내는 범위를 늘리고, 그사진들을 저장하자였다. 이정도 범위까지..? 그 다음에 저 핑크박스 안의 부분들을 모아서 숫자및 글자인식 인공지능을 사용해서 정확도를 엄청 높여보자라는 아이디어를 냈다. 위 방식은 숫자 및 번호를 인식하는 방식이 알고리즘으로 분석하는거라 여러 오차가 계속 나왔는데 인공지능을 이용하여 해보면 잘 나오지 않을까 싶다.. 나중에 자세히 기록하겠지만 우리드론은 라즈베리파이와 FC를 연동시켜놨고, 라즈베리파이로 드론조종도 가능하다!(장애물감지) 아마 라즈베리파이상에서 차량 번호판을 촬영한 후에 저렇게 번호판주변으로 커팅해서 웹으로 보내는거 까지 하지 않을까 싶다 드론 제작과정은 나중에 자세하게 다뤄봐야겠다! 밑은 사용했던 코드이다. import cv2 import numpy as np import matplotlib.pyplot as plt import pytesseract plt.style.use('dark_background') img_ori = cv2.imread('car_num10.png') height, width, channel = img_ori.shape plt.figure(figsize=(12, 10)) plt.imshow(img_ori,cmap='gray') print(height, width, channel) print(height, width, channel) gray = cv2.cvtColor(img_ori, cv2.COLOR_BGR2GRAY) plt.figure(figsize=(12,10)) plt.imshow(gray, cmap='gray') plt.show() img_blurred = cv2.GaussianBlur(gray, ksize=(5, 5), sigmaX=0) img_blur_thresh = cv2.adaptiveThreshold( img_blurred, maxValue=255.0, adaptiveMethod=cv2.ADAPTIVE_THRESH_GAUSSIAN_C, thresholdType=cv2.THRESH_BINARY_INV, blockSize=19, C=9 ) img_thresh = cv2.adaptiveThreshold( gray, maxValue=255.0, adaptiveMethod=cv2.ADAPTIVE_THRESH_GAUSSIAN_C, thresholdType=cv2.THRESH_BINARY_INV, blockSize=19, C=9 ) plt.figure(figsize=(20,20)) plt.subplot(1,2,1) plt.title('Threshold only') plt.imshow(img_thresh, cmap='gray') plt.subplot(1,2,2) plt.title('Blur and Threshold') plt.imshow(img_blur_thresh, cmap='gray') contours, _ = cv2.findContours( img_blur_thresh, mode=cv2.RETR_LIST, method=cv2.CHAIN_APPROX_SIMPLE ) temp_result = np.zeros((height, width, channel), dtype=np.uint8) cv2.drawContours(temp_result, contours=contours, contourIdx=-1, color=(255,255,255)) plt.figure(figsize=(12, 10)) plt.imshow(temp_result) temp_result = np.zeros((height, width, channel), dtype=np.uint8) contours_dict = [] for contour in contours: x, y, w, h = cv2.boundingRect(contour) cv2.rectangle(temp_result, pt1=(x, y), pt2=(x + w, y + h), color=(255, 255, 255), thickness=2) contours_dict.append({ 'contour': contour, 'x': x, 'y': y, 'w': w, 'h': h, 'cx': x + (w / 2), 'cy': y + (h / 2) }) plt.figure(figsize=(12,10)) plt.imshow(temp_result, cmap='gray') MIN_AREA = 80 MIN_WIDTH, MIN_HEIGHT = 2, 8 MIN_RATIO, MAX_RATIO = 0.25, 1.0 possible_contours = [] cnt = 0 for d in contours_dict: area = d['w'] * d['h'] ratio = d['w'] / d['h'] if area > MIN_AREA \ and d[‘w’] > MIN_WIDTH and d[‘h’] > MIN_HEIGHT \ and MIN_RATIO < ratio < MAX_RATIO: d['idx'] = cnt cnt += 1 possible_contours.append(d) temp_result = np.zeros((height, width, channel), dtype=np.uint8) for d in possible_contours: cv2.rectangle(temp_result, pt1=(d['x'], d['y']), pt2=(d['x'] + d['w'], d['y'] + d['h']), color=(255, 255, 255), thickness=2) plt.figure(figsize=(12, 10)) plt.imshow(temp_result, cmap='gray') plt.show() MAX_DIAG_MULTIPLYER = 5 MAX_ANGLE_DIFF = 12.0 MAX_AREA_DIFF = 0.5 MAX_WIDTH_DIFF = 0.8 MAX_HEIGHT_DIFF = 0.2 MIN_N_MATCHED = 3 def find_chars(contour_list): matched_result_idx = [] for d1 in contour_list: matched_contours_idx = [] for d2 in contour_list: if d1['idx'] == d2['idx']: continue dx = abs(d1['cx'] - d2['cx']) dy = abs(d1['cy'] - d2['cy']) diagonal_length1 = np.sqrt(d1['w'] ** 2 + d1['h'] ** 2) distance = np.linalg.norm(np.array([d1['cx'], d1['cy']]) - np.array([d2['cx'], d2['cy']])) if dx == 0: angle_diff = 90 else: angle_diff = np.degrees(np.arctan(dy / dx)) area_diff = abs(d1['w'] * d1['h'] - d2['w'] * d2['h']) / (d1['w'] * d1['h']) width_diff = abs(d1['w'] - d2['w']) / d1['w'] height_diff = abs(d1['h'] - d2['h']) / d1['h'] if distance < diagonal_length1 * MAX_DIAG_MULTIPLYER \ and angle_diff < MAX_ANGLE_DIFF and area_diff < MAX_AREA_DIFF \ and width_diff < MAX_WIDTH_DIFF and height_diff < MAX_HEIGHT_DIFF: matched_contours_idx.append(d2['idx']) matched_contours_idx.append(d1['idx']) if len(matched_contours_idx) < MIN_N_MATCHED: continue matched_result_idx.append(matched_contours_idx) unmatched_contour_idx = [] for d4 in contour_list: if d4['idx'] not in matched_contours_idx: unmatched_contour_idx.append(d4['idx']) unmatched_contour = np.take(possible_contours, unmatched_contour_idx) recursive_contour_list = find_chars(unmatched_contour) for idx in recursive_contour_list: matched_result_idx.append(idx) break return matched_result_idx result_idx = find_chars(possible_contours) matched_result = [] for idx_list in result_idx: matched_result.append(np.take(possible_contours, idx_list)) temp_result = np.zeros((height, width, channel), dtype=np.uint8) for r in matched_result: for d in r: cv2.rectangle(temp_result, pt1=(d['x'], d['y']), pt2=(d['x'] + d['w'], d['y'] + d['h']), color=(255, 255, 255), thickness=2) PLATE_WIDTH_PADDING = 1.3 # 1.3 PLATE_HEIGHT_PADDING = 1.5 # 1.5 MIN_PLATE_RATIO = 3 MAX_PLATE_RATIO = 10 plate_imgs = [] plate_infos = [] for i, matched_chars in enumerate(matched_result): sorted_chars = sorted(matched_chars, key=lambda x: x['cx']) plate_cx = (sorted_chars[0]['cx'] + sorted_chars[-1]['cx']) / 2 plate_cy = (sorted_chars[0]['cy'] + sorted_chars[-1]['cy']) / 2 plate_width = (sorted_chars[-1]['x'] + sorted_chars[-1]['w'] - sorted_chars[0]['x']) * PLATE_WIDTH_PADDING sum_height = 0 for d in sorted_chars: sum_height += d['h'] plate_height = int(sum_height / len(sorted_chars) * PLATE_HEIGHT_PADDING) triangle_height = sorted_chars[-1]['cy'] - sorted_chars[0]['cy'] triangle_hypotenus = np.linalg.norm( np.array([sorted_chars[0]['cx'], sorted_chars[0]['cy']]) - np.array([sorted_chars[-1]['cx'], sorted_chars[-1]['cy']]) ) angle = np.degrees(np.arcsin(triangle_height / triangle_hypotenus)) rotation_matrix = cv2.getRotationMatrix2D(center=(plate_cx, plate_cy), angle=angle, scale=1.0) img_rotated = cv2.warpAffine(img_thresh, M=rotation_matrix, dsize=(width, height)) img_cropped = cv2.getRectSubPix( img_rotated, patchSize=(int(plate_width), int(plate_height)), center=(int(plate_cx), int(plate_cy)) ) if img_cropped.shape[1] / img_cropped.shape[0] < MIN_PLATE_RATIO or img_cropped.shape[1] / img_cropped.shape[ 0] < MIN_PLATE_RATIO > MAX_PLATE_RATIO: continue plate_imgs.append(img_cropped) plate_infos.append({ ‘x’: int(plate_cx – plate_width / 2), ‘y’: int(plate_cy – plate_height / 2), ‘w’: int(plate_width), ‘h’: int(plate_height) }) plt.subplot(len(matched_result), 1, i + 1) plt.imshow(img_cropped, cmap=’gray’) plt.show() longest_idx, longest_text = -1, 0 plate_chars = [] for i, plate_img in enumerate(plate_imgs): plate_img = cv2.resize(plate_img, dsize=(0, 0), fx=1.6, fy=1.6) _, plate_img = cv2.threshold(plate_img, thresh=0.0, maxval=255.0, type=cv2.THRESH_BINARY | cv2.THRESH_OTSU) # find contours again (same as above) contours, _ = cv2.findContours(plate_img, mode=cv2.RETR_LIST, method=cv2.CHAIN_APPROX_SIMPLE) plate_min_x, plate_min_y = plate_img.shape[1], plate_img.shape[0] plate_max_x, plate_max_y = 0, 0 for contour in contours: x, y, w, h = cv2.boundingRect(contour) area = w * h ratio = w / h if area > MIN_AREA \ and w > MIN_WIDTH and h > MIN_HEIGHT \ and MIN_RATIO < ratio < MAX_RATIO: if x < plate_min_x: plate_min_x = x if y < plate_min_y: plate_min_y = y if x + w > plate_max_x: plate_max_x = x + w if y + h > plate_max_y: plate_max_y = y + h img_result = plate_img[plate_min_y:plate_max_y, plate_min_x:plate_max_x] img_result = cv2.GaussianBlur(img_result, ksize=(3, 3), sigmaX=0) _, img_result = cv2.threshold(img_result, thresh=0.0, maxval=255.0, type=cv2.THRESH_BINARY | cv2.THRESH_OTSU) img_result = cv2.copyMakeBorder(img_result, top=10, bottom=10, left=10, right=10, borderType=cv2.BORDER_CONSTANT, value=(0, 0, 0)) pytesseract.pytesseract.tesseract_cmd = ‘C:/mingyu/tesseract.exe’ chars = pytesseract.image_to_string(img_result, lang=’kor’, config=’–psm 7 –oem 0′) result_chars = ” has_digit = False for c in chars: if ord(‘가’) <= ord(c) <= ord('힣') or c.isdigit(): if c.isdigit(): has_digit = True result_chars += c print(result_chars) plate_chars.append(result_chars) if has_digit and len(result_chars) > longest_text: longest_idx = i plt.subplot(len(plate_imgs), 1, i + 1) plt.imshow(img_result, cmap=’gray’) info = plate_infos[longest_idx] chars = plate_chars[longest_idx] print(chars) img_out = img_ori.copy() cv2.rectangle(img_out, pt1=(info[‘x’], info[‘y’]), pt2=(info[‘x’]+info[‘w’], info[‘y’]+info[‘h’]), color=(255,0,0), thickness=2) cv2.imwrite(chars + ‘.jpg’, img_out) plt.figure(figsize=(12, 10)) plt.imshow(img_out) plt.show()

728×90

[C#] 자동차 번호판 인식 프로그램 with Tesseract-OCR, OpenALPR

안녕하세요 열코입니다!

OpenCV로 영상처리 및 패턴인식을 공부하는 동안 가장 기본적인 자동차 번호판 인식 프로그램을

간단하게 제작해 보았습니다.

* 개발 환경

개발 툴 : Visual Studio 2017

개발 언어 : C#

* 기능

– IplImage Load(불러오기) 및 Save(저장)

– Web Image Load 및 Save

– Image GrayScale(흑백)

– Image Binary(이진화)

– Image CannyEdge(에지 검출)

– Templet Match 및 ROI(관심 영역 추출)

– Mouse Drag Event로 ROI

– Tesseract-OCR(API)

– OpenALPR(API)

* 소스 코드

전체 소스코드는 300줄가량 되며, 부분 소스코드만 공개하고 설명합니다.

– Binary 함수

1 2 3 4 5 6 7 8 9 IplImage gray = new IplImage(src.Size, BitDepth.U8, 1 ); // GrayScale 수행 Cv.CvtColor(src, gray, ColorConversion.BgrToGray); IplImage bina = new IplImage(src.Size, BitDepth.U8, 1 ); int temp = trackBar1.Value * 20 ; // track bar 값을 받아와 적절한 수치로 변환 Binarizer.SauvolaFast(gray, bina, temp, 0. 2 , 64 ); // Sauvola 방법으로 이진화 수행 cs

– Tesseract 함수

1 2 3 4 5 6 7 8 9 Bitmap img = new Bitmap(pictureBoxIpl2.Image); var ocr = new TesseractEngine( “./tessdata” , “kor” , EngineMode.Default); // ./tessdata : tesseract 문자 인식 훈련 데이터, kor : 한글 or eng : 영문 var texts = ocr.Process(img); textBox1.AppendText(texts.GetText()); Colored by Color Scripter cs

– OpenALPR

1 2 3 4 5 6 7 8 9 10 11 12 13 Task < string > recognizeTask = Task.Run(() = > ProcessImage(savePath)); recognizeTask.Wait(); string task_result = recognizeTask.Result; string result = task_result.Split( ‘:’ )[ 11 ].Split( ‘,’ )[ 0 ]; result = result.Replace( “\”” , “” ); result = result.Trim(); // 결과 값을 적절히 파싱 textBox1.AppendText(result); Colored by Color Scripter cs

* 실행 화면

프로그램을 실행하면 아래 사진과 같은 화면이 실행됩니다.

Load Image를 눌러 로컬 컴퓨터 내 이미지를 불러오거나 Capture를 눌러 연결된 카메라로 사진을 불러올 수 있습니다. 먼저 Capture를 눌러 사진을 찍어보겠습니다.

제 책상 위의 아이폰을 한번 찍어봤습니다…

캡처된 이미지를 불러오기 성공했다는 로그가 뜨고 화면에 이미지를 불러옵니다.

이 사진은 딱히 패턴인식할 사진이 아니므로 Load Image로 자동차 사진을 불러오겠습니다.

자동차 이미지를 불러왔습니다.

자동차 번호판을 인식해야 하는데 먼저 전처리 과정을 거쳐야 인식률이 많이 높아집니다.

(사진 선명도 및 노이즈를 제거하기 위해 처리하는 과정입니다.)

– GrayScale은 RGB 컬러사진은 흑백 사진으로 변환해주는 버튼입니다.

– CannyEdge는 사진의 Edge(모서리) 영역만 감지하여 나타내주는 버튼입니다.

– Init은 작업한 사진을 초기화하는 버튼입니다.

– Binary Degree는 Track Bar로 구현했습니다. 수치의 degree( 정도) 값에 의해 Binary(이진화) 처리를 해줍니다.

120 degree로 Binary 처리한 결과입니다.

번호판 부분이 선명하게 처리되었습니다.

마우스 이벤트를 이용하여 마우스 드래그를 하여 마우스 X, Y좌표를 받아온 후 좌표 크기만큼 잘라서 따로 표시해줍니다. (패턴 인식 알고리즘 속도 향상을 위해)

패턴인식 알고리즘은 2가지를 사용했습니다.

Logic 1은 Open Library인 Tesseract 알고리즘을 사용했고,

Logic 2는 OpenALPR API를 사용했습니다.

(OpenALPR은 유료 라이브러리로 2000회 무료로 사용 가능합니다.)

두 라이브러리 모두 인식률은 상용적으로 사용할 정도로 높진 않았습니다.

하지만 전처리 과정과 알고리즘 수정을 통해 어느 정도 인식률을 높이는 것이 가능합니다.

Logic 1 버튼을 눌러 결과를 확인하니 번호판을 정확히 인식하는 모습입니다.

실행 시간은 두 로직 모두 1~3초 정도 소요합니다. (더 많은 최소화 작업이 필요)

Templet Match는 미리 저장해 둔 Templet 사진을 통해 기존 사진과 비교하여 번호판을 찾아내는 방법입니다. 번호판 모양이 제각각이기 때문에 인식률이 많이 낮았습니다.

Logic 2(OpenALPR) 실행 결과입니다.

실행 시간은 Logic 1(Tesseract) 조금(0.5초~1초) 더 걸리는 정도였습니다.

OpenALPR 홈페이지에서 데모 프로그램 실행 시 한글 인식이 잘 되는 걸로 확인했는데

소스코드로 구현하니 한글 인식이 안되는 걸로 나옵니다… (아시는 분 댓글로 부탁드립니다)

* 수정

– OpenALPR 한글지원 가능합니다.

PostAsync에서 API주소에 country를 us에서 kr로 변경하면 한글도 인식됩니다.

한글을 unicode로 처리하기 때문에 한글이 \ud638 이런식으로 출력됩니다.

유니코드를 한글로 변경하는 알고리즘을 따로 구현하여 처리하였습니다.

유니코드 한글로 변환하기

또 다른 차량 번호판 인식 결과입니다.

다른 차량 역시 번호판을 잘 인식하는 모습입니다.

위 사진처럼 깨끗하고 깔끔하게 번호판이 나온 경우 따로 전처리 작업을 해주지 않아도 인식하는 모습입니다.

알고리즘 및 전처리 과정을 수정하여 실시간 CCTV에서 번호판 인식이 가능하고, 자율 주행 자동차에서 도로 인식 및 표지판, 신호등을 인식하는 알고리즘을 만들어 볼 수 있겠네요

질문 또는 오타나 잘못된 정보가 있는 경우 댓글로 달아주세요!

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.

키워드에 대한 정보 opencv 번호판 인식 c++

다음은 Bing에서 opencv 번호판 인식 c++ 주제에 대한 검색 결과입니다. 필요한 경우 더 읽을 수 있습니다.

이 기사는 인터넷의 다양한 출처에서 편집되었습니다. 이 기사가 유용했기를 바랍니다. 이 기사가 유용하다고 생각되면 공유하십시오. 매우 감사합니다!

사람들이 주제에 대해 자주 검색하는 키워드 4 시간 만에 OPENCV C ++ 배우기 | 3x 프로젝트 포함 | 컴퓨터 시각 인식

  • object detection
  • face
  • haar
  • opencv tutorial
  • computer vision
  • opencv tutorial for beginners
  • Learn OpenCV
  • install opencv
  • opencv projects
  • opencv object detection
  • learn python
  • opencv
  • computer vision basics
  • image processing
  • learn computer vision
  • opencv c++
  • c++ opencv tutorial
  • c++ opencv course
  • c++ course
  • face detection c++ opencv
  • Learn opencv c++
  • computer vision c++
  • c++ tutorial for begineers
  • opencv projects c++

4 #시간 #만에 #OPENCV #C #++ #배우기 #| #3x #프로젝트 #포함 #| #컴퓨터 #시각 #인식


YouTube에서 opencv 번호판 인식 c++ 주제의 다른 동영상 보기

주제에 대한 기사를 시청해 주셔서 감사합니다 4 시간 만에 OPENCV C ++ 배우기 | 3x 프로젝트 포함 | 컴퓨터 시각 인식 | opencv 번호판 인식 c++, 이 기사가 유용하다고 생각되면 공유하십시오, 매우 감사합니다.

See also  클리어 틴 블랙 헤드 | 클리어틴으로 간단한 블랙헤드 제거! 상위 236개 베스트 답변

Leave a Reply

Your email address will not be published. Required fields are marked *