'Study 외/Image Processing' 카테고리의 글 목록 :: Horizontal Grays S2

Basler 카메라는 흑백은 둘째치고 비네팅이 생긴다... FOV도 현재 사용하고자 하는 영역과 맞지 않는다.

Application에 맞는 카메라와 렌즈는 딜리버리가 6개월... 이놈의 공급망 문제 ㅠㅜ

 

일단 알고리즘은 계속 진행해야 해서 로지텍 웹캠으로 알고리즘은 진행하기로 하고 적당한 카메라는 계속 고민 중이다.

 

여튼 로지텍 웹캠을 opencv에서 장치를 열때 발생하는 문제에 대해 적어본다.

( 단지 C930C 만의 문제일지 해당하는 모델이 더 있는지는 모르겠다.)

 

기본적으로 opencv에서 장치를 열때

 

cap = cv2.VideoCapture(0)

 

이와 같이 VedeoCapture 함수로 장치를 열고 괄호 안의 장치번호를 이용하는데 현재 노트북을 사용하고 있어서

노트북 기본 카메라가 0번

로지텍 웹캠이 1번이었다.

 

그런데 문제는 cap = cv2.VideoCapture(1) 로 열면 640 * 480 화질로 열리긴 하는데 원래의 해상도인 FULL HD로 설정이 안된다.

 

cap = cv2.VideoCapture(1)

cap.set(cv2.CAP_PROP_FRAME_WIDTH,1920)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT,1080)

여기서 VedeoCapture를 열때 방법이 있다.

아래의 opencv document에 자세히 나와있다.

OpenCV: Flags for video I/O

 

OpenCV: Flags for video I/O

enum  cv::VideoCaptureAPIs {   cv::CAP_ANY = 0,   cv::CAP_VFW = 200,   cv::CAP_V4L = 200,   cv::CAP_V4L2 = CAP_V4L,   cv::CAP_FIREWIRE = 300,   cv::CAP_FIREWARE = CAP_FIREWIRE,   cv::CAP_IEEE1394 = CAP_FIREWIRE,   cv::CAP_DC1394 = CAP_FIRE

docs.opencv.org

 

그래서 아래처럼 해보았더니 바로 열리긴 한다.

cap = cv2.VideoCapture(1,cv2.CAP_DSHOW)
cap.set(cv2.CAP_PROP_FRAME_WIDTH,1920)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT,1080)

해상도도 FULL HD로 설정이 되었다. 문제는 보여지는 화면이 뭔가 끊기는 기분이라 Frame을 계산해서 출력해보았더니...

5FPS... 30FPS이 5FPS로 Frame Drop이 일어났다.

 

뭐 Buffer 셋팅에서부터 별걸 다해보았지만 아무것도 안됨

 

결국 로지텍 웹캠이 지원하는 open 방법이 무엇인지를 찾아보니

cap = cv2.VideoCapture(1,cv2.CAP_DSHOW)

cap = cv2.VideoCapture(1,cv2.CAP_MSMF)

이 두가지 방법이었다.

Direct Show는 Frame Drop 이 있으니 안되고 MSMF로 시도해보았더니 아.. 이 MSMF가 별도의 설정없이 VideoCapture(1) 이것이었다. 이게 아예 안되는 것은 아니었고

열리고 FULL HD로 셋팅은 되는데 시간이 5분가량 걸린다...

이래선 진행을 할 수 가없다. ㅠㅜ

 

열심히 검색한 결과 해결방법을 찾았는데 python 코드 앞머리에서 아래와 같은 설정을 해주어야 한다.

os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0"
 
 

이것도 주의해야 하는데

 

import os
import cv2
import timeit
import numpy as np
os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0"
 
이렇게 import의 뒤에 갖다 놓으면 동작 안됨 --;;;
 

 

import os
os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0"
import cv2
import timeit
import numpy as np
 
이렇게 해야 정상적으로 동작
 
여튼 이 방법으로 로지텍 C930C는 정상적으로 바로 열리고 Frame Drop 없이 FULL-HD 해상도로 설정도 가능하게 되었다.
 
관련 자료가 없어 애먹어서 여기에 남긴다.

정말 오랫동안 건드리지도 않았던 비전 일을 해야할일이 갑자기 생겨버렸다.

 

카메라도 렌즈도 내가 선택안했는데 남이 선택해놓은걸....

 

여튼 Basler 카메라, 리코 12mm c마운트 렌즈, GiGE 인터페이스 이거 달랑 인계 받고 시작하게 되었다.

 

GiGE 인터페이스라고 별다른 것은 없더라 이더넷 케이블로 연결하고 Basler 홈페이지에서 프로그램 다운받은 후

 

pylon viewer 실행 시켜서 보면 보인다.

 

어라.. 근데 흑백이다. 사람에게 보여줄게 아니므로 흑백도 상관없겠다만 프로젝트를 생각하면 컬러가 유리할듯한데...

 

일단 회사에 있는 로지텍 웹캠 C930c를 연결해서 이걸로 진행해보기로 한다.

 

지금까지는 C++ 에 opencv를 사용했는데

 

이번엔 파이썬에 opencv를 사용해보기로 결정

 

1. python 홈페이지에서 파이썬을 다운로드/ 설치  그냥 최신 3.10.4 다운로드 했다.

당연히 환경변수 path에 들어가야 한다.  (설치할때 체크하면 ok)

Download Python | Python.org

 

2. vscode 다운로드/설치

Download Visual Studio Code - Mac, Linux, Windows

 

Download Visual Studio Code - Mac, Linux, Windows

Visual Studio Code is free and available on your favorite platform - Linux, macOS, and Windows. Download Visual Studio Code to experience a redefined code editor, optimized for building and debugging modern web and cloud applications.

code.visualstudio.com

 

3. 폴더 하나 만들고 그 안에서 작업

 

일단 vscode 실행시키고 터미널에서 

pip install opencv-python 

하면 opencv가 깔린다. 세상 편하다.. 

이게 뭐 문제점은 없는지 걱정이다 ㅋㅋㅋ

 

여튼 비전이 내 전공도 아니고 까먹을까봐 여기에 기록해가면서 또 진행해보려고 한다.

 

주식자동화 그것도 해야하는데... 귀찮네 ㅠㅜ

특정색을 지정한 뒤 화면에서 그 색의 원만 검출한다.


일정환경에서 그냥 원을 찾는 것은 쉬운데 이건 생각보다 쉽지 않다.


이유는 기본적으로 물체추적을 할 때 영상을 흑백으로 전환하고 이진화하고 엣지 디텍트를 하는데


흑백으로 변환하면 색정보를 잃어버리고 또한 색에 따라서 이진화할 때 옵셋을 다르게 해주어야 하기 때문이다.


그래서 폭풍검색 결과 색공간을 변환해서 사용한다는 것을 알아냄

