블로그 이미지
devtang
Instagram : @taebr0 devtaehyeong@gmail.com

calendar

1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30

Notice

2021. 1. 5. 20:41 개인 프로젝트

안녕하세요. 요새 코로나도 심해지는 와중에 건강 조심하시기 바랍니다.

집에서 코딩하는 것을 추천드려요~

 

 

 

이번 게시물에서는 딥러닝을 이용하지 않고

OpenCV를 이용하여 자동차 번호판 글꼴의 숫자를 인식하는 프로젝트를 작성합니다.

아직 자동차 번호판을 곧바로 인식하기엔 어려움이 있기 때문에 자동차 번호판의 숫자 글꼴을 인식하는 프로젝트로 진행하였습니다.

 

딥러닝을 이용해보신적이 있으시다면 숫자인식은 어떻게보면 "Hello World"같은 예제일정도로 쉬운 일입니다.

숫자나 어떤 이미지를 인식한다고 하면, 딥러닝에서는 데이터에서 특징을 추출하여 학습을 하여 인식을합니다.

그러나 OpenCV 라이브러리만을 이용하여 인식한다면 꽤나 생각해야할 부분이 많습니다..

본 게시물에서 진행한 숫자인식은 이런방법으로도 접근할 수 있구나라고 생각하고 봐주시면 감사하겠습니다.

 

제가 진행한 프로젝트의 흐름도는 다음과 같습니다.

우선 간단하게 요약하자면 숫자인식을 하기 위해 이미지 전처리, 관심영역 추출, 히스토그램 생성의 과정을 거칩니다.

 

표준 숫자 0~9 까지에 대해 위의 세과정을 거쳐서 표본을 만든후에

테스트할 숫자를 넣어 위와 같은 과정을 거쳐 표본 히스토그램의 픽셀수와 테스트 이미지 히스토그램의 픽셀수를 비교하여 가장 적게 차이나는 히스토그램을 가진 숫자를 인식된 숫자로 판정합니다. 

 

이미지 전처리

 

#define _CRT_SECURE_NO_WARNINGS

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
	Mat original_image; //image read(grayscale)
	Mat binary_image; //binary image


	original_image = imread("1.jpg", IMREAD_GRAYSCALE); //grayscale
	threshold(original_image, binary_image, 127, 255, THRESH_BINARY); //threshold

	imshow("grayscale", original_image);
	imshow("binary", binary_image);
	waitKey(0);
}

OpenCV를 이용하면 간단하게 이미지를 불러와서 그레이스케일 변환과 이진화를 진행할 수 있습니다.

 

이진화를 진행할때 threshold 값을 정해주어야 합니다. 그림에서 보이는 것과 같이 0이라는 숫자에

이미지의 quality가 좋지않아 noise가 발생되어있는데 0과 255 두개로만 픽셀을 깔끔하게 표현하기 위해서는

0의 윤곽쪽을 제외하고는 모두 255(흰색)으로 만들어줍니다. 

 

입력된 기본 original image를 그레이스케일 변환과 이진화를 진행한 사진입니다.

컬러 이미지를 흑백으로 전환하여 1채널(0~255)의 값으로 변경후 숫자 이미지의 픽셀을 0255로만 이루어지게 이진화를 거쳐 연산에 효율성과 인식율을 향상할 수 있었습니다.

 

이렇게 전처리를 진행함으로써 이미지의 픽셀에 접근하여 연산하는 과정 및 픽셀을 확인하는 과정을 효율적으로

진행할 수 있습니다. 

 

관심 영역 추출

이제 이진화까지의 과정을 거쳐서 0과 255로만 구성된 이미지를 얻었습니다.

다음 단계는 숫자 이외의 불필요한 배경들을 제거하여 숫자의 해당하는 픽셀의 연산에 조금더 정확하고 효율적으로

진행할 수 있도록 관심 영역의 x축 최소,최대 y축 최소,최대 좌표를 구하여 자르는 과정입니다. 

아래의 그림을 보면 이해가 쉽게 될 것 같습니다.

 

 

그림의 가로축을 X, 세로축 Y으로 놓았을때 좌측 상하단 우측 상하단을 순차적으로 돌면서 0의 라인에 해당하는

최솟값 최대값을 찾아냅니다. X-MIN ,X-MAX, Y-MIN , Y-MAX 값만 찾아내면 그값을 기준으로 자르면 되니까요.

 

#define _CRT_SECURE_NO_WARNINGS

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
	Mat original_image; //image read
	Mat binary_image; //binary image
	Mat roi_image; //roi image

	int x_count[200] = { 0, };
	int y_count[200] = { 0, };
	int x_min = 0, x_max = 0, y_min = 0, y_max = 0;
	int status = 0, pixel_count = 0;

	original_image = imread("0.jpg", IMREAD_GRAYSCALE); //grayscale
	threshold(original_image, binary_image, 127, 255, THRESH_BINARY); //threshold


 /************************** ROI 좌표 찾기 *****************************/
	for (int x = 0; x < binary_image.rows; x++) //x좌표 최솟값
	{
		for (int y = 0; y < binary_image.cols; y++)
		{
			if (binary_image.at<uchar>(y, x) == 0)
			{
				x_min = x;
				status = 1;
				break;
			}
			if (status == 1)
				break;
		}
	}
	status = 0;

	for (int y = binary_image.cols - 1; y >= 0; y--) //x좌표 최댓값
	{
		for (int x = binary_image.rows - 1; x >= 0; x--)
		{
			if (binary_image.at<uchar>(y, x) == 0)
			{
				x_max = x;
				y_max = y;
				status = 1;
				break;
			}
			if (status == 1)
				break;
		}
	}
	status = 0;

	for (int y = 0; y < binary_image.cols; y++) //y좌표 최솟값
	{
		for (int x = 0; x < binary_image.rows; x++)
		{
			if (binary_image.at<uchar>(y, x) == 0)
			{
				y_min = y;
				status = 1;
				break;
			}
			if (status == 1)
				break;
		}
	}
	status = 0;

	for (int x = binary_image.rows - 1; x >= 0; x--) //y좌표 최댓값
	{
		for (int y = binary_image.cols - 1; y >= 0; y--)
		{
			if (binary_image.at<uchar>(y, x) == 0)
			{
				if (y >= y_max)
					y_max = y;
				if (x >= x_max)
					x_max = x;
				status = 1;
				break;
			}
			if (status == 1)
				break;
		}
	}
	status = 0;
