검색결과 리스트
분류 전체보기에 해당되는 글 183건
- 2015.04.05 움직임 추적(motion tracking)
- 2015.04.05 집중력을 높여주는 백색 소음
- 2015.04.05 15년(하) SW멤버십 및 Friendship
- 2015.04.05 옵티컬 플로우 Optical Flow -3
- 2015.04.05 옵티컬 플로우 Optical Flow -2
- 2015.04.04 옵티컬 플로우 Optical Flow -1
- 2015.04.04 영상 정합(image patch similarity)
- 2015.04.03 해리스 코너(Harris Corner) 2
- 2015.04.03 직선 검출 허프 변환
- 2015.02.27 에이다부스트(adaboost)
글
움직임 추적(motion tracking)
움직임 추적에서 보통 많이 사용하는 mean-shift와 cam-shift에 대해 정리해본다.
1. mean-shift(민시프트)
mean-shift 알고리즘은 데이터 집합의 밀도 분포에서 지역 극값(local extrema)을 찾아내는 방법이다.
언덕오르기 알고리즘과 비슷한 방법으로 생각할 수 있다.
데이터 집합이 있을때 보통 mean-shift는 데이터의 이상치(outlier)를 무시한다. 쉽게 말하면 데이터의
정점(peak)으로부터 너무 멀리 떨어진 데이터는 무시한다는 것을 의미한다.
따라서 특정 지역내의 윈도우 안에 존재하는 데이터 점들만을 사용하여 윈도우를 움직인다.
mean-shift 알고리즘은 다음과 같이 동작한다.
1. 탐색 윈도우내의 데이터들로 무게중심(center of mass)를 구한다. 2. 윈도우의 중심을 무게 중심 위치로 옮긴다. 3. 윈도우가 움직임을 멈출 때까지 2번 단계부터 반복한다. |
보통 윈도우 내의 데이터들로 부터 무게중심을 구할때 가우시안 분포(Gaussian distribution)을 사용한다.
보통 중심으로 부터 가까이 있는 데이터에 가중치를 좀더 높여 주는 방식이 좋다.
위 그림은 mean-shift 알고리즘의 이동 분포를 표현한 것이다. 사진을 보면 데이터의 중심(많은 픽셀이 분포한 점)
으로 윈도우가 이동하는것을 확인할 수 있다.
mean-shift알고리즘은 지역적 정점을 찾기 때문에 여러 지점에서 정점이 발견 될 수도 있다.
이런 움직임으로 인하여 윈도우 아래 존재하는 데이터들이 바뀌게 되고, 반복적으로 중심 이동 작업을 수행할
수행할 수 있다. 중심 이동이 반복되면 mean-shift 벡터가 0이 되는 경우가 생기면 이때 수렴하는 순간이라고
판단하면 된다.
OpenCV는 mean-shift 함수를 제공한다.
1 | int meanShift(InputArray probImage, Rect& window, TermCriteria criteria) | cs |
probImage는 역투영 히스토그램 이미지를 입력으로 받는다. calcBackProject()함수를 이용하면 된다.
window는 커널 윈도우 초기위치, 크기를 나타낸다. criteria는 반복연산의 종료조건을 입력하면 된다.
2. camshift(캠시프트)
[Bradski98] Bradski, Gary R. "Computer vision face tracking for use in a perceptual user interface." (1998).
예를 들어 얼굴 특징을 이용하여 추적하는 경우, 카메라로부터 사람이 가까워지거나 멀어짐에
따라 얼굴 크기에 맞게 자동으로 윈도우 크기를 변화시킨다.
1 | RotatedRect CamShift(InputArray probImage, Rect& window, TermCriteria criteria) | cs |
mean shift와 cam shift 알고리즘은 색상 특징값을 이용하는 추적 알고리즘이라고 생각하기 쉬운데,
이 생각은 잘못된 생각일 수 있다. 이 두 알고리즘은 확률적 분포에서 표현되는 모든 종류의 특징 분포를 추적한다.
'컴퓨터비전/영상처리' 카테고리의 다른 글
descriptor(기술자)란 무엇인가? (0) | 2015.04.15 |
---|---|
칼만 필터(Kalman Filter) (0) | 2015.04.06 |
옵티컬 플로우 Optical Flow -3 (0) | 2015.04.05 |
옵티컬 플로우 Optical Flow -2 (0) | 2015.04.05 |
옵티컬 플로우 Optical Flow -1 (0) | 2015.04.04 |
설정
트랙백
댓글
글
집중력을 높여주는 백색 소음
백색소음은 주변의 소음을 중화시켜 차단하고, 심신에 안정을 준다고 알려져 있다. 한국산업심리학회의 연구 결과에 따르면 백색소음은 집중력을 47.7% 향상시키며, 기억력을 9.6% 높여준다. 이와 함께 스트레스를 27.1% 낮춰준다. 이런 원리를 이용한 학습 보조 기기도 있다. 이런 기기를 통해 효과를 봤다는 사람도 있으니, 백색소음의 효과에 관한 이야기는 전혀 허황된 것은 아닌 듯하다.
1. RAINY MOOD
소나기가 오는 소리를 들을 수 있다. 가끔 천둥 소리도 들린다. 실제로 비가 오고 있는 듯한 착각을 불러 일으킨다.
2. Jazz and Rain
벽난로에서 나무가 타는 소리와 함께 음악이 재생된다. 음악을 듣고 싶지 않다면 끌 수도 있고 다른 음악을 선택할 수도 있다.
7. AUGUST AMBIENCE
8.SNOWY MOOD
눈을 밟으며 걷는 소리
설정
트랙백
댓글
글
15년(하) SW멤버십 및 Friendship
[삼성전자 소프트웨어멤버십 회원 선발 공고] 삼성소프트웨어멤버십에서 2015년도 하반기 신입회원을 선발 합니다. 1. 모집 요강 □ 모집대상 : IT분야 연구개발에 ‘재능’과 ‘열정’있는 국내 정규 4년제 대학(원)생 □ 해당지역 : 서울, 수원, 대전, 대구, 부산, 광주, 전주 □ 접수방법 : 온라인 접수( www.secmem.org ) □ 모집일정 * 서류접수 : 2015. 4. 29 ~ 2015. 5. 12 / 오후 3시까지 * 기술면접 : 2015. 5. 26 ~ 약 2주 * 결과발표 : 2015. 6월 중 예정 ※ 선발 된 지원자를 대상으로 S/W CAMP 운영 (기술 전형 합격자들에게 S/W Camp 교육 프로그램 제공 / 이수 후 평가를 통해 정회원 자격을 부여함) ※ 2016년 상반기 서류접수/기술전형 : 2015년 11월 예정 2. 지원 분야 □ 분 야 : S/W 및 H/W 전 분야(Application / Middleware / System / Robotics / Etc.) 3. 지원 자격 □ IT 연구개발에 재능과 열정이 있는 자 □ 정규 4년제 대학(원)생 (1~4학년, 석사) □ 전공 학과 불문 □ 국내외 공모전 수상자 우대 □ 대학 졸업 전 1년 이상 회원 활동이 가능한 자(대학 졸업과 동시에 수료) 4. 선발 인원 : 약 ○○○명 5. 회원 혜택 □ 연구개발 활동 및 환경 지원 □ 회원 활동 수료 후 입사 희망 시 SSAT 면제 ※ 보다 자세한 사항은 홈페이지( www.secmem.org )를 참조하세요. [삼성소프트웨어멤버십이란?] 1. 설립목적 삼성소프트웨어멤버십이란, S/W 및 관련 분야에 대한 재능과 열정이 있는 대학생들에게 연구, 개발에 필요한 모든 것들을 지원하여 창의적이고 실력 있는 삼성의 Software 전문가가 되도록 지원하는 프로그램입니다. 2. S/W멤버십에서 바라는 인재상 열정적이고 창의적인, Software 에 재능이 있는 학생들입니다. 학년, 나이, 학교, 전공, 성별에 구애 받지 않고 S/W 분야에 대한 열정, 그리고 그 열정을 꽃 피울만한 실력이 갖춰진 학생이라면 S/W멤버십 회원이 될 수 있습니다. 3. S/W멤버십 Program 이란? S/W멤버십 회원이 되기 위한 절차를 거쳐 모든 시험과정에 합격한 뒤 회원이 되어 활동 과정을 수료하는 사람에 한해 삼성전자에 입사할 수 있는 특전이 주어지는 제도입니다. ▷▶▷삼성소프트웨 어멤버십 홈페이지 바로가기 |
설정
트랙백
댓글
글
옵티컬 플로우 Optical Flow -3
현재 가장 많이 사용하고 있는 옵티컬 플로우 방법은 Lucas와 Kanade방법이다.
[Lucas81] Lucas, Bruce D., and Takeo Kanade. "An iterative image registration technique with an application
to stereo vision." IJCAI. Vol. 81. 1981.
위 영상을 보면 추적 영상의 외곡에도 상당히 추적을 잘하는 것을 볼수 있다.
각 화소에 대하여 특정 범위 안 윈도우 크기 내의 이웃 화소 들에 대해서 광류식(Optical Flow Equation)의 에러 제곱을
최소화하는 벡터를 구하기 위해 벡터를 지역 최소자승법(local least squares)로 계산한다.
예를 들면 윈도우 크기 3x3일 떄 9개의 화소에 대하여 에러 제곱을 최소화하는 (u,v)을 의사 역행렬(pseudo inverse matrix)을
최소자승법으로 계산한다.
위와 같이 광류식을 새워놓고 에러 제곱을 최소화 하는 광류 벡터(u,v)를 구하이 위해 u와 v로 각각 미분한다.
(에러 제곱을 최소화 하는 광류 벡터)
각각 미분한 식을 0으로 놓고 계산하면 아래 수식을 얻을 수 있다.
을 아래 수식으로 변형한다.
OpenCV에는 Lucas와 Kanade의 Optical Flow함수를 2가지 제공한다.
첫번째는 위 수식을 그대로 적용한 기본 방법이고,
1 | void cvCalcOpticalFlowLK(const CvArr* prev, const CvArr* curr, CvSize win_size, CvArr* velx, CvArr* vely) | cs |
1 | void cvCalcOpticalFlowPyrLK(const CvArr* prev, const CvArr* curr, CvArr* prev_pyr, CvArr* curr_pyr, const CvPoint2D32f* prev_features, CvPoint2D32f* curr_features, int count, CvSize win_size, int level, char* status, float* track_error, CvTermCriteria criteria, int flags) | cs |
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 | #include <cv.h> #include <highgui.h> #include <stdio.h> const int MAX_CORNERS = 200; int main(int argc, char** argv) { //초기화. 두개의 영상을 불러오고, 결과를 저장한 영상을 생성한다. IplImage* imgA = cvLoadImage("img/prev.bmp", CV_LOAD_IMAGE_GRAYSCALE); IplImage* imgB = cvLoadImage("img/curr.bmp", CV_LOAD_IMAGE_GRAYSCALE); CvSize img_sz = cvGetSize(imgA); int win_size = 10; IplImage* imgC = cvLoadImage("img/curr.bmp", CV_LOAD_IMAGE_UNCHANGED); //추적할 특징을 검출한다 IplImage* eig_image = cvCreateImage(img_sz, IPL_DEPTH_32F, 1); IplImage* tmp_image = cvCreateImage(img_sz, IPL_DEPTH_32F, 1); int corner_count = MAX_CORNERS; CvPoint2D32f* cornersA = new CvPoint2D32f[MAX_CORNERS]; // 이미지에서 추적하기 적절한 코너 추출 cvGoodFeaturesToTrack(imgA, eig_image, tmp_image, cornersA, &corner_count, 0.01, 5.0, 0, 3, 0, 0.04); // 좀더 정확한 서브 픽셀 코너 계산 cvFindCornerSubPix(imgA, cornersA, corner_count, cvSize(win_size, win_size), cvSize(-1, -1), cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03)); // Lucas-Kanade 옵티컬 플로우 char feature_found[MAX_CORNERS]; float feature_errors[MAX_CORNERS]; CvSize pyr_sz = cvSize(imgA->width + 8, imgB->height / 3); IplImage* pyrA = cvCreateImage(pyr_sz, IPL_DEPTH_32F, 1); IplImage* pyrB = cvCreateImage(pyr_sz, IPL_DEPTH_32F, 1); CvPoint2D32f* cornersB = new CvPoint2D32f[MAX_CORNERS]; //추출한 코너(cornerA)를 추적함 -> 이동한 점들의 위치는 cornerB에 저장된다. cvCalcOpticalFlowPyrLK(imgA, imgB, pyrA, pyrB, cornersA, cornersB, corner_count, cvSize(win_size, win_size), 5, feature_found, feature_errors, cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, .3), 0); for (int i = 0; i<corner_count; i++) { if (feature_found[i] == 0 || feature_errors[i] > 550) { //feature_found[i]값이 0이 리턴이 되면 대응점을 발견하지 못함 //feature_errors[i] 현재 프레임과 이전프레임 사이의 거리가 550이 넘으면 예외로 처리 printf("Error is %f\n", feature_errors[i]); continue; } printf("Got it\n"); CvPoint p0 = cvPoint(cvRound(cornersA[i].x), cvRound(cornersA[i].y)); CvPoint p1 = cvPoint(cvRound(cornersB[i].x), cvRound(cornersB[i].y)); cvLine(imgC, p0, p1, CV_RGB(255, 0, 0), 2); } cvNamedWindow("ImageA", 0); cvNamedWindow("ImageB", 0); cvNamedWindow("Lkpyr_OpticalFlow", 0); cvShowImage("ImageA", imgA); cvShowImage("ImageB", imgB); cvShowImage("Lkpyr_OpticalFlow", imgC); cvWaitKey(0); cvDestroyAllWindows(); cvReleaseImage(&imgA); cvReleaseImage(&imgB); cvReleaseImage(&imgC); cvReleaseImage(&eig_image); cvReleaseImage(&tmp_image); cvReleaseImage(&pyrA); cvReleaseImage(&pyrB); return 0; } | cs |
소스 참조 : http://lueseypid.tistory.com/archive/20130122
'컴퓨터비전/영상처리' 카테고리의 다른 글
칼만 필터(Kalman Filter) (0) | 2015.04.06 |
---|---|
움직임 추적(motion tracking) (0) | 2015.04.05 |
옵티컬 플로우 Optical Flow -2 (0) | 2015.04.05 |
옵티컬 플로우 Optical Flow -1 (0) | 2015.04.04 |
영상 정합(image patch similarity) (0) | 2015.04.04 |
설정
트랙백
댓글
글
옵티컬 플로우 Optical Flow -2
지금 소개할 Optical Flow는 Horn이 1981년
[Horn81] Horn, Berthold K., and Brian G. Schunck. "Determining optical flow." 1981 Technical Symposium East.
International Society for Optics and Photonics, 1981.
논문에서 소개한 방법이다.
Horn과 Schunck는 밝기값 패턴에서 임의의 화소에서의 발기값이 시간에 따라 변화하지 않는 상수로 가정하고,
속도 분포가 영상의 모든 화소에서 부드럽게(smooth) 변화한다라는 제약조건을 추가했다. 쉽게 말하면 프레임 간의
간격은 매우 짧은 시간이므로 픽셀의 움직임이 크지 않다는 것을 가정한 것이다.
따라서 제곱에러 e^2를 최소화하는 속도 벡터 u와 v를 반복적으로 계산한다. (아래 수식)
Ex, Ey, Et를 2x2윈도우를 사용하여 다음과 같이 계산한다.
위 방법은 모든 화소에서 광류 계산을 수행하기 때문에 좀 느린 단점이 있다.
'컴퓨터비전/영상처리' 카테고리의 다른 글
움직임 추적(motion tracking) (0) | 2015.04.05 |
---|---|
옵티컬 플로우 Optical Flow -3 (0) | 2015.04.05 |
옵티컬 플로우 Optical Flow -1 (0) | 2015.04.04 |
영상 정합(image patch similarity) (0) | 2015.04.04 |
해리스 코너(Harris Corner) (2) | 2015.04.03 |
설정
트랙백
댓글
글
옵티컬 플로우 Optical Flow -1
어떤 물체를 추적할때 가장 간단한 방법이 해당 블록(영역)을 다음 프레임에서 어딨을지 찾아가는
방법이 가장 간단한 추적 방법일 것이다.
OpenCV에서는 이 방법을 하나의 메소드 형태로 제공한다.
1 2 | void cvCalcOpticalFlowBM(const CvArr* prev, const CvArr* curr, CvSize block_size, CvSize shift_size, CvSize max_range, int use_previous, CvArr* velx, CvArr* vely) | cs |
파라메타로 prev는 이전프레임으로 (8비트, 1채널 영상)을 입력받으며, curr는 현재 프레임으로
이전 프레임과 동일하게 8비트 1채널 영상을 입력 받는다.
block_size는 두 영상에서 비교할 블록의 크기를 의미하며, shift_size는 탐색할 영역 크기를 나타낸다.
velx와 vely는 속도 벡터를 나타내는 변수로 1채널 32비트 실수 영상이다.
max_range는 curr에서 최대 탐색 범위를 나타내고 usePreviosu가 1이면 velx와 vely에 있는 이전 결과를 사용한다.
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 | #include <cv.h> #include <highgui.h> #include <opencv\cvaux.h> int main() { IplImage *prev = NULL, *curr = NULL; IplImage *velx = NULL, *vely = NULL; prev = cvLoadImage("img/prev.bmp", 0); curr = cvLoadImage("img/curr.bmp", 0); CvSize block_Size = cvSize(10, 10); CvSize shift_size = cvSize(5, 5); CvSize max_range = cvSize(5, 5); int usePrevious = 0; int sX = (prev->width - block_Size.width) / shift_size.width + 1; int sY = (prev->height - block_Size.height) / shift_size.height + 1; velx = cvCreateImage(cvSize(sX, sY), IPL_DEPTH_32F, 1); vely = cvCreateImage(cvSize(sX, sY), IPL_DEPTH_32F, 1); cvCalcOpticalFlowBM(prev, curr, block_Size, shift_size, max_range, usePrevious, velx, vely); IplImage *result = cvLoadImage("img/curr.bmp", 1); double dx, dy; CvPoint p1, p2; int threshold = 2; // Draw OpticalFlow Vector for (int y = 0; y < sY; y++) { for (int x = 0; x < sX; x++) { dx = cvGetReal2D(velx, y, x); dy = cvGetReal2D(vely, y, x); if (sqrt(dx * dx + dy * dy) < threshold) continue; p1.x = block_Size.width + x * shift_size.width; p1.y = block_Size.height + y * shift_size.height; p2.x = cvRound(p1.x + dx); p2.y = cvRound(p1.y + dy); cvLine(result, p1, p2, CV_RGB(0, 255, 0), 1, CV_AA); } } cvNamedWindow("velx"); cvShowImage("velx", velx); cvNamedWindow("vely"); cvShowImage("vely", vely); cvNamedWindow("OpticalFlow Result"); cvShowImage("OpticalFlow Result", result); cvWaitKey(0); cvDestroyAllWindows(); return 0; } | cs |
입력 영상 및 결과 영상
결과 영상 (vely) (velx)
'컴퓨터비전/영상처리' 카테고리의 다른 글
옵티컬 플로우 Optical Flow -3 (0) | 2015.04.05 |
---|---|
옵티컬 플로우 Optical Flow -2 (0) | 2015.04.05 |
영상 정합(image patch similarity) (0) | 2015.04.04 |
해리스 코너(Harris Corner) (2) | 2015.04.03 |
직선 검출 허프 변환 (0) | 2015.04.03 |
설정
트랙백
댓글
글
영상 정합(image patch similarity)
영상의 패치 정합
1. SSD(Sum of Squared Difference)
같은 위치에 있는 픽셀의 차를 구해서 제곱한 값을 다 더해 영역의 유사성 측정을 한다.
유사할 경우 0에 가깝다.
2. SAD(Sum Of Absolute Difference)
같은 위치에 있는 픽셀의 차를 절대값 하고 다 더해 영역의 유사성 측정을 한다.
SSD와 마찬가지로 유사할 경우 0에 가깝다.
3. NCC(Normalized Cross Correlation)
NCC는 SSD와 SAD가 갖는 밝기 차의 문제를 결하기 위해 사용하는 중 하나이다.
두 영역의 픽셀 평균값과 표준편차를 계산하여 평균 값기가 0, 표준편차는 1이 되도록 정규화한다.
m은 평균 밝기를 나타내며 σ는 표준편차이다.
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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | #include <cv.h> #include <highgui.h> #include <iostream> using namespace cv; using namespace std; int SSD(Mat &src1, Mat &src2, Point pt1, Point pt2, int patch_size) { // Range Check if ((pt1.x - patch_size) < 0 || (pt2.x - patch_size) < 0 || (pt1.x + patch_size) >= src1.rows || (pt2.x + patch_size) >= src2.rows || (pt1.y - patch_size) < 0 || (pt2.y - patch_size) < 0 || (pt1.y + patch_size) >= src1.cols || (pt2.y + patch_size) >= src2.cols) throw exception("SSD::Inavlid range"); // Image Channel Check if (src1.channels() != src2.channels()) throw exception("SSD::Image channel not same"); int nSum = 0; unsigned char *src1_ptr = (unsigned char*)src1.data; unsigned char *src2_ptr = (unsigned char*)src2.data; int src1_cn = src1.channels(); int src2_cn = src2.channels(); for (int r = -patch_size; r <= patch_size; r++) { for (int c = -patch_size; c <= patch_size; c++) { for (int l = 0; l <= src1_cn; l++) { nSum += ((src1_ptr[(pt1.x + r) * src1.cols * src1_cn + (pt1.y + c) * src1_cn + l]) - (src2_ptr[(pt2.x + r) * src2.cols * src2_cn + (pt2.y + c) * src2_cn + l])) * ((src1_ptr[(pt1.x + r) * src1.cols * src1_cn + (pt1.y + c) * src1_cn + l]) - (src2_ptr[(pt2.x + r) * src2.cols * src2_cn + (pt2.y + c) * src2_cn + l])); } } } return nSum; } int SAD(Mat &src1, Mat &src2, Point pt1, Point pt2, int patch_size) { // Range Check if ((pt1.x - patch_size) < 0 || (pt2.x - patch_size) < 0 || (pt1.x + patch_size) >= src1.rows || (pt2.x + patch_size) >= src2.rows || (pt1.y - patch_size) < 0 || (pt2.y - patch_size) < 0 || (pt1.y + patch_size) >= src1.cols || (pt2.y + patch_size) >= src2.cols) throw exception("SAD::Inavlid range"); // Image Channel Check if (src1.channels() != src2.channels()) throw exception("SAD::Image channel not same"); int nSum = 0; unsigned char *src1_ptr = (unsigned char*)src1.data; unsigned char *src2_ptr = (unsigned char*)src2.data; int src1_cn = src1.channels(); int src2_cn = src2.channels(); for (int r = -patch_size; r <= patch_size; r++) { for (int c = -patch_size; c <= patch_size; c++) { for (int l = 0; l < src1_cn; l++) { nSum += abs((src1_ptr[(pt1.x + r) * src1.cols * src1_cn + (pt1.y + c) * src1_cn + l]) - (src2_ptr[(pt2.x + r) * src2.cols * src2_cn + (pt2.y + c) * src2_cn + l])); } } } return nSum; } float NCC(Mat &src1, Mat &src2, Point pt1, Point pt2, int patch_size) { // Range Check if ((pt1.x - patch_size) < 0 || (pt2.x - patch_size) < 0 || (pt1.x + patch_size) >= src1.rows || (pt2.x + patch_size) >= src2.rows || (pt1.y - patch_size) < 0 || (pt2.y - patch_size) < 0 || (pt1.y + patch_size) >= src1.cols || (pt2.y + patch_size) >= src2.cols) throw exception("NCC::Inavlid range"); // Image Channel Check if (src1.channels() != src2.channels()) throw exception("NCC::Image channel not same"); // calcurate average, stendard deviation int nSum1 = 0, nSum2 = 0; int nSqr1 = 0, nSqr2 = 0; unsigned char *src1_ptr = (unsigned char*)src1.data; unsigned char *src2_ptr = (unsigned char*)src2.data; int src1_cn = src1.channels(); int src2_cn = src2.channels(); for (int r = -patch_size; r <= patch_size; r++) { for (int c = -patch_size; c <= patch_size; c++) { for (int l = 0; l < src1_cn; l++) { int t1 = src1_ptr[(pt1.x + r) * src1.cols * src1_cn + (pt1.y + c) * src1_cn + l]; int t2 = src2_ptr[(pt2.x + r) * src2.cols * src2_cn + (pt2.y + c) * src2_cn + l]; nSum1 += t1; nSum2 += t2; nSqr1 += t1 * t1; nSqr2 += t2 * t2; } } } int nPixels = (patch_size * 2 + 1) * (patch_size * 2 + 1); // pixel num float fMean1 = (float)nSum1 / (float)nPixels; float fMean2 = (float)nSum2 / (float)nPixels; float fVar1 = (float)nSqr1 / (float)nPixels - (float)fMean1 * (float)fMean1; float fVar2 = (float)nSqr2 / (float)nPixels - (float)fMean2 * (float)fMean2; // NCC float fSumNCC = 0.0f; for (int r = -patch_size; r <= patch_size; r++) { for (int c = -patch_size; c <= patch_size; c++) { for (int l = 0; l < src1_cn; l++) { fSumNCC += (float)((src1_ptr[(pt1.x + r) * src1.cols * src1_cn + (pt1.y + c) * src1_cn + l]) - fMean1) * ((src2_ptr[(pt2.x + r) * src2.cols * src2_cn + (pt2.y + c) * src2_cn + l]) - fMean2); } } } return fSumNCC / (fVar1 * fVar2); } int main() { Mat img, img2; img = imread("img/flower.jpg", 1); img2 = imread("img/flower.jpg", 1); int result_SSD = SSD(img, img2, Point(100, 100), Point(100, 100), 3); int result_SAD = SAD(img, img2, Point(100, 100), Point(100, 100), 3); float result_NCC = NCC(img, img2, Point(100, 100), Point(100, 100), 3); cout << "SSD = " << result_SSD << endl; cout << "SAD = " << result_SAD << endl; cout << "NCC = " << result_NCC << endl; namedWindow("image"); imshow("image", img); waitKey(); destroyAllWindows(); return 0; } | cs |
'컴퓨터비전/영상처리' 카테고리의 다른 글
옵티컬 플로우 Optical Flow -2 (0) | 2015.04.05 |
---|---|
옵티컬 플로우 Optical Flow -1 (0) | 2015.04.04 |
해리스 코너(Harris Corner) (2) | 2015.04.03 |
직선 검출 허프 변환 (0) | 2015.04.03 |
이미지 비율 유지 크기 조절 (0) | 2015.02.10 |
설정
트랙백
댓글
글
해리스 코너(Harris Corner)
해리스 코너는 크리스 해리스(Chris Harris)라는 사람이 1988년 제안한 것이다.
해리스 코너 검출기는 미분값에 기반을 둔 검출 방법이다. 코너 응답 함수(CRF-Corner Response Function)을 기반으로
코너를 파악한다. 코너 응답 함수(CRF)는 코너가 얼마나 강한지 판별하는 함수이다.
※ 해리스 코너 검출은 다음 5가지 과정을 거친다.
1. 소벨 마스크를 이용해 미분값을 계산 2. 미분값의 곱 계산 3. 미분값에 가우시안 마스크 적용 4. 코너 응답 함수(CRF) 계산 5. 비최대값억제(non-maximum supression) 으로 최종 코너 검출 |
위 행렬식은 2x2행렬의 고유 벡터(Eigen vector)가 얼마나 서로 수직인 방향으로 큰 값을 갖는지를 측정하는 값이다.
여기서 Ix와 Iy는 각각 영상에서 가로와 세로 방향의 미분값을 나타낸다. 이러한 행렬에서 고유 벡터를 구하면 경계선
방향에 수직인 벡터 두개를 얻을 수 있다. 아래 그림과 같이 두 방향의 벡터를 구할 수 있다.
각각 두 벡터는 수직일 경우와 수직보다 작을 경우, 수직보다 클 경우로 구할 수 있다.
이때 수직인 경우가 가장 좋은 코너점으로 생각하면 된다. 코너의 벡터는 미분을 이용해 고유벡터를 구하면 된다.
고유 벡터를 구하는 것은 고유값 분해(Eigen Value Decomposition)을 계산해야 하지만 조금 복잡하고 시간이 많이 소요된다.
따라서 해리스 코너 검출기에서는 고유 벡터를 직접 구하지 않고 코너 응답 함수(CRF)를 사용한다.
여기서 det(M)은 행렬식을 뜻하고, Trace(M)은 행렬의 대각합을 뜻한다. K값은 보통 0.04를 사용한다.
위와 같이 모든 점들에 관해 코너 응답함수(CRF)를 구하고, 그 값이 적절한 임계값(Threshold)이상이면 코너로 판별하면 된다.
'컴퓨터비전/영상처리' 카테고리의 다른 글
옵티컬 플로우 Optical Flow -1 (0) | 2015.04.04 |
---|---|
영상 정합(image patch similarity) (0) | 2015.04.04 |
직선 검출 허프 변환 (0) | 2015.04.03 |
이미지 비율 유지 크기 조절 (0) | 2015.02.10 |
Tracking Learning and Detection(TLD) (0) | 2014.10.30 |
설정
트랙백
댓글
글
직선 검출 허프 변환
2차원 공간상의 직선을 표현하는 방법중 가장 쉬운 방법은 y = ax+b이다.
a값이 직선의 기울기, b 값이 y축과 만나는 y절편이다.
직선의 방정식을 이용하면 모든 직선을 실수 a와 b의 조합으로 나타낼 수 있으며, 입력 영상에 나타날 수 있는
직선이 가지는 a, b값의 범위는 매우 많다.
기울기와, y절편값을 조절하면 수많은 직선을 만들 수 있다.
따라서 직선을 찾기 위해 이 모든 값을 조사하는건 현실적으로 불가능하다.
이 문제를 해결하기 위해 사용하는 방법이 바로 허프 변환(Hough Transform)이다.
좀더 효율적으로 변수를 조합하여 직선을 표현하여 문제를 해결하는 것이다.
허프 변환은 직선 뿐만 아니라 2차원 평면, 원, 3차원 공간평면 등 다양한 도형에 대해 적용이 가능하다.
허프 변환은 아주 신기한 방법이다. 직선의 방정식을 x, y 극좌표계에서 a값(기울기)와 b(y절편)을 아래와 같은
극좌표계로 변형했을때 같은 기울기를 갖는 직선은 한점에서 만난다는 점이다.
따라서 이를 이용하면 직선을 구할 수 있다.
직선을 위 그림과 같이 표현하면 ρ는 원점에서부터 직선 까지의 거리를 나타내며, Θ는 y축과 직선에서 수직으로 그린
선의 각도를 나타낸다. 위 직선의 방정식을 이용하면 유한한 범위의 ρ와 Θ값을 이용하여 평면 위의 직선을 표현할 수 있다.
따라서 기존 직선의 방정식을 위와 같은 직선의 방정식으로 변형하면 한정 범위안의 직선을 찾을 수 있을 것이다.
극좌표계를 사용하면 평면 위의 한점(x,y)을 지나는 모든 직선을 ρ와 Θ의 조합으로 표현할 수 있다.
영상에서 모든 경계선 좌표 (x,y)에 대해 위 작업을 수행하면 많은 수의 ρ와 Θ 조합을 얻을 수 있다.
만약 한 점에서 많은 직선이 교차할 경우 하나의 픽셀 에 많은 점이 교차할 것이다.
이것을 이용하여 같은 직선상의 좌표를 구할 수 있다.
허프 변환을 구현할 때 각도 Θ값의 범위를 0'~360' 사이가 아닌 0'~180'로 하고, ρ 값이 음수 값을 가질 수 있도록 한다.
'컴퓨터비전/영상처리' 카테고리의 다른 글
영상 정합(image patch similarity) (0) | 2015.04.04 |
---|---|
해리스 코너(Harris Corner) (2) | 2015.04.03 |
이미지 비율 유지 크기 조절 (0) | 2015.02.10 |
Tracking Learning and Detection(TLD) (0) | 2014.10.30 |
random tree(임의 숲) (0) | 2014.10.25 |
설정
트랙백
댓글
글
에이다부스트(adaboost)
'패턴인식 & 기계학습' 카테고리의 다른 글
Deep Learning 두번째 세미나 (0) | 2015.09.29 |
---|---|
딥러닝 framework Theano 설치 (0) | 2015.09.21 |
k-means clustering (0) | 2015.02.27 |
딥러닝 Deep Learning (0) | 2015.02.27 |
SVM(Support Vector Machine) (0) | 2014.11.04 |