(색추출 알고리즘 참고 링크 : http://newstyle.egloos.com/2707246)


1. 우선 특정 포인트의 색을 지정하기 위해서 마우스 핸들러 프로그래밍

2. 원 영상의 Smoothing 

3. 색공간 변환 RGB -> HSV

4. 지정한 포인트의 H,S,V 값 추출

5. 검출할 H,S,V 값의 범위 지정

6. HSV 이미지 마스킹

7. 모폴로지 

8. 이미지에서 원 검출


하기로 한다.


1. 마우스 핸들링

 OpenCV의 HighGui는 마우스 이벤트에 대한 핸들러도 제공한다. cvSetMouseCallback() 이라는 함수를 이용하는데 OpenCV에서는 아래와 같이 설명한다


Sets mouse handler for the specified window


C++: void setMouseCallback(const string& winname, MouseCallback onMouse, void* userdata=0 )

C: void cvSetMouseCallback(const char* window_name, CvMouseCallback on_mouse, void* param=NULL )

Python: cv.SetMouseCallback(windowName, onMouse, param=None) → None

Parameters:

winname – Window name

onMouse – Mouse callback. See OpenCV samples, such as https://github.com/Itseez/opencv/tree/master/samples/cpp/ffilldemo.cpp, on how to specify and use the callback.

userdata – The optional parameter passed to the callback.


link : http://docs.opencv.org/modules/highgui/doc/user_interface.html?highlight=cvsetmousecallback#void cvSetMouseCallback(const char* window_name, CvMouseCallback on_mouse, void* param)


cvSetMouseCallback("PreviewImage",on_eventhandle,(void*)img); 

위와 같은 식으로 사용하였는데 img 영상에서 마우스 이벤트가 발생하면 on_eventhandle 라는 함수를 호출하도록 Setting 하는 것이다.

(http://yonghello.tistory.com/entry/%EB%A7%88%EC%9A%B0%EC%8A%A4-%EC%9D%B4%EB%B2%A4%ED%8A%B8  참조하였슴)


작성한 on_eventhandle는 아래와 같다.

void on_eventhandle(int event, int x ,int y, int flag, void* param)

{

switch(event)

{

case CV_EVENT_LBUTTONDOWN:

if(getFlag == 0)

{

getx = x;

gety = y;

getFlag = 1;

}else if(getFlag == 1)

{

getFlag = 2;

}

break;


case CV_EVENT_RBUTTONDOWN:

getFlag = 0;

break;

}

}


별다른건 없고 마우스 왼쪽 버튼 누르면 좌표를 얻어오고 한번 더 누르면 그 좌표로 셋팅, 우측 버튼 누르면 다시 처음부터 좌표 얻어올 수 있도록 초기화 하는거임.


3. 색공간 변환

색공간을 기존의 RGB(Red,Green,Blue) color space 에서 HSV(Hue, Saturation, Value - Brightness) 로 바꾼다 (http://ko.wikipedia.org/wiki/HSV_%EC%83%89_%EA%B3%B5%EA%B0%84)

OpenCV에서는  cvCvtColor(src,dest,CV_BGR2HSV); 로 간단하게 바꿀 수 있다.


4. 지정한 포인트의 H,S,V 추출

cvGet2D()함수를 이용하여 cvScalar로 저장하는 방법을 사용

cvGet2D()는 아래와 같이 설명하고 있다.

C: CvScalar cvGet1D(const CvArr* arr, int idx0)

C: CvScalar cvGet2D(const CvArr* arr, int idx0, int idx1)

C: CvScalar cvGet3D(const CvArr* arr, int idx0, int idx1, int idx2)

C: CvScalar cvGetND(const CvArr* arr, const int* idx)

Python: cv.Get1D(arr, idx) → scalar

Python: cv.Get2D(arr, idx0, idx1) → scalar

Python: cv.Get3D(arr, idx0, idx1, idx2) → scalar

Python: cv.GetND(arr, indices) → scalar

Return a specific array element.


Parameters:

arr – Input array

idx0 – The first zero-based component of the element index

idx1 – The second zero-based component of the element index

idx2 – The third zero-based component of the element index

idx – Array of the element indices

The functions return a specific array element. In the case of a sparse array the functions return 0 if the requested node does not exist (no new node is created by the functions).


link : http://docs.opencv.org/modules/core/doc/old_basic_structures.html?highlight=cvget2d#CvScalar cvGet2D(const CvArr* arr, int idx0, int idx1)


뭐 별건 없다. image에 지정한 x,y 좌표의 색정보를 가져오는 것임

CvScalar s = cvGet2D(HSVImg,gety,getx);

H = (int)s.val[0];

S = (int)s.val[1];

V = (int)s.val[2];


위와 같이 사용하였는데 여기서 중요한 것 하나!!!

cvGet2D의  첫번째 파라미터는 image, 두번째 파라미터는 y좌표, 세번째 파리미터는 x좌표 라는 것!! 

의례적으로 x,y 로 썼다가 한참 헤맸다. T.T

해당 영상이 HSV color space 이므로 자동으로 H,S,V 값으로 저장된다.

만약 RGB Color Space 라면

CvScalar v = cvGet2D(img,gety,getx);

getBlue = (int)v.val[0];

getGreen = (int)v.val[1];

getRed = (int)v.val[2];

역시 자동으로 Blue,Green,Red 값으로 받아들인다. 여기서 (Red,Green,Blue의 순이 아닌 Blue,Green,Red 순이라는 것 주의)


5. 검출할 H,S,V의 범위 지정

이건 같은 H,S,V 값만 가지는 픽셀을 선택하면 원의 색상 자체가 완전히 일정하지 않으므로 일정 범위를 줄 필요가 있겠다.

나는 그냥 원시적으로 다음과 같이 사용


//calculate HSV Boundary

lowH = H * LOW_BOUND;

highH = H * HIGH_BOUND;

if(highH >= 180) highH = 179;


lowS = S * LOW_BOUND;

highS = S * HIGH_BOUND;

if(highS >= 256) highS = 255;


lowV = V * LOW_BOUND;

highV = V * HIGH_BOUND;

if(highV >= 256) highV = 255;


위 소스를 보면 OpenCV의 HSV Color Space에서 각 H,S,V 값의 범위는 H = 0~179, S = 0~255, V = 0~255 라는 것을 알 수 있다.


6. HSV 이미지 마스킹

 지정한 범위의 색 외에 나머지는 모두 마스킹 시켜 원을 검출하기 용이하도록 한다.

 마스킹은 IplImage 가 아닌 Mat를 이용한다.


cvCreateMat(int rows, int cols, int type);

Creates a matrix header and allocates the matrix data.


C: CvMat* cvCreateMat(int rows, int cols, int type)

Python: cv.CreateMat(rows, cols, type) → mat

Parameters:

rows – Number of rows in the matrix

cols – Number of columns in the matrix

type – The type of the matrix elements in the form CV_<bit depth><S|U|F>C<number of channels> , where S=signed, U=unsigned, F=float. For example, CV _ 8UC1 means the elements are 8-bit unsigned and the there is 1 channel, and CV _ 32SC2 means the elements are 32-bit signed and there are 2 channels.

The function call is equivalent to the following code:


link : http://docs.opencv.org/modules/core/doc/old_basic_structures.html?highlight=cvcreatemat#CvMat* cvCreateMat(int rows, int cols, int type)


cvInRange(const CvArr* src, const CvArr* lower, const CvArr* upper, CvArr* dst)

Checks if array elements lie between the elements of two other arrays.


C++: void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst)

Python: cv2.inRange(src, lowerb, upperb[, dst]) → dst

C: void cvInRange(const CvArr* src, const CvArr* lower, const CvArr* upper, CvArr* dst)

C: void cvInRangeS(const CvArr* src, CvScalar lower, CvScalar upper, CvArr* dst)

Python: cv.InRange(src, lower, upper, dst) → None

Python: cv.InRangeS(src, lower, upper, dst) → None

Parameters:

src – first input array.

lowerb – inclusive lower boundary array or a scalar.

upperb – inclusive upper boundary array or a scalar.

dst – output array of the same size as src and CV_8U type.

The function checks the range as follows:


For every element of a single-channel input array:


\texttt{dst} (I)= \texttt{lowerb} (I)_0  \leq \texttt{src} (I)_0 \leq  \texttt{upperb} (I)_0


For two-channel arrays:


\texttt{dst} (I)= \texttt{lowerb} (I)_0  \leq \texttt{src} (I)_0 \leq  \texttt{upperb} (I)_0  \land \texttt{lowerb} (I)_1  \leq \texttt{src} (I)_1 \leq  \texttt{upperb} (I)_1


and so forth.


That is, dst (I) is set to 255 (all 1 -bits) if src (I) is within the specified 1D, 2D, 3D, ... box and 0 otherwise.


When the lower and/or upper boundary parameters are scalars, the indexes (I) at lowerb and upperb in the above formulas should be omitted.


link : http://docs.opencv.org/modules/core/doc/operations_on_arrays.html?highlight=cvinranges#void cvInRangeS(const CvArr* src, CvScalar lower, CvScalar upper, CvArr* dst)


mask = cvCreateMat(size.height, size.width,CV_8UC1);

cvInRangeS(HSVImg, cvScalar(lowH,lowS,lowV),cvScalar(highH,highS,highV),mask);


이와 같이 mask Mat를 만들고 cvInRanges 하면 원래 image에 정하진 범위 안의 H,S,V 값만을 남기는 Masking 이 완료된다.


7. 모폴로지

Masking 한 이미지는 도형 내부의 그림자나 외부와의 간섭으로 인한 불필요한 것들을 보정해 줄 필요가 있다. 

내가 사용한 cvErode()와 cvDilate()외에  OpenCV에서는 그 외에 다른 모폴로지 함수(cvMorphologyEx와 같은)를 많이 제공한다.

cvErode는 침식, cvDelate는 팽창  http://bench87.tistory.com/47 여기에 예제와 이미지까지 잘 설명되어 있다.


Erodes an image by using a specific structuring element.


C++: void erode(InputArray src, OutputArray dst, InputArray kernel, Point anchor=Point(-1,-1), int iterations=1, int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue() )

Python: cv2.erode(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]]) → dst

C: void cvErode(const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iterations=1)

Python: cv.Erode(src, dst, element=None, iterations=1) → None

Parameters:

src – input image; the number of channels can be arbitrary, but the depth should be one of CV_8U, CV_16U, CV_16S, CV_32F` or ``CV_64F.

dst – output image of the same size and type as src.

element – structuring element used for erosion; if element=Mat() , a 3 x 3 rectangular structuring element is used.

anchor – position of the anchor within the element; default value (-1, -1) means that the anchor is at the element center.

iterations – number of times erosion is applied.

borderType – pixel extrapolation method (see borderInterpolate() for details).

borderValue – border value in case of a constant border (see createMorphologyFilter() for details).

The function erodes the source image using the specified structuring element that determines the shape of a pixel neighborhood over which the minimum is taken:


\texttt{dst} (x,y) =  \min _{(x',y'):  \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')


The function supports the in-place mode. Erosion can be applied several ( iterations ) times. In case of multi-channel images, each channel is processed independently.


link : http://docs.opencv.org/modules/imgproc/doc/filtering.html?highlight=cverode#void cvErode(const CvArr* src, CvArr* dst, IplConvKernel* element, int iterations)


Dilates an image by using a specific structuring element.


C++: void dilate(InputArray src, OutputArray dst, InputArray kernel, Point anchor=Point(-1,-1), int iterations=1, int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue() )

Python: cv2.dilate(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]]) → dst

C: void cvDilate(const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iterations=1 )

Python: cv.Dilate(src, dst, element=None, iterations=1) → None

Parameters:

src – input image; the number of channels can be arbitrary, but the depth should be one of CV_8U, CV_16U, CV_16S, CV_32F` or ``CV_64F.

dst – output image of the same size and type as src.

element – structuring element used for dilation; if element=Mat() , a 3 x 3 rectangular structuring element is used.

anchor – position of the anchor within the element; default value (-1, -1) means that the anchor is at the element center.

iterations – number of times dilation is applied.

borderType – pixel extrapolation method (see borderInterpolate() for details).

borderValue – border value in case of a constant border (see createMorphologyFilter() for details).