/************************** ROI 좌표 찾기 *****************************/

	roi_image = binary_image(Rect(Point(x_min, y_min), Point(x_max, y_max)));
	resize(roi_image, roi_image, Size(200, 200), 0, 0, INTER_LINEAR);

	rectangle(binary_image, Rect(Point(x_min, y_min), Point(x_max, y_max)), Scalar(0, 0, 100), 1, 4, 0);

	imshow("original", original_image);
	imshow("bin", binary_image);
	imshow("roi_image", roi_image);

	waitKey(0);
}

본 코드에는 나와있지 않지만 테스트겸 line을 그려주는 함수를 이용한 사진입니다.

 

위의 코드를 숫자 0을 입력하여 얻은 이미지입니다.

좌표가 올바르게 찾아졌는지 rectangle 함수를 이용하여 관심영역에 사각형을 그려보았습니다.

 

좌상하단 , 우상하단 = 4번의 연산이 필요합니다.

이중 for문을 이용하여 간단하게(?) 구현할 수 있습니다.

좌표를 찾았을때 break문을 걸어 탈출하며 flag를 바꾸는 방식으로 구현해보았습니다.

사실 코딩실력이 좋은편이 아니기 때문에 다소 난잡할 수 있으나,

구현적인 측면도 중요했기 떄문에 코드가 난잡하더라도 귀엽게 주니어 개발자를 봐주시면 감사하겠습니다.

	roi_image = binary_image(Rect(Point(x_min, y_min), Point(x_max, y_max)));
	resize(roi_image, roi_image, Size(200, 200), 0, 0, INTER_LINEAR);

 

주의할 코드부분은 관심영역을 자르고 나서 원본 사진의 해상도와 맞게 resize를 시켜주었습니다.

OpenCV에서는 resize 진행시 보간법(interpolation methods)을 지정해줄 수 있습니다. 
대표적으로 INTER_CUBIC ,INTER_LINEAR 등이 있습니다만, INTER_CUBIC을 사용하면 보다 더 선명한 이미지를 얻을수 있다고 합니다. 그러나 저는 INTER_LINEAR 방식으로 진행하였습니다.

 

 

히스토그램 생성

관심영역이 추출된 이미지를 가지고 본격적으로 인식에 가장 필요한 히스토그램을 생성하는 파트입니다.

 

히스토그램이란?

 

히스토그램(Histogram)은 표로 되어 있는 도수 분포를 정보 그림으로 나타낸 것입니다.

그러나 이미지 히스토그램은 이야기가 조금 다릅니다.

 

이미지 히스토그램은 가로축(x축) 에는 이미지의 픽셀 값을 나타내는 좌표값이고, 세로축(y축)으로는 픽셀의 수를 나타내는 좌표값입니다. 히스토그램으로 이미지의 대비(Contrast)나 빛의 강도 등을 나타낼 수도 있기 때문에, 이미지에서 어떤 특징점이 어느정도 분포하고 있는지 알 수 있는 그래프라고 볼 수 있습니다.

 

0과 9에대한 히스토그램... 별 차이가 없다.

저는 본 프로젝트를 진행하면서, 그레이스케일 변환한 숫자에 대해 히스토그램을이용하여 숫자 0~9 까지의 히스토그램을 모두 그려보았으나 뚜렷한 특징을 찾을 수가 없었습니다. 따라서 아래의 그림과 같이 진행하였습니다.

 

 

 

 

x축좌표에 대한 0에 해당하는 픽셀 개수를 표현

 

y축좌표에 대한 0에 해당하는 픽셀 개수를 표현

 

가로, 세로 축 좌표를 기준으로 숫자의 픽셀(검정색)의 개수를 카운팅하여, 빈 검정색 이미지에 line함수로 픽셀의 개수만큼 그려주었습니다. 축의 기준을 나눠서 히스토그램을 생성한 이유는 기존 히스토그램 사용시에, 밝은 픽셀과 어두운 픽셀의 분포를 표시하기 때문에 입력되는 이미지의 숫자의 크기에 따라 오인식 하는 확률이 크기 때문에 위와 같이 진행했었습니다.

 

x축 좌표에 대한 히스토그램 을 보면 가로축은 x가 0부터 image의 rows까지 , 세로축은 픽셀의 개수를 나타내었고

y축 좌표에 대한 히스토그램 역시 보면 가로축은 y가 0부터 image의 cols까지, 세로축은 픽셀의 개수를 나타내었습니다.

 