The function dilates the source image using the specified structuring element that determines the shape of a pixel neighborhood over which the maximum is taken:


\texttt{dst} (x,y) =  \max _{(x',y'):  \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')


The function supports the in-place mode. Dilation can be applied several ( iterations ) times. In case of multi-channel images, each channel is processed independently.



cvErode(mask,mask,NULL);

cvDilate(mask,mask,NULL);


위와 같이 사용하였는데 3번째 파라미터에 따라 그 침식 또는 팽창의 정도를 수정할 수 있다.


이렇게 완성한 영상을 마지막으로 기존에 해왔던 것 처럼 cvHoughCircle 함수를 이용하여 원을 찾아내면 끝


이렇게 완성한 소스는 다음과 같다.

(역시 대충 삽질해가며 작성하였기에 주석처리해버린 것들이라던지 좀 무식한 방법이라던지 그대로다 ㅋ)

//============================================================================

// Name        : GetColor.cpp

// Author      : 

// Version     :

// Copyright   : Your copyright notice

// Description : Hello World in C++, Ansi-style

//============================================================================


#include <iostream>

#include <stdio.h>

#include <sys/time.h>



#include "opencv2/opencv.hpp"


#define LOW_BOUND 0.7

#define HIGH_BOUND 1.3


struct timeval start_time;

struct timeval end_time;

double elapsed_time;

float fps;

double total_fps = 0.0,average_fps = 0.0;

long cnt=1;

char buffer[25];

CvFont font;

char getFlag = 0;

long getx,gety;

int getRed,getGreen,getBlue;

int Bval,Rval,Gval;


using namespace std;


void on_eventhandle(int event, int x ,int y, int flag, void* param)

{

switch(event)

{

case CV_EVENT_LBUTTONDOWN:


if(getFlag == 0)

{

getx = x;

gety = y;

getFlag = 1;

}else if(getFlag == 1)

{

getFlag = 2;

}

break;


case CV_EVENT_RBUTTONDOWN:

getFlag = 0;

break;

}




}

int main() {


IplImage* img;

IplImage* GrayImg=0;

IplImage* SmoothImg=0;

IplImage* Edge=0;

IplImage* HSVImg;

IplImage* HImg = 0;

IplImage* SImg = 0;

IplImage* VImg = 0;

CvMat* mask;

int key;

int k;

double dp = 1;

double min_dist = 300;

int H,S,V;

int lowH,lowS,lowV,highH,highS,highV;

CvSize size;

int cx,cy,radius,ccnt;

cvInitFont(&font,CV_FONT_HERSHEY_SIMPLEX,0.5,0.5);



CvCapture* capture = cvCaptureFromCAM(0);

cvSetCaptureProperty( capture, CV_CAP_PROP_FRAME_WIDTH, 640);

cvSetCaptureProperty( capture, CV_CAP_PROP_FRAME_HEIGHT, 480);

cvSetCaptureProperty( capture, CV_CAP_PROP_FPS, 65);



while(1)

{

gettimeofday(&start_time,NULL);


cvGrabFrame(capture);

img = cvRetrieveFrame(capture);


cvSetMouseCallback("PreviewImage",on_eventhandle,(void*)img);


if(getFlag == 0)

{

sprintf(buffer,"Picking up color by left click");

cvPutText(img,buffer,cvPoint(200,20),&font,CV_RGB(255,255,255));

}else if(getFlag == 1)

{

sprintf(buffer,"If below color is right you chosen, start detect by left click again");

cvPutText(img,buffer,cvPoint(20,20),&font,CV_RGB(255,255,255));

size = cvGetSize(img);

CvScalar v = cvGet2D(img,gety,getx);

getBlue = (int)v.val[0];

getGreen = (int)v.val[1];

getRed = (int)v.val[2];

//Convert get RGB to HSV

SmoothImg = cvCreateImage(size,IPL_DEPTH_8U,3);

cvSmooth(img,SmoothImg,CV_GAUSSIAN,9,9,2,2);

HSVImg = cvCreateImage(size,IPL_DEPTH_8U,3);

cvCvtColor(SmoothImg,HSVImg,CV_BGR2HSV);

CvScalar s = cvGet2D(HSVImg,gety,getx);

H = (int)s.val[0];

S = (int)s.val[1];

V = (int)s.val[2];


sprintf(buffer,"X= %d,Y= %d,R= %d G= %d, B= %d, H= %d, S= %d, V= %d",getx,gety,getRed,getGreen,getBlue,H,S,V);

cvPutText(img,buffer,cvPoint(getx,gety),&font,CV_RGB(255,255,255));

//printf("X= %d,Y= %d,Red = %d, Green = %d, Blue = %d\n",getx,gety,getRed,getGreen,getBlue);


}else{

sprintf(buffer,"You can picking up again by right click");

cvPutText(img,buffer,cvPoint(100,20),&font,CV_RGB(255,255,255));

//SmoothImg = cvCreateImage(size,IPL_DEPTH_8U,3);

cvSmooth(img,SmoothImg,CV_GAUSSIAN,9,9,2,2);

//Change Color Space

cvCvtColor(SmoothImg,HSVImg,CV_BGR2HSV);


//calculate HSV Boundary

lowH = H * LOW_BOUND;

highH = H * HIGH_BOUND;

if(highH >= 180) highH = 179;


lowS = S * LOW_BOUND;

highS = S * HIGH_BOUND;

if(highS >= 256) highS = 255;


lowV = V * LOW_BOUND;

highV = V * HIGH_BOUND;

if(highV >= 256) highV = 255;

//Masking

mask = cvCreateMat(size.height, size.width,CV_8UC1);

cvInRangeS(HSVImg, cvScalar(lowH,lowS,lowV),cvScalar(highH,highS,highV),mask);

//CvMat* tempMat;

//tempMat = cvCreateMat(size.height, size.width,CV_8UC1);



//cvMorphologyEx(mask,mask,tempMat,NULL,CV_MOP_OPEN);

//IplConvKernel *se21 = cvCreateStructuringElementEx(21,21,10,10,CV_SHAPE_RECT,NULL);

//IplConvKernel *se11 = cvCreateStructuringElementEx(11,11,5,5,CV_SHAPE_RECT,NULL);

cvErode(mask,mask,NULL);

cvDilate(mask,mask,NULL);


//cvErode(mask,mask,se21);

//cvDilate(mask,mask,se11);

//cvErode(mask,mask,se21);

//cvReleaseStructuringElement(&se21);

//cvReleaseStructuringElement(&se11);

//cvReleaseMat(&tempMat);


//Hough Tranform

GrayImg = cvCreateImage(size,8,1);

cvCopy(mask,GrayImg,NULL);

cvSmooth(GrayImg,GrayImg,CV_GAUSSIAN,15,15,2,2);


CvMemStorage* storage = cvCreateMemStorage(0);


CvSeq* Circles = cvHoughCircles(GrayImg,storage,CV_HOUGH_GRADIENT,2,size.height/10,100,40,5,60);

//CvSeq* Circles = cvHoughCircles(GrayImg,storage,CV_HOUGH_GRADIENT,1,100,100,50,0,0);


for(k=0;k<Circles->total;k++)

{

float* circle;

circle = (float*)cvGetSeqElem(Circles,k);


ccnt = Circles->total;


cx = cvRound(circle[0]);

cy = cvRound(circle[1]);

radius = cvRound(circle[2]);

cvCircle(img,cvPoint(cx,cy),radius,CV_RGB(0,255,0),3,8,0);


sprintf(buffer,"X=%d, Y=%d, R=%d",cx,cy,radius);

cvPutText(img,buffer,cvPoint(cx-80,cy+radius+15),&font,CV_RGB(255,255,255));

}


cvReleaseMemStorage(&storage);


cvNamedWindow( "HSVImage", 1 );

cvShowImage( "HSVImage", HSVImg);

cvMoveWindow("HSVImage",850,200);


cvNamedWindow( "Mask", 1 );

cvShowImage( "Mask", mask);

cvMoveWindow("Mask",850,700);



}


cvNamedWindow( "PreviewImage", 1 );

cvShowImage( "PreviewImage", img );

cvMoveWindow("PreviewImage",200,200);


//cvNamedWindow( "GrayImage", 1 );

//cvShowImage( "GrayImage", GrayImg );

//cvMoveWindow("GrayImage",850,400);



key = cvWaitKey(1);

if(key > 10) break;


gettimeofday(&end_time, NULL);



elapsed_time = (double)(end_time.tv_sec) + (double)(end_time.tv_usec)/1000000.0 - (double)(start_time.tv_sec) - (double)(start_time.tv_usec)/1000000.0;

fps = 1.0 / elapsed_time;

total_fps += fps;

average_fps = total_fps / (double)(cnt);

cnt++;

if(cnt == 100)

{

cnt = 1;

total_fps = 0.0;

}

elapsed_time *= 1000.0;


printf("Elapsed T = %2.3f ms, Frame = %2.3f (fps), Avrg Frame Rate = %2.3f\n",elapsed_time,fps,average_fps);



//cvReleaseMemStorage(&storage);

} //end of while(1)

cvReleaseImage(&img);

cvReleaseCapture( &capture );

cvReleaseMat(&mask);

cvReleaseImage(&SmoothImg);

cvReleaseImage(&GrayImg);

cvReleaseImage(&HSVImg);

return 0;

}


그래서 잘 찾느냐.. 그 결과는 다음과 같았다.




왼쪽 위의 영상이 원래의 이미지, 녹색의 원이 파란색의 물체를 잘 찾았슴을 보이고 아래에 마우스로 찍은 X,Y 좌표가 표시되고 있다.

우측 위의 영상은 원래의 이미지를 HSV Color Space로 변환한 이미지

우측 아래의 영상은 마스킹하고 모폴로지 까지 완료한 영상 :)


끝! ㅋ



이번에 할 것은 영상내의 사각형을 찾아내고 그 사각형의 기울기를 계산하는 것


아래 OpenCV에서 친절히 샘플 소스를 줬는데... 뭔 소린지 잘 모르겠어서 참고하고 기존에 쓰던 알고리즘을 이용해서 하기로 했다.


1. 영상취득

2. Gray Scale로 변환 - cvCvtColor()

3. Smooth - cvSmooth()

4. 이진화 - cvThreshold()

5. Edge Detect - cvCanny()

6. 코너검출 - cvFindContours()

7. 다각형 추측 - cvApproxPoly()

8. cvApproxPoly의 결과값이 사각형이라면 각 코너포인트를 이용하여 사각형의 긴 부분과 짧은 부분 구분

9. 사각형의 짧은 두 부분의 중간점 찾아내고 그 중간점을 atan() 이용하여 두 중간점을 있는 선의 기울기를 구한다.



OpenCV Squares.c Sample Code

// 
// The full "Square Detector" program. 
// It loads several images subsequentally and tries to find squares in 
// each image 
// 
#ifdef _CH_ 
#pragma package <opencv> 
#endif 

#include "cv.h" 
#include "highgui.h" 
#include <stdio.h> 
#include <math.h> 
#include <string.h> 

int thresh = 50; 
IplImage* img = 0; 
IplImage* img0 = 0; 
CvMemStorage* storage = 0; 
const char* wndname = "Square Detection Demo"; 

// helper function: 
// finds a cosine of angle between vectors 
// from pt0->pt1 and from pt0->pt2 
double angle( CvPoint* pt1, CvPoint* pt2, CvPoint* pt0 ) 

double dx1 = pt1->x - pt0->x; 
double dy1 = pt1->y - pt0->y; 
double dx2 = pt2->x - pt0->x; 
double dy2 = pt2->y - pt0->y; 
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10); 


// returns sequence of squares detected on the image. 
// the sequence is stored in the specified memory storage 
CvSeq* findSquares4( IplImage* img, CvMemStorage* storage ) 

CvSeq* contours; 
int i, c, l, N = 11; 
CvSize sz = cvSize( img->width & -2, img->height & -2 ); 
IplImage* timg = cvCloneImage( img ); // make a copy of input image 
IplImage* gray = cvCreateImage( sz, 8, 1 ); 
IplImage* pyr = cvCreateImage( cvSize(sz.width/2, sz.height/2), 8, 3 ); 
IplImage* tgray; 
CvSeq* result; 
double s, t; 
// create empty sequence that will contain points - 
// 4 points per square (the square's vertices) 
CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage ); 

// select the maximum ROI in the image 
// with the width and height divisible by 2 
cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height )); 

// down-scale and upscale the image to filter out the noise 
cvPyrDown( timg, pyr, 7 ); 
cvPyrUp( pyr, timg, 7 ); 
tgray = cvCreateImage( sz, 8, 1 ); 

// find squares in every color plane of the image 
for( c = 0; c < 3; c++ ) 

// extract the c-th color plane 
cvSetImageCOI( timg, c+1 ); 
cvCopy( timg, tgray, 0 ); 

// try several threshold levels 
for( l = 0; l < N; l++ ) 

// hack: use Canny instead of zero threshold level. 
// Canny helps to catch squares with gradient shading 
if( l == 0 ) 

// apply Canny. Take the upper threshold from slider 
// and set the lower to 0 (which forces edges merging) 
cvCanny( tgray, gray, 0, thresh, 5 ); 
// dilate canny output to remove potential 
// holes between edge segments 
cvDilate( gray, gray, 0, 1 ); 

else 

// apply threshold if l!=0: 
// tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0 
cvThreshold( tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY ); 


// find contours and store them all as a list 
cvFindContours( gray, storage, &contours, sizeof(CvContour), 
CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) ); 

// test each contour 
while( contours ) 

// approximate contour with accuracy proportional 
// to the contour perimeter 
result = cvApproxPoly( contours, sizeof(CvContour), storage, 
CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 ); 
// square contours should have 4 vertices after approximation 
// relatively large area (to filter out noisy contours) 
// and be convex. 
// Note: absolute value of an area is used because 
// area may be positive or negative - in accordance with the 
// contour orientation 
if( result->total == 4 && 
fabs(cvContourArea(result,CV_WHOLE_SEQ)) > 1000 && 
cvCheckContourConvexity(result) ) 

s = 0; 

for( i = 0; i < 5; i++ ) 

// find minimum angle between joint 
// edges (maximum of cosine) 
if( i >= 2 ) 

t = fabs(angle( 
(CvPoint*)cvGetSeqElem( result, i ), 
(CvPoint*)cvGetSeqElem( result, i-2 ), 
(CvPoint*)cvGetSeqElem( result, i-1 ))); 
s = s > t ? s : t; 



// if cosines of all angles are small 
// (all angles are ~90 degree) then write quandrange 
// vertices to resultant sequence 
if( s < 0.3 ) 
for( i = 0; i < 4; i++ ) 
cvSeqPush( squares, 
(CvPoint*)cvGetSeqElem( result, i )); 


// take the next contour 
contours = contours->h_next; 




// release all the temporary images 
cvReleaseImage( &gray ); 
cvReleaseImage( &pyr ); 
cvReleaseImage( &tgray ); 
cvReleaseImage( &timg ); 

return squares; 



// the function draws all the squares in the image 
void drawSquares( IplImage* img, CvSeq* squares ) 

CvSeqReader reader; 
IplImage* cpy = cvCloneImage( img ); 
int i; 

// initialize reader of the sequence 
cvStartReadSeq( squares, &reader, 0 ); 

// read 4 sequence elements at a time (all vertices of a square) 
for( i = 0; i < squares->total; i += 4 ) 

CvPoint pt[4], *rect = pt; 
int count = 4; 

// read 4 vertices 
CV_READ_SEQ_ELEM( pt[0], reader ); 
CV_READ_SEQ_ELEM( pt[1], reader ); 
CV_READ_SEQ_ELEM( pt[2], reader ); 
CV_READ_SEQ_ELEM( pt[3], reader ); 

// draw the square as a closed polyline 
cvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0 ); 


// show the resultant image 
cvShowImage( wndname, cpy ); 
cvReleaseImage( &cpy ); 



char* names[] = { "pic1.png", "pic2.png", "pic3.png", 
"pic4.png", "pic5.png", "pic6.png", 0 }; 

int main(int argc, char** argv) 

int i, c; 
// create memory storage that will contain all the dynamic data 
storage = cvCreateMemStorage(0); 

for( i = 0; names[i] != 0; i++ ) 

// load i-th image 
img0 = cvLoadImage( names[i], 1 ); 
if( !img0 ) 

printf("Couldn't load %s\n", names[i] ); 
continue; 

img = cvCloneImage( img0 ); 

// create window and a trackbar (slider) with parent "image" and set callback 
// (the slider regulates upper threshold, passed to Canny edge detector) 
cvNamedWindow( wndname, 1 ); 

// find and draw the squares 
drawSquares( img, findSquares4( img, storage ) ); 

// wait for key. 
// Also the function cvWaitKey takes care of event processing 
c = cvWaitKey(0); 
// release both images 
cvReleaseImage( &img ); 
cvReleaseImage( &img0 ); 
// clear memory storage - reset free space position 
cvClearMemStorage( storage ); 
if( (char)c == 27 ) 
break; 


cvDestroyWindow( wndname ); 

return 0; 
}




1~5 까지는 전에 설명했었으니 패스

6. cvFindCountours 는 영상에서 도형의 윤곽을 찾아내는 역할

   여기서 Mode를 잘 써야한다는데 난 그냥 예제대로 CV_RETR_LIST를 사용, 요놈은 도형의 외곽을 찾아 hierarchy구조 없이 List에 저장하는 모드.

   여튼 필요에 따라 이 모드를 잘 설정해야함


* cvFindContours


Finds contours in a binary image.


C++: void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())

C++: void findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int method, Point offset=Point())

Python: cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]]) → contours, hierarchy

C: int cvFindContours(CvArr* image, CvMemStorage* storage, CvSeq** first_contour, int header_size=sizeof(CvContour), int mode=CV_RETR_LIST, int method=CV_CHAIN_APPROX_SIMPLE, CvPoint offset=cvPoint(0,0) )

Python: cv.FindContours(image, storage, mode=CV_RETR_LIST, method=CV_CHAIN_APPROX_SIMPLE, offset=(0, 0)) → contours

Parameters:

image – Source, an 8-bit single-channel image. Non-zero pixels are treated as 1’s. Zero pixels remain 0’s, so the image is treated as binary . You can use compare() , inRange() , threshold() , adaptiveThreshold() , Canny() , and others to create a binary image out of a grayscale or color one. The function modifies the image while extracting the contours.

contours – Detected contours. Each contour is stored as a vector of points.

hierarchy – Optional output vector, containing information about the image topology. It has as many elements as the number of contours. For each i-th contour contours[i] , the elements hierarchy[i][0] , hiearchy[i][1] , hiearchy[i][2] , and hiearchy[i][3] are set to 0-based indices in contours of the next and previous contours at the same hierarchical level, the first child contour and the parent contour, respectively. If for the contour i there are no next, previous, parent, or nested contours, the corresponding elements of hierarchy[i] will be negative.