/************************** ROI 좌표 찾기 *****************************/

	roi_image = binary_image(Rect(Point(x_min, y_min), Point(x_max, y_max)));
	resize(roi_image, roi_image, Size(200, 200), 0, 0, INTER_LINEAR);

	Mat x_hist_image = Mat::zeros(roi_image.rows, roi_image.cols, CV_8U);
	Mat y_hist_image = Mat::zeros(roi_image.rows, roi_image.cols, CV_8U);
	Mat total_hist_image = Mat::zeros(roi_image.cols, roi_image.rows * 2, CV_8U);
	/**************************   histogram    ****************************/
	for (int y = 0; y < roi_image.rows; y++) //y축 히스토그램
	{
		pixel_count = 0;
		for (int x = 0; x < roi_image.cols; x++)
		{
			if (roi_image.at<uchar>(x, y) == 0)
			{
				pixel_count++;
			}
		}
		y_count[y] = pixel_count;

	}



	for (int x = 0; x < roi_image.cols; x++) //x축 히스토그램
	{
		pixel_count = 0;
		for (int y = 0; y < roi_image.rows; y++)
		{
			if (roi_image.at<uchar>(x, y) == 0)
			{
				pixel_count++;
			}
		}
		x_count[x] = pixel_count;
	}
	for (int x = 0; x < roi_image.rows; x++) //x축 히스토그램 그리기
		line(x_hist_image, Point(x, roi_image.rows), Point(x, roi_image.rows - x_count[x]), Scalar(255, 255, 255), 0);

	for (int y = 0; y < roi_image.cols; y++) //y축 히스토그램 그리기
		line(y_hist_image, Point(y, roi_image.cols), Point(y, roi_image.cols - y_count[y]), Scalar(255, 255, 255), 0);

	/* 통합 히스토그램 그리기 */
	for (int x= 0; x < roi_image.rows; x++) 
		line(total_hist_image , Point(x + 200, roi_image.rows), Point(x+200, roi_image.rows - x_count[x]), Scalar(255, 255, 255), 0);
	for (int y = 0; y < roi_image.cols; y++)
		line(total_hist_image, Point(y, roi_image.cols), Point(y, roi_image.cols - y_count[y]), Scalar(255, 255, 255), 0);

	/**************************   histogram    ****************************/

	imshow("x_hist", x_hist_image);
	imshow("y_hist", y_hist_image);
	imshow("original", original_image);
	imshow("bin", binary_image);
	imshow("roi_image", roi_image);
	imshow("total", total_hist_image);
	waitKey(0);
}

코드 중 가로 세로축 히스토그램을 그리는 코드의 일부입니다.

위의 코드 역시 이중 for문을 이용하여 이미지의 행 ,열을 훑어 연산하면서 픽셀의 카운트를 진행하였습니다.

테스트할 이미지의 데이터와 비교할 표준 숫자 데이터 0~9까지의 히스토그램을 모두 생성하여 저장하였습니다.

 

그림과 같이 각 숫자마다 모두 다른 히스토그램을 확인할 수 있습니다.

이제 표준 데이터의 히스토그램을 모두 저장하였으니,

테스트할 숫자 이미지 역시 지금까지 진행했던 방식으로 히스토그램을 생성합니다.

 

테스트할 숫자 데이터는 인터넷에서 자동차 번호판의 숫자를 찾아서 아래 그림과 같이 크기와 위치가 다르게 저장하여 진행하였습니다.

 

/* compare */
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			for (int y = 0; y < total_hist_image[i].cols; y++) //y축 히스토그램
			{
				for (int x = 0; x < total_hist_image[i].rows; x++)
				{
					if (total_hist_image[i].at<uchar>(x, y) != test_total_hist_image[j].at<uchar>(x, y))
						test_check_sum[i]++;
				}
			}
			if (check_sum[i] <= min)
				min = test_check_sum[i];
		}
		if (test_check_sum[i] == min)
			min = i;
		detect_num[i] = min;
	}
    
    /* compare */

표준 히스토그램 데이터와의 픽셀차이를 계산하는 코드중 일부입니다.

 

본 코드가 관심영역 추출이 올바르게 되는지, 글자의 크기에 영향을 미치지 않는지 테스트를 진행해보았습니다.

 

youtu.be/1li7GSmIwqk

 

 

테스트할 숫자 이미지가 입력이 되면 표준 숫자 데이터 0~9 를 각각 비교하면서 픽셀차이가 가장 적은 숫자를

인식하는 숫자로 판정하였습니다. 위의 사진과 같이 똑같은 글꼴이지만 위치와 숫자의 크기를 달리 하여도 표준 히스토그램의 특징과 뚜렷하게 다른점이 거의 없습니다.

 

테스트 숫자의 픽셀이 관심영역을 추출하면서 resize되어

글꼴이 깨져도 숫자의 본 모습은 크게 바뀌지 않아서 그런것 같습니다.

한계점..

 

본 프로젝트에서 진행한 숫자인식 방법은 가로 세로축을 기준으로 픽셀의 개수를 측정하였기 때문에 숫자가 조금만 회전이 되어도 오인식 하는 경우가 많았습니다..

 

회전이나 글꼴 차이에 해당하는 경우의 수는 고려하지 않고 반영하였기 때문에 본프로젝트는 추후에 더욱 좋은 구상을 통해 보완해야할 것 같습니다. 추후에 더욱 좋은 구상을 통해 보완하게 된다면 비교적 무거운 딥러닝을 사용하지 않고도 자동차 번호판을 인식할 수 있는 프로그램을 만들 수 있기 때문에 제한된 하드웨어 성능내에서 효율적인 시스템을 만들수 있을 것 같습니다.

 

본 프로젝트는 자동차 번호판 숫자 인식을 위한 프로젝트의 일부로 진행하였습니다.