mode –

Contour retrieval mode (if you use Python see also a note below).


CV_RETR_EXTERNAL retrieves only the extreme outer contours. It sets hierarchy[i][2]=hierarchy[i][3]=-1 for all the contours.

CV_RETR_LIST retrieves all of the contours without establishing any hierarchical relationships.

CV_RETR_CCOMP retrieves all of the contours and organizes them into a two-level hierarchy. At the top level, there are external boundaries of the components. At the second level, there are boundaries of the holes. If there is another contour inside a hole of a connected component, it is still put at the top level.

CV_RETR_TREE retrieves all of the contours and reconstructs a full hierarchy of nested contours. This full hierarchy is built and shown in the OpenCV contours.c demo.

method –

Contour approximation method (if you use Python see also a note below).


CV_CHAIN_APPROX_NONE stores absolutely all the contour points. That is, any 2 subsequent points (x1,y1) and (x2,y2) of the contour will be either horizontal, vertical or diagonal neighbors, that is, max(abs(x1-x2),abs(y2-y1))==1.

CV_CHAIN_APPROX_SIMPLE compresses horizontal, vertical, and diagonal segments and leaves only their end points. For example, an up-right rectangular contour is encoded with 4 points.

CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS applies one of the flavors of the Teh-Chin chain approximation algorithm. See [TehChin89] for details.

offset – Optional offset by which every contour point is shifted. This is useful if the contours are extracted from the image ROI and then they should be analyzed in the whole image context.



7. cvApproxPoly는 위에  cvFindContours로 찾아낸 도형의 윤곽을 근사화 시키는데 결과값반환이 4이면 사각형 요놈을 이용해서 사각형을 판별한다.

여기서 4번째 파라미터는 오직 CV_POLY_APPROX_DP만 서포트 한다니 예제대로 CV_POLY_APPROX_DP 를 사용하고

5번째 파라미터 epsilon은 근사화 정밀도.. 역시 어떤값이 좋은지 잘 모르므로 예제대로 ^^;


cvApproxPoly

Approximates a polygonal curve(s) with the specified precision.


C++: void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed)

Python: cv2.approxPolyDP(curve, epsilon, closed[, approxCurve]) → approxCurve

C: CvSeq* cvApproxPoly(const void* src_seq, int header_size, CvMemStorage* storage, int method, double eps, int recursive=0 )

Parameters:

curve –

Input vector of a 2D point stored in:


std::vector or Mat (C++ interface)

Nx2 numpy array (Python interface)

CvSeq or `` CvMat (C interface)

approxCurve – Result of the approximation. The type should match the type of the input curve. In case of C interface the approximated curve is stored in the memory storage and pointer to it is returned.

epsilon – Parameter specifying the approximation accuracy. This is the maximum distance between the original curve and its approximation.

closed – If true, the approximated curve is closed (its first and last vertices are connected). Otherwise, it is not closed.

header_size – Header size of the approximated curve. Normally, sizeof(CvContour) is used.

storage – Memory storage where the approximated curve is stored.

method – Contour approximation algorithm. Only CV_POLY_APPROX_DP is supported.

recursive – Recursion flag. If it is non-zero and curve is CvSeq*, the function cvApproxPoly approximates all the contours accessible from curve by h_next and v_next links.

The functions approxPolyDP approximate a curve or a polygon with another curve/polygon with less vertices so that the distance between them is less or equal to the specified precision. It uses the Douglas-Peucker algorithm http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm


See https://github.com/Itseez/opencv/tree/master/samples/cpp/contours2.cpp for the function usage model.



http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=cvapproxpoly#CvSeq* cvApproxPoly(const void* src_seq, int header_size, CvMemStorage* storage, int method, double eps, int recursive)



8. 사실 OpenCV에 Moments 함수가 있어서 이를 이용하여 기울어진 이미지는 쉽게 판독이 가능하다는데 난 이미지 내의 도형의 기울기를 찾아내야 해서 안맞고

    뭔가 쉬운 방법이 있을 것 같은데  찾을수가 없었다. 그래서 그냥 무식한 방법으로 해결하기로 결정

   cvGetSeqElem을 이용해서 4개의 포인트를 찾고 이를 각각의 좌표로 변환

   x1 = pt1.x

   y1 = pt1.y

   이런식으로 4 포인트 각각의 x,y를 구하고


  4포인트간의 길이를 계산 pt1-pt2, pt2-pt3, pt3-pt4, pt4-pt1

  length = sqrt(pow(x1-x2,2) + pow(y1-y2,2)); <- 뭐 이거는 그냥 피타고라스 정리로 ^^;


  그 중 짧은 두 선분을 찾고 그에 해당하는 좌표를 기억


 

9. 사각형의 짧은 선분을 구성하는 두 포인트의 중간점을 계산

   그 두 중간점을 이용하여 각도를 계산

abase = px2 - px1;

aheight = py2 - py1;

angle = atan(aheight/abase);

angle_degree = angle*180.0/3.141592;


실행결과는 다음과 같이 잘 되었슴 ㅋ




왼쪽 위가 실제 촬영되고 있는 영상이고

빨간색 포인트는 검출한 사각형으 코너 포인트, 파란색 라인은 각 코너포인트간의 연결선으로 검출된 사각형을 그리고,

녹색선은 짧은 선의 중간점끼리 연결한 선

녹색선의 각도를 도형의 각도로 계산한다.


그 우측 Edge는 그 아래 이진화 한 영상의 Edge를 detect한 화면

속도도 초당 41fps 정도로 꽤 빠르다.


소스는 다음과 같다.

(좀 멍청하게 짜서 쓸데없이 중복되는거 많음 ㅋㅋㅋ 수정하기 귀찮아서 냅뒀슴)




//============================================================================

// Name        : 20140317_RECTANGLE2.cpp

// Author      : jslee

// Version     :

// Copyright   : Your copyright notice

// Description : Hello World in C++, Ansi-style

//============================================================================



#include <iostream>

#include <stdio.h>

#include <sys/time.h>

#include <math.h>

#include "opencv2/opencv.hpp"



struct timeval start_time;

struct timeval end_time;

double elapsed_time;

float fps;

double total_fps = 0.0,average_fps = 0.0;

long cnt=1;

int cx,cy,radius,ccnt;



using namespace std;



int main() {


IplImage* img;

IplImage* GrayImg=0;

IplImage* Edge=0;

IplImage* HSVImg;

IplImage* HImg = 0;

IplImage* SImg = 0;

IplImage* VImg = 0;


int key;

int i,k;

double dp = 1;

double min_dist = 300;

double t,s;

double angle12,angle23,angle34,angle41;

int px1,py1,px2,py2,sx1,sy1,sx2,sy2,sx3,sy3,sx4,sy4,spx1,spy1,spx2,spy2;

double len12,len23,len34,len41;

double longlen;

double abase, aheight;

char buffer[25];

CvFont font;

cvInitFont(&font,CV_FONT_HERSHEY_SIMPLEX,0.5,0.5);

CvSeq* result;

CvPoint* pt1;

CvPoint* pt2;

CvPoint* pt3;

CvPoint* pt4;



CvCapture* capture = cvCaptureFromCAM(0);

cvSetCaptureProperty( capture, CV_CAP_PROP_FRAME_WIDTH, 640);

cvSetCaptureProperty( capture, CV_CAP_PROP_FRAME_HEIGHT, 480);

cvSetCaptureProperty( capture, CV_CAP_PROP_FPS, 65);



while(1)

{

gettimeofday(&start_time,NULL);


cvGrabFrame(capture);

img = cvRetrieveFrame(capture);

GrayImg = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);

Edge = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);

cvCvtColor(img,GrayImg,CV_BGR2GRAY);


cvSmooth(GrayImg,GrayImg,CV_GAUSSIAN,9,9,2,2);

cvThreshold(GrayImg,GrayImg,200,255,CV_THRESH_BINARY);


cvCanny(GrayImg,Edge,25,200,3);


CvMemStorage* storage = cvCreateMemStorage(0);



CvSeq* Contours;

cvFindContours(Edge,storage,&Contours, sizeof(CvContour),CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE);

while(Contours)

{

result = cvApproxPoly(Contours,sizeof(CvContour),storage,CV_POLY_APPROX_DP,cvContourPerimeter(Contours)*0.02,0);


if(result->total == 4 && fabs(cvContourArea(result,CV_WHOLE_SEQ)) > 1000 && cvCheckContourConvexity(result))

{


pt1 = (CvPoint*)cvGetSeqElem(result,0);

pt2 = (CvPoint*)cvGetSeqElem(result,1);

pt3 = (CvPoint*)cvGetSeqElem(result,2);

pt4 = (CvPoint*)cvGetSeqElem(result,3);


int x1 = pt1->x;

int y1 = pt1->y;

int x2 = pt2->x;

int y2 = pt2->y;

int x3 = pt3->x;

int y3 = pt3->y;

int x4 = pt4->x;

int y4 = pt4->y;



cvCircle(img,cvPoint(x1,y1),3,CV_RGB(255,0,0),3,8,0);

cvCircle(img,cvPoint(x2,y2),3,CV_RGB(255,0,0),3,8,0);

cvCircle(img,cvPoint(x3,y3),3,CV_RGB(255,0,0),3,8,0);

cvCircle(img,cvPoint(x4,y4),3,CV_RGB(255,0,0),3,8,0);


cvLine(img,cvPoint(x1,y1),cvPoint(x2,y2),CV_RGB(0,0,255),2,8,0);

cvLine(img,cvPoint(x2,y2),cvPoint(x3,y3),CV_RGB(0,0,255),2,8,0);

cvLine(img,cvPoint(x3,y3),cvPoint(x4,y4),CV_RGB(0,0,255),2,8,0);

cvLine(img,cvPoint(x4,y4),cvPoint(x1,y1),CV_RGB(0,0,255),2,8,0);


//calculate angle

//calculate length of two point

len12 = sqrt(pow(x1-x2,2) + pow(y1-y2,2));

len23 = sqrt(pow(x2-x3,2) + pow(y2-y3,2));

len34 = sqrt(pow(x3-x4,2) + pow(y3-y4,2));

len41 = sqrt(pow(x4-x1,2) + pow(y4-y1,2));


//sort len

longlen = len12;

px1 = x1;

py1 = y1;

px2 = x2;

py2 = y2;


sx1 = x2;

sy1 = y2;

sx2 = x3;

sy2 = y3;

sx3 = x4;

sy3 = y4;

sx4 = x1;

sy4 = y1;



if(len23 > longlen)

{

longlen = len23;

px1 = x2;

py1 = y2;

px2 = x3;

py2 = y3;


sx1 = x3;

sy1 = y3;

sx2 = x4;

sy2 = y4;

sx3 = x1;

sy3 = y1;

sx4 = x2;

sy4 = y2;

}


if(len34 > longlen)

{

longlen = len34;

px1 = x3;

py1 = y3;

px2 = x4;

py2 = y4;


sx1 = x4;

sy1 = y4;

sx2 = x1;

sy2 = y1;

sx3 = x2;

sy3 = y2;

sx4 = x3;

sy4 = y3;

}


if(len41 > longlen)

{

longlen = len41;

px1 = x1;

py1 = y1;

px2 = x4;

py2 = y4;


sx1 = x1;

sy1 = y1;

sx2 = x2;

sy2 = y2;

sx3 = x3;

sy3 = y3;

sx4 = x4;

sy4 = y4;

}


spx1 = (sx1 + sx2) / 2;

spy1 = (sy1 + sy2) / 2;

spx2 = (sx3 + sx4) / 2;

spy2 = (sy3 + sy4) / 2;

cvLine(img,cvPoint(spx1,spy1),cvPoint(spx2,spy2),CV_RGB(0,255,0),2,8,0);

px1 = spx1;

py1 = spy1;

px2 = spx2;

py2 = spy2;

//find min x -> point2

//point 1 - point2

abase = px2 - px1;

aheight = py2 - py1;

angle23 = atan(aheight/abase);

angle12 = angle23*180.0/3.141592;

sprintf(buffer,"ang= %02.2f",angle12);

cvPutText(img,buffer,cvPoint(abs((px2+px1)/2),abs((py2+py1)/2)),&font,CV_RGB(255,0,0));





}

Contours = Contours->h_next;

}



cvNamedWindow( "PreviewImage", 1 );

cvShowImage( "PreviewImage", img );

cvMoveWindow("PreviewImage",200,100);


cvNamedWindow( "GrayImage", 1 );

cvShowImage( "GrayImage", GrayImg );

cvMoveWindow("GrayImage",850,600);


cvNamedWindow( "Edge", 1 );

cvShowImage( "Edge", Edge );

cvMoveWindow("Edge",850,100);


key = cvWaitKey(1);

if(key > 10) break;


gettimeofday(&end_time, NULL);



elapsed_time = (double)(end_time.tv_sec) + (double)(end_time.tv_usec)/1000000.0 - (double)(start_time.tv_sec) - (double)(start_time.tv_usec)/1000000.0;

fps = 1.0 / elapsed_time;

total_fps += fps;

average_fps = total_fps / (double)(cnt);

cnt++;

if(cnt == 100)

{

cnt = 1;

total_fps = 0.0;

}

elapsed_time *= 1000.0;


printf("Elapsed T = %2.3f ms, Frame = %2.3f (fps), Avrg Frame Rate = %2.3f count = %d\n",elapsed_time,fps,average_fps,result->total);

//Circles->total = 0;

cvReleaseImage(&GrayImg);

cvReleaseImage(&Edge);

cvReleaseMemStorage(&storage);




} //end of while(1)

cvReleaseImage(&img);

cvReleaseCapture( &capture );


return 0;

}





http://blog.naver.com/PostView.nhn?blogId=budlbaram&logNo=50169171521 를 참고. (감사합니다)


1. OpenCV를 위한 라이브러리 설치

 터미널 열고 (원본에는 한꺼번에 타이핑 하는 것으로 나와있는데 혹시 모를 오류를 대비하여 난 하나씩 설치하였다.)


sudo apt-get install cmake

sudo apt-get install libtbb2

sudo apt-get install libtbb-dev

sudo apt-get install libgtk2.0-dev

sudo apt-get install libjpeg62-dev

sudo apt-get install libjasper-dev

sudo apt-get install libtiff4-dev

sudo apt-get install ffmpeg

sudo apt-get install libavformat-dev

sudo apt-get install libswscale-dev

sudo apt-get install libgstreamer0.10-dev

sudo apt-get install libgstreamermm-0.10-dev

sudo apt-get install libdc1394-22-dev

sudo apt-get install libv4l-dev

sudo apt-get install libopenexr-dev

sudo apt-get install libxine-dev

sudo apt-get install libunicap2-dev

sudo apt-get install libucil2-dev


별문제 없이 설치 되는 걸로 보아 그냥 이렇게 해도 된다. (나도 참 무식하다 ㅋ)


sudo apt-get install cmake libtbb2 libtbb-dev  libgtk2.0-dev libjpeg62-dev libjasper-dev libtiff4-dev ffmpeg libavformat-dev libswscale-dev libgstreamer0.10-dev 

libgstreamermm-0.10-dev libdc1394-22-dev libv4l-dev libopenexr-dev libxine-dev libunicap2-dev libucil2-dev


2. OpenCV 2.4.8 (현재) 설치


 http://opencv.org/downloads.html 에 가서 OpenCV2.4.8 for Linux/Mac 클릭하면 OpenCV 다운이 시작된다.


3. 압축을 풀고 폴더 이동

 opencv-2.4.8.zip 을 압축을 풀고 다운로드 폴더에 받았으므로 /home으로 이동


4. 압축을 풀어 놓은 opencv-2.4.8 폴더에 make를 위한 release 폴더 생성

mkdir release


5. cmake


sudo cmake -D BUILD_EXAMPLES=ON -D INSTALL_C_EXAMPLES=ON -D BUILD_TESTS=ON -D WITH_TBB=ON -D WITH_UNICAP=ON -D WITH_XINE=ON ../

(맨끝에 ../ 는 압축을 풀어놓은 디렉토리. 현재 opencv-2.4.8 폴더 안에 release 폴더를 만들었으므로 ../ 으로 씀


 시간 많이 걸린다. ^^;;;


6. OpenCV 설치

sudo make install


7. 라이브러리 링크 및 패키지 확인


라이브러리 링크

sudo ldconfig


패키지 확인

pkg-config --cflags opencv

pkg-config --libs opencv


다음과 같은 결과가 나온다




8. 이클립스 설치

http://www.eclipse.org/downloads/packages/eclipse-ide-cc-developers/junosr2

위에서 리눅스32bit용 다운로드 (현재 Juno SR2 버전)


다운받으면 압축풀면 됨


9. java 설치

sudo apt-get purge openjdk

sudo add-apt-repository ppa:webupd8team/java

sudo apt-get update

sudo apt get install oracle-java7-installer


여기서 openjdk는 안깔려있어서 없는 package라고 나오는 것 같고

마지막 oracle-java7-installer 실행하면 무슨 license 어쩌구 binary 어쩌구 code 어쩌구 에서 accept 해줘야 되는듯하다.

(귀찮아서 자세히 안읽어봄... 봐도 모름 ㅋ)


10. 이클립스 바로가기

 텍스트 편집기를 이용해서 /usr/share/applications/eclipse.desktop 파일 생성

sudo gedit /usr/share/applications/eclipse.desktop

그리고 다음의 내용 복사해서 붙여넣기


[Desktop Entry]

Encoding=UTF-8

Name=Eclipse

Comment=Eclipse IDE

Exec=/home/jslee/eclipse/eclipse

Icon=/home/jslee/eclipse/icon.xpm

Terminal=false

Type=Application

Categories=GNOME;Application;Development;

StartupNotify=true


난 /home/jslee/ 에 eclipse 압축을 풀어놓았으므로 위의 경로로 썼다.


11. OpenCV 테스트


 1) 프로젝트 만들기

    File / New / C++ Project 하면



일단 Project Name을 작성하고 (여기서는 OpenCV_Test)

Project Type: Executable / Hello World C++ Project

Toolchains : Linux GCC

그리고 Finish하면 다음과 같이 기본코드가 생성된다.


 2) OpenCV Header 및 Library 



 위 그림에서와 같이 Project Explorer 에서 프로젝트를 선택하고 우측 마우스 버튼 클릭 / Properties 를 선택한다.




  위 그림과 같이 C/C++ Builder 에서 Settings 를 선택하고

  GCC C++ Compiler에서 Includes를 선택한 뒤 OpenCV Header가 있는 /usr/local/include 의 경로를 넣어준다.



  마찬가지로   위 그림과 같이 C/C++ Builder 에서 Settings 를 선택하고

  GCC C++ Linker에서 Libraries를 선택한 뒤 

  Library 에는 사용할 Library 이름 opencv_core, opencv_highgui 를 입력

  그리고 Library search path에는 OpenCV library가 있는 /usr/local/lib 의 경로를 넣어준다.


  3) 소스 작성

   OpenCV_Test.cpp를 아래와 같이 작성하고 build 후 실행하면 아래의 화면과 같이 test.png를 opencv를 이용하여 화면에 출력한다.


 * test.png가 프로젝트 폴더 안에 있어야 한다.

 ** jpg는 안되더라. 처음에 test.jpg로 했는데 안되서 png로 했더니 된다. 이유는 모르겠지만 opencv의 cvLoadImage()에서 jpg는 load 하지 못하는것 같다.





  