막상 딥러닝을 이용할때는 몰랐는데 딥러닝의 필요성을 알게되는 프로젝트 중 하나였습니다. 영상처리에 대한 공부를 진행하면서 숫자 인식을 위해 진행했던 부분들이 흥미로운점이 많았던 것 같습니다.

 

긴 게시물 읽어주셔서 감사합니다.

 

posted by devtang
2020. 7. 29. 16:57 OpenCV

 

오늘은 OpenCV를 이용하여 비디오를 읽어오는 예제를 진행해보겠습니다.

 

 

import cv2

def readVideo():
    try:
        cap = cv2.VideoCapture('1.mp4')
    except:
        print('ERROR')
        return

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

        if not ret:
            print('Error')
        

        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        #cv2.imshow('frame',frame)
        cv2.imshow('frame',gray)

        k = cv2.waitKey(1) & 0xFF
        if k == 27:
            break
    cap.release()
    cv2.destroyAllWindows()

readVideo()

 

 

우선 전체 코드입니다.

readVideo()라는 함수를 생성하여 진행하였습니다.

 

cv2.VideoCapture() : 비디오를 읽어오기위한 함수입니다. 인자에 동영상을 지정하면 동영상을 읽어오고

0 이나 1을 넣으면 웹캠도 실시간으로 읽어올 수 있습니다. 일반적으로 웹캠은 0이면 연결이됩니다.

이번 예제는 영상을 Grayscale로 변환하여 출력해보겠습니다.

BGR->Grayscale로 변환하기 위해서는 cv2.COLOR_BGR2GRAY 를 사용합니다.

BGR->HSV로 변환하기 위해서는 cv2.COLOR_BGR2HSV 를 사용합니다.

 

 

                       

 

 

 

 

'OpenCV' 카테고리의 다른 글

[Python] OpenCV 이미지 출력하기  (0) 2020.07.29
[Windows] OpenCV를 이용한 이미지 축소 , 회전  (0) 2020.02.26
posted by devtang
2020. 7. 29. 16:25 OpenCV

오늘은 OpenCV를 이용하여 이미지를 출력하는 예제에 대해 알아보도록 하겠습니다.

 

 

예제 코드 입니다.

우선 OpenCV를 import 해주시고 예제 사진으로는 강아지 사진을 사용했습니다.

import cv2

image = 'doge.jpg'

img_color = cv2.imread(image, cv2.IMREAD_COLOR)
img_gray = cv2.imread(image,cv2.IMREAD_GRAYSCALE)
img_alpha = cv2.imread(image,cv2.IMREAD_UNCHANGED)

cv2.imread 는 opencv에서 이미지를 read하기 위한 함수입니다.

 

여기서 이미지를 읽어올때 플래그중 세가지만 예제로 돌려보았는데

IMREAD_COLOR : 이미지 원본 그대로를 컬러로 출력합니다.

IMREAD_GRAYSCALE : 이미지를 흑백 사진으로 출력합니다.

IMREAD_UNCHANGED : 이미지를 Alpha-channel 을 포함하여 출력합니다. 이것은

우리가 아는 RGB 채널에 Alpha 채널을 추가한것인데, Alpha 는 이미지의 투명도(Transparent)를 나타낸 것인데, 

RGB 처럼 색을 표현하는 값이 아니라 컴퓨터에서 렌더링할 때 쓰이는 보조적인 값입니다.

 

cv2.imshow('frame',img_color)
cv2.imshow('frame2',img_gray)
cv2.imshow('frame3',img_alpha)
cv2.waitKey(0)
cv2.destroyAllWindows()

이제 opencv의 imshow 함수를 이용하여 이미지를 화면에 띄웁니다.

 

cv2.waitKey() : 지정된 시간동안 키보드 입력을 기다리는 함수로써, 기본단위는 1/1000초 = ms 입니다.

예를들어서 cv2.waitKey(1)를 입력하였으면 1ms 동안 대기한다는 뜻이고, 0을 넣었으므로 키보드 입력이 발생하기 전까지 기다리는 뜻입니다.

cv2.destroyAllWindows() : 생성했던 윈도우 창을 모두 지웁니다. 

 

강아지가 정상적으로 로드되는데 성공했습니다.

 

'OpenCV' 카테고리의 다른 글

[Python] OpenCV 비디오 출력하기  (0) 2020.07.29
[Windows] OpenCV를 이용한 이미지 축소 , 회전  (0) 2020.02.26
posted by devtang
2020. 3. 2. 17:25 Raspberry Pi

 

EAR(Eye Aspect ratio)알고리즘을 이용한 운전자 졸음운전 방지 시스템 입니다.

 

구현 영상

 

EAR 알고리즘

Soukupová와 Čech의 2016 년 논문인 Facial Landmarks를 사용한 Real-Time Eye Blink Detection 의 작업을 기반으로  EAR ( eye aspect ratio) 관계를 반영하는 방정식을 도출 할 수 있습니다.

 

 

 

눈을 감게 되면 눈의 세로 비율이 작아지고, EAR 비율역시 작아집니다. 사람이 깜박이면 눈의 종횡비가 급격히 감소하여 0에 가까워집니다. 사람이 졸리면 눈을 감거나 눈을 조그마하게 뜨는 행동을 하게 되므로 EAR값을 이용하여 주기적으로 낮아지면 알람을 울리게 하는 졸음감지 시스템을 구현할 수 있습니다.

 

def eye_aspect_ratio(eye):
#눈에 랜드마크 좌표를 찍어서 EAR값을 예측합니다.
	A = dist.euclidean(eye[1],eye[5]) 
	B = dist.euclidean(eye[2],eye[4])
	C = dist.euclidean(eye[0],eye[3])
	
	ear = (A+B) / (2.0 * C)
	return ear

 

또한 평소 눈의 THRESH값을 0.3정도로 설정해놓고 눈을 감게되면 EAR비율이 작아지므로 ALARM이 울리게 됩니다. 

# 눈의 EAR의 THRESH 기본값을 0.3으로 설정합니다.
EYE_AR_THRESH = 0.3
EYE_AR_CONSEC_FRAMES = 48
# 프레임 카운터와 부울을 초기화하는 데 사용
# 알람이 울리면 표시
counter = 0
ALARM_ON = false

 

 

OpenCV의 drawContours 함수를 이용하여 눈 주변에 선을 실시간으로 그려줍니다.

   #외곽선으로 인식된 눈을 그려줍니다(drawContours함수이용)
		leftEyeHull = cv2.convexHull(leftEye)
		rightEyeHull = cv2.convexHull(rightEye)
		cv2.drawContours(frame, [leftEyeHull], -1, (0, 255, 0), 1)
		cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1)

 

눈을 인식해야 하므로 68_face_landmark.dat 파일을 이용하여 예제를 실행합니다.

 

정상적으로 구동이 되는것을 확인했습니다. 라즈베리파이에서 실시간으로 돌려봤지만

위에 동영상을 보시면 프레임이 상당히 낮습니다.

 

읽어주셔서 감사합니다.

 

posted by devtang
2020. 2. 28. 16:51 Jetson Nano

제 나노 보드에 설치된 Jetson Nano Developer Kit image, CUDA 10.0, Jetpack 4.3 버전 기준으로 작성하였습니다.

 

우선 OpenCV 설치 전에 필요한 라이브러리 들을 설치해줍니다.

 

(참고) - pkg-config나 unzip 또는 cmake 같은 기본 도구들은 설치가 되어있는 상태에서 진행하였습니다.

위와 같은 문제는 구글 검색하면 쉽게 찾을수 있어요.

필요한 라이브러리

·pkg-config

·unzip

·cmake

·build-essential

 

버전 업데이트 및 업그레이드 설치를 해줍시다.

sudo apt-get update
sudo apt-get upgrade

OpenCV는 C++에비해 비교적 간결한 코드를 사용할 수 있는 python 을 지원합니다.

기본적으로 2.7이 설치되어있지만, numpy와 3버전대를 설치해줬습니다.

sudo apt-get install python2.7-dev python3-dev python-numpy python3-numpy

  OpenCV에 필요한 라이브러리를 설치해줍니다.

sudo apt-get install libjpeg-dev libpng-dev libtiff-dev
sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev v4l-utils 
sudo apt-get install libxvidcore-dev libx264-dev libxine2-dev
sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
sudo apt-get install libgtk-3-dev
sudo apt-get install mesa-utils libgl1-mesa-dri libgtkgl2.0-dev libgtkglext1-dev
sudo apt-get install libatlas-base-dev gfortran libeigen3-dev

한번에 설치하면 안되는 경우도 있어서 혹시나 오류가 발생하면 

sudo apt-get install libjpeg-dev

sudo apt-get install libpng-dev 

이렇게 하나하나씩 직접 설치하셔도 괜찮습니다.

 

다 설치가 완료되면 본격적인 OpenCV 설치를 진행합니다.

mkdir opencv
cd opencv
wget -O opencv.zip https://github.com/opencv/opencv/archive/3.4.0.zip
wget -O opencv_contrib.zip https://github.com/opencv/opencv_contrib/archive/3.4.0.zip

opencv라는 폴더를 만들고 폴더안에 opencv.zip 과 opencv_contrib.zip 을 다운받아줍니다.

중요한것은 opencv 와 opencv_contrib 버전이 동일해야 합니다. (3.4.0)

 

설치가 완료되면 압축을 풀기위해 아래와 같은 명령어를 입력해줍니다.

unzip opencv.zip
unzip opencv_contrib.zip

이제 빌드 및 설치를 위해 build폴더를 생성하여 빌드 준비를 합니다.

cd opencv-3.4.0
mkdir build
cd build

build 주의! - 한글자라도 틀리면 안되니까 메모장 같은데 복사해놓고 쓰세요.

cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D WITH_TBB=OFF \
-D WITH_IPP=OFF \
-D WITH_1394=OFF \
-D BUILD_WITH_DEBUG_INFO=OFF \
-D BUILD_DOCS=OFF \
-D INSTALL_C_EXAMPLES=ON \
-D INSTALL_PYTHON_EXAMPLES=ON \
-D BUILD_EXAMPLES=OFF \
-D BUILD_TESTS=OFF \
-D BUILD_PERF_TESTS=OFF \
-D WITH_QT=OFF \
-D WITH_GTK=ON \
-D WITH_OPENGL=ON \
-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-3.4.0/modules \
-D WITH_V4L=ON  \
-D WITH_FFMPEG=ON \
-D WITH_XINE=ON \
-D BUILD_NEW_PYTHON_SUPPORT=ON \
-D PYTHON2_INCLUDE_DIR=/usr/include/python2.7 \
-D PYTHON2_NUMPY_INCLUDE_DIRS=/usr/lib/python2.7/dist-packages/numpy/core/include/ \
-D PYTHON2_PACKAGES_PATH=/usr/lib/python2.7/dist-packages \
-D PYTHON2_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython2.7.so \
-D PYTHON3_INCLUDE_DIR=/usr/include/python3.6m \
-D PYTHON3_NUMPY_INCLUDE_DIRS=/usr/lib/python3/dist-packages/numpy/core/include/  \
-D PYTHON3_PACKAGES_PATH=/usr/lib/python3/dist-packages \
-D PYTHON3_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython3.6m.so \
../