우선 제일 먼저 해본게 원을 검출하는 일이다. 

(이게 실제 내가 나중에 가장 중점적으로 해야할 일 중 하나이기도 해서 ^^;;)


기본적으로 다음과 같이 프로그래밍 하면 정석이지 않을까 싶어서 프로그래밍 시작.

 1. 영상 획득 

 2. 획득한 영상을 흑백영상으로 변환 ( cvCvtColor 이용)

 3. 흑백영상을 이진화 (cvThreshold 이용)

 4. Edge 검출 (cvCanny 이용)

 5. 원 검출 (cvHoughCircles 이용)

 6. 검출한 원을 중심점 및 반지름을 구해서 Window에 표시


이러면 될 것 같아서 짠 프로그램의 결과는 다음과 같았슴




 오오 잘된다!!! 그런데 60fps으로 입력받던 카메라 영상이 영상 처리하느라고 20프레임까지 떨어져 버림 ㅠㅜ

여튼 위에서 언급한 함수들을 적용하는데 파라미터 설정의 삽질이 좀 있었다. ㅠㅜ

그걸 좀 적어두자면


1. 흑백영상으로 변환하기 위해 GrayImg라고 Iplimage를 선언하고 cvCreateImage를 사용

C: IplImage* cvCreateImage(CvSize size, int depth, int channels)

Parameters:

size – Image width and height

depth – Bit depth of image elements. See IplImage for valid depths.

channels – Number of channels per pixel. See IplImage for details. This function only creates images with interleaved channels.


link:

http://docs.opencv.org/modules/core/doc/old_basic_structures.html?highlight=cvcreateimage#IplImage* cvCreateImage(CvSize size, int depth, int channels)


 여기서 첫번째 parameter size는 획득한 영상을 이용 cvGetSize()로 잘 얻어왔고, depth도 예제대로 IPL_DEPTH_8U(unsigned 8bit integer)로 문제 없었다.

문제는 channels parameter, 예제에 3으로 되어있어서 3으로 두었더니 


 what():  /home/ubuntu/release/opencv-2.4.8/modules/imgproc/src/color.cpp:4422: error: (-215) dst.data == dst0.data in function cvCvtColor


위와 같은 Run-time 에러가 발생한다. 대체 이게 뭔 문제가 싶어서 뒤적여본 결과 CreateImage를 할 때 3 channel로 RGB 컬러영상으로 만들고 나중에 cvCvtcolor로 영상변환을 하려고 하니

메모리가 다른거지 뭐 --;;;

그래서 Channel을 1 (흑백영상)로 바꾸고 해결


2. 영상을 흑백으로 변환

Converts an image from one color space to another.


C++: void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0 )

Python: cv2.cvtColor(src, code[, dst[, dstCn]]) → dst

C: void cvCvtColor(const CvArr* src, CvArr* dst, int code)

Python: cv.CvtColor(src, dst, code) → None

Parameters:

src – input image: 8-bit unsigned, 16-bit unsigned ( CV_16UC... ), or single-precision floating-point.

dst – output image of the same size and depth as src.

code – color space conversion code (see the description below).

dstCn – number of channels in the destination image; if the parameter is 0, the number of the channels is derived automatically from src and code ..


link:

http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html?highlight=cvcvtcolor#void cvCvtColor(const CvArr* src, CvArr* dst, int code)


cvCvtColor함수를 이용하는데 이건 뭐 예제대로 별 무리 없이 되었슴. 
흑백으로 변환 할 때 code parameter에 CV_BGR2GRAY 로 하면 지가 알아서 흑백으로 변환, 마지막 파라미터는 안씀

3. 이진화 
Applies a fixed-level threshold to each array element.

C++: double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)
Python: cv2.threshold(src, thresh, maxval, type[, dst]) → retval, dst
C: double cvThreshold(const CvArr* src, CvArr* dst, double threshold, double max_value, int threshold_type)
Python: cv.Threshold(src, dst, threshold, maxValue, thresholdType) → None
Parameters:
src – input array (single-channel, 8-bit or 32-bit floating point).
dst – output array of the same size and type as src.
thresh – threshold value.
maxval – maximum value to use with the THRESH_BINARY and THRESH_BINARY_INV thresholding types.
type – thresholding type (see the details below).

link:
http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html?highlight=cvthreshold#double cvThreshold(const CvArr* src, CvArr* dst, double threshold, double max_value, int threshold_type)
 
cvThreshold()를 이용  여기서는 Thresh parameter를 잘 선택해야함. 
type을 CV_THRESH_BINARY로 선택했기 때문에 임계값 초과는 255(white), 이하는 0(black)으로 만들기 때문이다.
적정한 값 대입해서 테스트 환경에 맞게 선택했슴

4. cvCanny를 이용하여 Edge 검출
Finds edges in an image using the [Canny86] algorithm.

C++: void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false )
Python: cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]]) → edges
C: void cvCanny(const CvArr* image, CvArr* edges, double threshold1, double threshold2, int aperture_size=3 )
Python: cv.Canny(image, edges, threshold1, threshold2, aperture_size=3) → None¶
Parameters:
image – single-channel 8-bit input image.
edges – output edge map; it has the same size and type as image .
threshold1 – first threshold for the hysteresis procedure.
threshold2 – second threshold for the hysteresis procedure.
apertureSize – aperture size for the Sobel() operator.
L2gradient – a flag, indicating whether a more accurate  L_2 norm  =\sqrt{(dI/dx)^2 + (dI/dy)^2} should be used to calculate the image gradient magnitude ( L2gradient=true ), or whether the default  L_1 norm =|dI/dx|+|dI/dy| is enough ( L2gradient=false ).

link:
http://docs.opencv.org/modules/imgproc/doc/feature_detection.html?highlight=cvcanny#void cvCanny(const CvArr* image, CvArr* edges, double threshold1, double threshold2, int aperture_size)

사실 cvCanny는 Edge Detector라는 것만 알고 Parameter의 사용법에 대해서 잘 몰랐슴..
역시 여기서도 중요한게 3,4번 parameter threshold1과 threshold2  5번 파라미터는 Edge의 두께 정도로 생각하면 될 듯.
여튼 threshold1과 threshold2의 설명을 읽어봐도 모르겠고 해서 뒤적였더니 각각은 다음과 같은 의미를 가지고 있는듯
threshold1 = low threshold
threshold2 = high threshold

 Edge를 검출함에 있어서 어두운 영역의 임계값과 밝은 영역의 임계값을 결정 짓는 요소인듯하다.
불확실한데 값들을 변경시켜가며 한 결과는 그런듯 ㅋ

5. cvHoughCircles를 이용하여 원 검출
Finds circles in a grayscale image using the Hough transform.

C++: void HoughCircles(InputArray image, OutputArray circles, int method, double dp, double minDist, double param1=100, double param2=100, int minRadius=0, int maxRadius=0 )
C: CvSeq* cvHoughCircles(CvArr* image, void* circle_storage, int method, double dp, double min_dist, double param1=100, double param2=100, int min_radius=0, int max_radius=0 )

Parameters:
image – 8-bit, single-channel, grayscale input image.
circles – Output vector of found circles. Each vector is encoded as a 3-element floating-point vector (x, y, radius) .
circle_storage – In C function this is a memory storage that will contain the output sequence of found circles.
method – Detection method to use. Currently, the only implemented method is CV_HOUGH_GRADIENT , which is basically 21HT , described in [Yuen90].
dp – Inverse ratio of the accumulator resolution to the image resolution. For example, if dp=1 , the accumulator has the same resolution as the input image. If dp=2 , the accumulator has half as big width and height.
minDist – Minimum distance between the centers of the detected circles. If the parameter is too small, multiple neighbor circles may be falsely detected in addition to a true one. If it is too large, some circles may be missed.
param1 – First method-specific parameter. In case of CV_HOUGH_GRADIENT , it is the higher threshold of the two passed to the Canny() edge detector (the lower one is twice smaller).
param2 – Second method-specific parameter. In case of CV_HOUGH_GRADIENT , it is the accumulator threshold for the circle centers at the detection stage. The smaller it is, the more false circles may be detected. Circles, corresponding to the larger accumulator values, will be returned first.
minRadius – Minimum circle radius.
maxRadius – Maximum circle radius.

link
http://docs.opencv.org/modules/imgproc/doc/feature_detection.html?highlight=cvhoughcircle#CvSeq* cvHoughCircles(CvArr* image, void* circle_storage, int method, double dp, double min_dist, double param1, double param2, int min_radius, int max_radius)

 여기서 dp 1이면 입력해상도와 동일한 해상도, 2면 절반이라는데 잘 모르겠으니 1만 썼다 ㅋ