이후 make 를 해줘야되는데 -j숫자(원하는 코어사용갯수)를 입력해줍니다.

-j4를 입력하게되면 코어4개를 다사용하므로 발열이 상당히 심합니다. (오류가 날 수도 있음)

쿨러나 보드를 식힐 수 있는 선풍기가 있을때 사용하는걸 추천합니다.

make -j4
make -j2

이과정에서 꽤 걸립니다!! 저는 -j4로 코어 다사용해서 돌렸는데도 50분정도 걸렸습니다.

발열도 심하고 굉장히 오래걸리니까, opencv 빌드중에는 보드 사용을 자제하는게 좋을것 같아요.

물론 엄청 느려져서 원활한 구동도 어렵습니다.

 

OpenCV 컴파일 결과물

sudo make install
sudo sh -c 'echo '/usr/local/lib' > /etc/ld.so.conf.d/opencv.conf'
sudo ldconfig

이렇게 해서 OpenCV가 설치가 완료됬습니다.

다음 게시물은 YOLO설치 및 구동을 올릴 예정입니다.

posted by devtang
2020. 2. 26. 15:38 OpenCV

OpenCV를 이용하여 비주얼 스튜디오 2015 에서 지정된 이미지를 축소 하고 회전시킬 수 있습니다.

 

 

먼저 코드를 실행시킨 모습입니다.

원본 사진의 크기와 축소시킨 사진의 크기를 비교했습니다.

원본 사진의 해상도는 1280 X 1920이며 1/4 크기, 320 X 480로 축소시켰습니다.

	Mat image; // 빈 영상 생성
	image = imread("woman.JPG", IMREAD_COLOR); //이미지 읽기

	if (image.empty()) //이미지를 제대로 읽을 수 없으면 에러 메시지 출력
	{
		printf("Loading Error!");
		return 0;
	}

	/*콘솔창에 띄워주기*/
	printf("Original image is %d x %d\n", image.rows, image.cols);
	namedWindow("Original image"); // 원본 이미지를 전시할 창 만들기 
	imshow("Original image", image); // 원본 이미지 보여주기



	/* 축소 이미지띄우기 */
	Mat resized_image; //축소된 이미지 저장
	resize(image, resized_image, Size(), 0.25, 0.25, INTER_AREA);  //원본 이미지 크기 조정하기. 가로 세로 모두 1/4로 축소. 
	printf("Resized image is %d x %d\n", resized_image.rows, resized_image.cols); // 축소된 이미지 사이즈 출력
	namedWindow("Resized image");
	imshow("Resized image", resized_image); // 축소된 이미지 보여주기

이미지를 imread 함수로 불러온 후 resize 함수를 이용하여 0.25(1/4)로 축소하도록 선언했습니다.

상세 기능은 주석으로 달았습니다.

 

이번에 쓴 기능은 원본사진의 크기만큼 빈 이미지파일을 생성시킨후에 축소된 사진을 복사하여 넣었습니다.

 

/* 축소 이미지->원본사이즈에 대입 */
	Mat cover_image(image.rows, image.cols, CV_8UC3); // 원본사이즈만큼의 빈 이미지 생성
	resized_image.copyTo(cover_image(Rect(100, 100, resized_image.cols, resized_image.rows))); //축소한 이미지를 원본사이즈크기에 결합
	imshow("covered_image", cover_image);

cover_image라 이름을 짓고, copyTo를 이용하여 복사하여 넣습니다.

 

마지막으로 사진을 원하는 각도만큼 회전시킬 수 있습니다.

 

/*회전 이미지 띄우기 */
	Mat rotation; // 회전할 이미지
	Point2f pt(resized_image.cols / 2., resized_image.rows / 2.); //Point2f 로 회전시킬 이미지의 중심 설정 (정중앙)
	Mat r = getRotationMatrix2D(pt, 45, 0.7); // 사진 회전시키기 (사진 중앙값, 회전값, 사진 비율) 
	warpAffine(resized_image, rotation, r, Size(resized_image.cols, resized_image.rows)); //설정해준 값을 회전시켜줌
	imshow("rotate_image", rotation);

Point2f로 이미지를 회전시킬 때 중심을 설정해줍니다.

축소한 사진의 가로,세로에 2로 나누면 정중앙을 기준으로 회전합니다.

getRotationMatrix2D()함수를 이용해 각도, 사진의 크기 비율을 조절하여 회전시킬 준비를합니다.

warpAffine()함수는 설정해준 값을 기준으로 회전 시켜줍니다.

 

아래는 전체 소스입니다.

#include <opencv2/opencv.hpp> 
#include <iostream>

using namespace cv;

int main()

{

	Mat image; // 빈 영상 생성
	image = imread("woman.JPG", IMREAD_COLOR); //이미지 읽기

	if (image.empty()) //이미지를 제대로 읽을 수 없으면 에러 메시지 출력
	{
		printf("Loading Error!");
		return 0;
	}

	/*콘솔창에 띄워주기*/
	printf("Original image is %d x %d\n", image.rows, image.cols);
	namedWindow("Original image"); // 원본 이미지를 전시할 창 만들기 
	imshow("Original image", image); // 원본 이미지 보여주기



	/* 축소 이미지띄우기 */
	Mat resized_image; //축소된 이미지 저장
	resize(image, resized_image, Size(), 0.25, 0.25, INTER_AREA);  //원본 이미지 크기 조정하기. 가로 세로 모두 1/4로 축소. 
	printf("Resized image is %d x %d\n", resized_image.rows, resized_image.cols); // 축소된 이미지 사이즈 출력
	namedWindow("Resized image");
	imshow("Resized image", resized_image); // 축소된 이미지 보여주기

	/* 축소 이미지->원본사이즈에 대입 */
	Mat cover_image(image.rows, image.cols, CV_8UC3); // 원본사이즈만큼의 빈 이미지 생성
	resized_image.copyTo(cover_image(Rect(100, 100, resized_image.cols, resized_image.rows))); //축소한 이미지를 원본사이즈크기에 결합
	imshow("covered_image", cover_image);

	/*회전 이미지 띄우기 */
	Mat rotation; // 회전할 이미지
	Point2f pt(resized_image.cols / 2., resized_image.rows / 2.); //Point2f 로 회전시킬 이미지의 중심 설정 (정중앙)
	Mat r = getRotationMatrix2D(pt, 45, 0.7); // 사진 회전시키기 (사진 중앙값, 회전값, 사진 비율) 
	warpAffine(resized_image, rotation, r, Size(resized_image.cols, resized_image.rows)); //설정해준 값을 회전시켜줌
	imshow("rotate_image", rotation);

	waitKey(0);

	return 0;

}

감사합니다.

'OpenCV' 카테고리의 다른 글

[Python] OpenCV 비디오 출력하기  (0) 2020.07.29
[Python] OpenCV 이미지 출력하기  (0) 2020.07.29
posted by devtang
2020. 2. 25. 14:00 Raspberry Pi

*참고한 사이트는 게시글 하단에 남겨놓겠습니다.

 

저번 게시물에서 얼굴의 68개의 특징점을 잡아낸 알고리즘을 이용한 예제를 실행해봤습니다.

이번에는 5개의 랜드마크를 추출하는 알고리즘을 사용했습니다.

 

 

68개의 랜드마크를 추출하는 방식과 달리 ,이번 알고리즘은 

왼쪽 눈 2점, 오른쪽 눈 2점, 코 1점을 검출하여, 라즈베리파이에서 보다 더 나은 속도를 보여줍니다.

속도는 68개 추출에 비해 8-10% 향상되었으며, 잠재적으로 정확성은 조금 떨어질 수 있다고 합니다.

인식률이 상당히 좋습니다. 라즈베리파이에서 이정도 속도만 나와도 감사할 따름입니다...

실시간으로 웹캠을 통해 얼굴의 특징점이 인식됩니다.

또한

68 포인트 랜드마크 데이터 파일은 99.7MB정도였지만

5 포인트 랜드마크 데이터 파일은 9.2MB로 기존 파일에 비해 약 10배 정도 작아졌습니다.

 

구성환경으로는 전 게시물과 동일합니다.

Raspberry Pi 3B+

Picamera

OS : Raspbian Stretch 9.11

OpenCV, imutils, dlib,라이브러리 설치

 

소스코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# USAGE
 
# import the necessary packages
from imutils.video import VideoStream
from imutils import face_utils
import argparse
import imutils
import time
import dlib
import cv2
 
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-p""--shape-predictor", required=True,
    help="/home/pi/Desktop/faster_facial_landmarks/shape_predictor_5_face_landmarks.dat")
args = vars(ap.parse_args())
 
# initialize dlib's face detector (HOG-based) and then create the
# facial landmark predictor
print("[INFO] loading facial landmark predictor...")
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(args["shape_predictor"])
 
# initialize the video stream and sleep for a bit, allowing the
# camera sensor to warm up
print("[INFO] camera sensor warming up...")
#vs = VideoStream(src=0).start()
vs = VideoStream(usePiCamera=True).start() # Raspberry Pi
 