minDist는 검출된 원의 중심사이의 거리를 설정하는 듯, 즉 이게 너무 좁으면 가까운 영역에서 많은 원들이 검출 될 수 있기에 적당히 조절할 필요가 있다.
minRadius 는 검출할 원의 최소 반지름, maxRadius는 검출할 원의 최대 반지름 <= 요넘들의 범위를 좁히면 cvHoughCircles()의 수행속도가 빨라진다. 그러므로 필요에 맞게 범위 조절할 것
여기서도 잘 모르겠는게 param1과 param2
param1은 Canny Edge Detector의 Threshold 값이라고 하고
param2는 accumulator 임계값이라는데 뭔소린지 모르겠슴.. ㅠㅜ (기본이 안되어 있다보니 이런게 어렵다 ㅠㅜ)
여튼 요놈들도 상황에 맞게 조절했슴 ㅋ


이렇게해서 프로그래밍한 소스는 다음과 같다.
//============================================================================
// Name        : OpenCVTest3.cpp
// Author      : jslee
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <stdio.h>
#include <sys/time.h>

#include "opencv2/opencv.hpp"

struct timeval start_time;
struct timeval end_time;
double elapsed_time;
float fps;
double total_fps = 0.0,average_fps = 0.0;
long cnt=1;



using namespace std;
int main() {

IplImage* img;
IplImage* GrayImg=0;
IplImage* Edge=0;

int key;
int k;
double dp = 1;
double min_dist = 100;



CvCapture* capture = cvCaptureFromCAM(0);
cvSetCaptureProperty( capture, CV_CAP_PROP_FRAME_WIDTH, 640);
cvSetCaptureProperty( capture, CV_CAP_PROP_FRAME_HEIGHT, 480);
cvSetCaptureProperty( capture, CV_CAP_PROP_FPS, 60);


while(1)
{
gettimeofday(&start_time,NULL);

cvGrabFrame(capture);
img = cvRetrieveFrame(capture);

GrayImg = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
cvCvtColor(img,GrayImg,CV_BGR2GRAY);
cvThreshold(GrayImg,GrayImg,200,255,CV_THRESH_BINARY);

Edge = cvCreateImage(cvGetSize(GrayImg),IPL_DEPTH_8U,CV_THRESH_BINARY);

cvCanny(GrayImg,Edge,25,200,3);

CvMemStorage* storage = cvCreateMemStorage(0);

CvSeq* Circles = cvHoughCircles(Edge,storage,CV_HOUGH_GRADIENT,dp,min_dist,200,25,30,50);

for(k=0;k<Circles->total;k++)
{
float* circle;
int cx,cy,radius;

circle = (float*)cvGetSeqElem(Circles,k);
cvCircle(img,cvPoint(cvRound(circle[0]),cvRound(circle[1])),cvRound(circle[2]),CV_RGB(255,0,0),3,8,0);

}

cvNamedWindow( "PreviewImage", 1 );
cvShowImage( "PreviewImage", img );
cvNamedWindow( "EdgeImage", 1 );
cvShowImage( "EdgeImage", Edge );
key = cvWaitKey(1);
if(key > 10) break;

gettimeofday(&end_time, NULL);


elapsed_time = (double)(end_time.tv_sec) + (double)(end_time.tv_usec)/1000000.0 - (double)(start_time.tv_sec) - (double)(start_time.tv_usec)/1000000.0;
fps = 1.0 / elapsed_time;
total_fps += fps;
average_fps = total_fps / (double)(cnt);
cnt++;
if(cnt == 100)
{
cnt = 1;
total_fps = 60.0;
}
elapsed_time *= 1000.0;

printf("Elapsed Time = %2.3f ms, Frame Rate = %2.3f (fps), Average Frame Rate = %2.3f \n",elapsed_time,fps,average_fps);



} //end of while(1)

cvReleaseCapture( &capture );

return 0;
}



그런데 parameter 들을 찾고 적용하면서 cvHoughCircles()의 parameter를 보고 문득 든 생각이....
 cvHoughCircles의 canny threshold가 있는 것을 보니 cvHoughCircles 함수 내부적으로 Edge Detect를 하는 것은 아닌가 하는 생각이 들었다.
결정적인 이유가 cvCanny 함수의 임계값과 cvHoughCircles에서 canny threshold를 잘못 적용하면 원을 검출 못하는 결과가 발생해서였다.
그리고 인터넷에 있던 예제들 중에도 별다른 과정 없이 cvHoughCircles만 사용하는 것들이 있었다.
그렇다면 실행속도도 단축시킬겸 한번 해보기로!!



 실행결과 잘 된다!!! 그런데 실행속도의 향상은 없다고 봐야겠다. T.T
 그리고 흑백처리를 하지 않으면 Run-Time Error가 발생해서 그거 찾기보단 일단 흑백변환은 해서 적용했다.
기존에 사용했던 cvThreshold(), cvCanny() 삭제하고 cvHoughCircles의 param2만 값을 50으로 바꿔서 수행했다.

자.. 이제 실행속도는 어떻게 향상시킬수 있을 지 고민 좀 해봐야겠다.



















 현재 Ubuntu 12.04에 OpenCV 설치해서 영상처리 프로그래밍 중임.


 640 * 480 해상도에 60fps 의 카메라가 필요해서 찾던 중 좋은 정보 발견!!!


 Sony PlayStation Eye가 USB Interface 임에도 640*480 (60fps), 320 * 240 (120fps) 임 가격도 44,000원 정도로 저렴!!


 여튼 이놈이 실제 60fps가 입력되는지 확인 할 필요가 있슴


 윈도우7에 Visual Studio2010 + OpenCV 에서는 확인 하였슴!! (오~ 조으네)


 여튼 실제 작업할 리눅스 환경에서 드라이버 문제라던지 있을 수 있으므로 동작 및 사양 확인할 필요가 있어서


1. 우선 영상을 녹화 -> 재생하여 해상도 및 프레임 확인 

  1) 가장 널리 알려진 웹캠 프로그램 Cheese에서 영상 녹화 및 확인 결과 640*480에 25fps 로 Fail !! (자동 설정된 Driver)

  2) lsusb 명령으로 현재 USB 장치 확인


       일단 lsusb로 확인한 USB에 연결된 Eye는 Playstation Eye로 확인 된다.

  3) 여기서 Driver 버전 확인, 터미널 열고 modinfo gspca-ov534 입력 

     

 

  3) 확인 결과 gspca/ov534 Driver 잘 잡힘, 즉 Cheese가 걍 25frame으로 녹화한 거임

  4) 다른 프로그램 없나 뒤적뒤적 -> 우분투 소프트웨어 관리자에서 cam으로 검색 camorama webcam viewer 라는 프로그램이 보임 -> 설치

  5) camorama webcam viewer 실행




    오오 하단을 보니 현재 설정 60fps , 평균 54.19fps 잘 된다!!! cheese가 역시 문제였슴



 2. 카메라의 인식 및 동작은 잘 되는 것으로 보이므로 프로그램을 직접 짜서 확인!




  하단 printf 된 콘솔을 확인해보니 프레임 잡아서 뿌리는데 걸리는 시간 그리고 이를 이용해 계산한 Frame Rate, 그리고 평균 Frame Rate를 보니 잘 되고 있다.

  여기서 cvWaitKey(1)의 사용으로 1ms 의 의미없는 지연시간이 들어간다. 

  실제 영상처리 연산을 하자면 60 fps 영상처리는 쉽지 않아 보이긴 한다 --;;


 3. 확인을 위하여 카메라를 30 fps로 설정하고 프로그램을 동작


  

   정확히 동작하고 있다.


 4. 동작을 확인한 소스코드는 다음과 같다. 


 

//============================================================================

// Name        : OpenCVTest3.cpp

// Author      : jslee

// Version     :

// Copyright   : Your copyright notice

// Description : Hello World in C++, Ansi-style

//============================================================================


#include <iostream>

#include <stdio.h>

#include <sys/time.h>


#include "opencv2/opencv.hpp"

struct timeval start_time;

struct timeval end_time;

double elapsed_time;

float fps;

double total_fps = 0.0,average_fps = 0.0;

long cnt=1;




using namespace std;

int main() {


IplImage* img = 0;

int key;


CvCapture* capture = cvCaptureFromCAM(0);

cvSetCaptureProperty( capture, CV_CAP_PROP_FRAME_WIDTH, 640);

cvSetCaptureProperty( capture, CV_CAP_PROP_FRAME_HEIGHT, 480);

cvSetCaptureProperty( capture, CV_CAP_PROP_FPS, 30);


while(1)

{

gettimeofday(&start_time,NULL);


cvGrabFrame(capture);

img = cvRetrieveFrame(capture);


cvNamedWindow( "PreviewImage", 1 );

cvShowImage( "PreviewImage", img );


key = cvWaitKey(1);

if(key > 10) break;

//end_time = clock();

gettimeofday(&end_time, NULL);


// elapsed_time = (double)(end_time-start_time) / CLOCKS_PER_SEC;

elapsed_time = (double)(end_time.tv_sec) + (double)(end_time.tv_usec)/1000000.0 - (double)(start_time.tv_sec) - (double)(start_time.tv_usec)/1000000.0;

fps = 1.0 / elapsed_time;

total_fps += fps;

average_fps = total_fps / (double)(cnt);

cnt++;

elapsed_time *= 1000.0;


printf("Elapsed Time = %2.3f ms, Frame Rate = %2.3f (fps), Average Frame Rate = %2.3f \n",elapsed_time,fps,average_fps);




} //end of while(1)


cvReleaseCapture( &capture );

//cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!

return 0;

}


 음.. 일단 오늘 작업은 이걸로 마무리.

 그러고보니 리눅스에서 OpenCV 설치에 관해 안적은듯.. 그건 내일 할수도 있고... 바쁘면 안할수도 있고 ㅋ

+ Recent posts