# loop over the frames from the video stream
while True:
    # grab the frame from the threaded video stream, resize it to
    # have a maximum width of 400 pixels, and convert it to
    # grayscale
    frame = vs.read()
    frame = imutils.resize(frame, width=400)
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
 
    # detect faces in the grayscale frame
    rects = detector(gray, 0)
 
    # check to see if a face was detected, and if so, draw the total
    # number of faces on the frame
    if len(rects) > 0:
        text = "{} face(s) found".format(len(rects))
        cv2.putText(frame, text, (1020), cv2.FONT_HERSHEY_SIMPLEX,
            0.5, (00255), 2)
 
    # loop over the face detections
    for rect in rects:
        # compute the bounding box of the face and draw it on the
        # frame
        (bX, bY, bW, bH) = face_utils.rect_to_bb(rect)
        cv2.rectangle(frame, (bX, bY), (bX + bW, bY + bH),
            (02550), 1)
 
        # determine the facial landmarks for the face region, then
        # convert the facial landmark (x, y)-coordinates to a NumPy
        # array
        shape = predictor(gray, rect)
        shape = face_utils.shape_to_np(shape)
 
        # loop over the (x, y)-coordinates for the facial landmarks
        # and draw each of them
        for (i, (x, y)) in enumerate(shape):
            cv2.circle(frame, (x, y), 1, (00255), -1)
            cv2.putText(frame, str(i + 1), (x - 10, y - 10),
                cv2.FONT_HERSHEY_SIMPLEX, 0.35, (00255), 1)
 
    # show the frame
    cv2.imshow("Frame", frame)
    key = cv2.waitKey(1& 0xFF
 
    # if the `q` key was pressed, break from the loop
    if key == ord("q"):
        break
 
# do a bit of cleanup
cv2.destroyAllWindows()
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

 

자세한 소스 코드 내용은, 이곳을 참조하세요.

 

코드 복사후, 동일한 폴더 내에 데이터 파일과 파이썬파일을 넣어줍니다.

(5 포인트 얼굴 랜드마크 추출 데이터 파일은 하단의 참조사이트에서 받을 수 있습니다.)

 

저는 Desktop 에 faster_facial_landmarks 라는 폴더를 생성하여 넣어주었습니다.

터미널에 명령어를 입력해줍니다.

1
2
pi@raspberrypi:~/Desktop/faster_facial_landmarks $ python faster_facial_landmarks.py -shape_predictor_5_face_landmarks.dat
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

 

이번 게시물은 5개의 특징점을 잡아내는 예제를 실행해봤습니다.

다음엔 졸음운전을 감지하는 시스템(?) 을 포스팅 하도록 하겠습니다.

 

서투른 글 읽어주셔서 감사합니다.

 

참고사이트

(Faster) Facial landmark detector with dlib

 

(Faster) Facial landmark detector with dlib - PyImageSearch

In this tutorial you'll learn how to use dlib's 5-point facial landmark model, over 10x smaller and 8-10% faster than the original 68-point facial landmark detector.

www.pyimagesearch.com

 

posted by devtang
2020. 2. 25. 12:54 Raspberry Pi

*참고한 사이트는 게시글 하단에 남겨놓겠습니다.

 

이번에 게시할 내용은 바로 dlib 와 OpenCV을 이용한 얼굴의 특징점을 찾아내는 예제입니다.

 

위와같이 얼굴의 특징점을 실시간으로 잡아주는 예제를 실행해봤습니다.

 

Facial Landmarks?

 

 

68개의 특징점을 추출한 포인트

사람의 얼굴에 특징점을 찍어낸 방법입니다.

Face Landmark estimation 알고리즘의 원리는 2014년도에 발명된 접근 방식입니다.

이것을 이용하여 카메라를 이용한 다양한 어플리케이션을 만들수도 있습니다.

 

저는 이것을 라즈베리파이로 실시간 캠영상을 받아, 얼굴에 랜드마크를 표시하는 예제를 실행해봤습니다.

실행한 환경으로는

 

Raspberry Pi 3B+

Picamera

OS : Raspbian Stretch 9.11

OpenCV, imutils, dlib,라이브러리 설치

(라즈베리파이 세팅, 라이브러리 설치 방법 등은 추후에 포스트할 예정입니다.)

 

shape_predictor_68_face_landmarks.dat 파일은 게시물 하단의 참고사이트나 메일로 연락주세요.

 

제가 실행해본 소스 코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# USAGE
 
# import the necessary packages
from imutils.video import VideoStream
from imutils import face_utils
import datetime
import argparse
import imutils
import time
import dlib
import cv2
 
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-p""--shape-predictor", required=True,
    help="path to facial landmark predictor")
ap.add_argument("-r""--picamera", type=int, default=-1,
    help="whether or not the Raspberry Pi camera should be used")
args = vars(ap.parse_args())
 
# initialize dlib's face detector (HOG-based) and then create
# the facial landmark predictor
print("[INFO] loading facial landmark predictor...")
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(args["shape_predictor"])
 
# initialize the video stream and allow the cammera sensor to warmup
print("[INFO] camera sensor warming up...")
vs = VideoStream(usePiCamera=args["picamera"> 0).start()
 
# loop over the frames from the video stream
while True:
    # grab the frame from the threaded video stream, resize it to
    # have a maximum width of 400 pixels, and convert it to
    # grayscale
    frame = vs.read()
    frame = imutils.resize(frame, width=400)
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
 
    # detect faces in the grayscale frame
    rects = detector(gray, 0)
 
    # loop over the face detections
    for rect in rects:
        # determine the facial landmarks for the face region, then
        # convert the facial landmark (x, y)-coordinates to a NumPy
        # array
        shape = predictor(gray, rect)
        shape = face_utils.shape_to_np(shape)
 
        # loop over the (x, y)-coordinates for the facial landmarks
        # and draw them on the image
        for (x, y) in shape:
            cv2.circle(frame, (x, y), 1, (00255), -1)
      
    # show the frame
    cv2.imshow("Frame", frame)
    key = cv2.waitKey(1& 0xFF
 
    # if the `q` key was pressed, break from the loop
    if key == ord("q"):
        break
 
# do a bit of cleanup
cv2.destroyAllWindows()
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

 

소스코드의 자세한 내용은 , 이곳을 참조하세요.

 

소스코드를 저장한후 꼭 소스파일과 landmark 데이터 파일이 한 폴더 내에 있어야합니다.

 

터미널 창을 열어 명령어를 입력해줍니다.

1
pi@raspberrypi:~/Desktop $ python real_landmarks.py -shape_predictor_68_face_landmarks.dat
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

성공적으로 감지가 됩니다!!

 

라즈베리파이에서 구현시 프레임이 저하되긴 하지만 활용할만한 알고리즘인것 같습니다. 

다음 게시물엔 라즈베리파이에서 조금더 효율적으로 인식이되는 방법에대해 포스팅 할 예정입니다.

서투른 글 읽어주셔서 감사합니다.

참고사이트

기계 학습(Machine Learning, 머신러닝)은 즐겁다! Part 4

 

기계 학습(Machine Learning, 머신 러닝)은 즐겁다! Part 4

딥러닝(Deep Learning)을 사용한 최신 얼굴 인식(Face Recognition)

medium.com

Real-time facial landmark detection with OpenCV, Python, and dlib

 

Real-time facial landmark detection with OpenCV, Python, and dlib - PyImageSearch

In this tutorial, I demonstrate how to detect facial landmarks in video streams in real-time using OpenCV, Python, and dlib.

www.pyimagesearch.com

 

posted by devtang
prev 1 next