Python에서 OpenCV 라이브러리를 이용하여 영상처리하는 과정에 대한 동영상 강좌입니다.

이제 시작으로, 가끔씩 천천히 업로드됩니다.

 

우선 1편은 이미지를 읽어서 화면에 표시하는 간단한 과정을 보여줍니다.

 

유튜브 동영상

반응형
LIST

 

소스코드 링크

https://drive.google.com/open?id=1_SJeavuZhjo_gS7XvXyJizbt41EEYFw6

 

사진데이터 링크

https://drive.google.com/open?id=1AjVLqhWeZI_oY9Rm5WIZb3DfcNEBA8jS

반응형
LIST

'영상데이터-딥러닝' 카테고리의 다른 글

linear regression 소스코드  (0) 2019.08.27
pytorch 실습코드 링크  (1) 2019.08.27
강의교안  (0) 2019.08.27
이미지 폴더  (0) 2019.08.26
Image Processing 링크  (0) 2019.08.26

https://drive.google.com/open?id=1SSaaJFdA-Lg7zKRLvHRfqf0_Ll3BDARe

반응형
LIST

'영상데이터-딥러닝' 카테고리의 다른 글

주차장 영상 데이터 및 소스코드 링크  (0) 2019.08.28
pytorch 실습코드 링크  (1) 2019.08.27
강의교안  (0) 2019.08.27
이미지 폴더  (0) 2019.08.26
Image Processing 링크  (0) 2019.08.26

https://drive.google.com/open?id=18xeHb1kE1Q_yIeqOhRJWTej0YeOhfmN3

반응형
LIST

'영상데이터-딥러닝' 카테고리의 다른 글

주차장 영상 데이터 및 소스코드 링크  (0) 2019.08.28
linear regression 소스코드  (0) 2019.08.27
강의교안  (0) 2019.08.27
이미지 폴더  (0) 2019.08.26
Image Processing 링크  (0) 2019.08.26

강의교안 다운로드 링크

 

https://drive.google.com/open?id=18Qigwv2v2pUb3sGCbq7_26tY_SDCptKy

반응형
LIST

'영상데이터-딥러닝' 카테고리의 다른 글

주차장 영상 데이터 및 소스코드 링크  (0) 2019.08.28
linear regression 소스코드  (0) 2019.08.27
pytorch 실습코드 링크  (1) 2019.08.27
이미지 폴더  (0) 2019.08.26
Image Processing 링크  (0) 2019.08.26

https://drive.google.com/open?id=1eHDROir1QetPyJKExLcTQaHrHyNi0xtD

반응형
LIST

'영상데이터-딥러닝' 카테고리의 다른 글

주차장 영상 데이터 및 소스코드 링크  (0) 2019.08.28
linear regression 소스코드  (0) 2019.08.27
pytorch 실습코드 링크  (1) 2019.08.27
강의교안  (0) 2019.08.27
Image Processing 링크  (0) 2019.08.26

https://drive.google.com/open?id=1i5uvZMlXPfkP4eEW6TRIMtDIJ5A-EtrX

 

이미지 프로세싱 관련한 소스코드에 대한 링크입니다.

 

ImageProcessing - Google 드라이브

 

drive.google.com

 

 

반응형
LIST

'영상데이터-딥러닝' 카테고리의 다른 글

주차장 영상 데이터 및 소스코드 링크  (0) 2019.08.28
linear regression 소스코드  (0) 2019.08.27
pytorch 실습코드 링크  (1) 2019.08.27
강의교안  (0) 2019.08.27
이미지 폴더  (0) 2019.08.26

>

>>

>>>


Microsoft는 얼굴사진에서 감정을 추측하는 Emotion API를 제공하고 있다.

향후 2019년 상반기에 Face API로 통합될 예정이라고 한다. 



감지가 가능한 감정은 다음 8가지 이다.

 anger, contempt, disgust, fear, happiness, neutral, sadness and surprise.


이 API를 이용하면, 각 사진에서 감정별로 예측값을 산출한다.


이러한 감정예측기술의 응용분야는 매우 다양할 것으로 예상된다.

반응형
LIST

'머신러닝' 카테고리의 다른 글

파이토치 모델 로딩, pytorch model loading  (0) 2022.10.27
Pytorch Simple - 1. Autograd  (0) 2019.09.09
패스트캠퍼스 Pytorch 강의자료  (0) 2018.07.21
Machine Learning 해외취업  (0) 2018.07.21
32,000장의 Medical CT images  (0) 2018.07.21

>

>>

>>>


패스트캠퍼스에서 pytorch를 이용한 딥러닝을 강의하신 분이 공유하신 자료와 코드


<github link>


아래는 강의순서의 일부



반응형
LIST

>

>>

>>>


특별한 능력있는 분이긴 하지만,

해외취업 과정을 자세히 설명한 슬라이드.

Deepmind에 취업하신 듯..


우리나라 사람 (국적은 한국이 아닐 수도 있지만)들 중에 

능력있는 젊은 분들이 많은 것 같습니다. 아직 미래는 밝다!


<슬라이드 링크>


아래는 슬라이드 저자가 지원했던 회사들의 로고


반응형
LIST

>

>>

>>>


미국 NIH (National Institute of Health) 산하의 Clinical Center에서 32,000장의 CT 사진들을 공개해서 딥러닝 연구에 사용토록 했다.


기사원문


이 사진 데이터에는 질병에 의해 발생하는 병변 (Lesion)들에 대한 위치정보와 설명이 들어있다.


4,400명의 환자들의 CT사진들을 annotation과 함께 공개함으로써,

인공지능을 이용한 CT이미지에서의 병변분석 기술의 발전을 기대한다고...


또, 이 CT이미지들에 대한 논문도 있는데, 

여기에는 간단한 딥러닝 기술을 이용한 병변감지 기술을 설명하고 있어서,

이 데이터를 이용하는 방법에 대해 가이드를 제공하고 있는 듯 하다.


DeepLesion: automated mining of large-scalelesion annotations and universal lesion detectionwith deep learning, Journal of Medical Imaging 5(3), 036501 (Jul–Sep 2018)

DeepLesion: automated mining of large-scale
lesion annotations and universal lesion detection
with deep learning


아래는 논문에 나온 사진들.



반응형
LIST

>

>>

>>>

Imitation Learning (흉내학습)


Tutorial video @ ICML 2018




사람 전문가가 하는 행동을 따라하기 위해서 학습하는 모델.


예를 들어,


운전자의 행동을 배운다든가, 

축구선수의 위치이동을 학습할 때 사용한다.


기존의 지도학습은 단순히 분류하는 일에 주로 사용된 반면,

imitation learning은 복잡한 형태의 지도학습으로, 상황별로 어떤 행동을 해야하는 지를 학습한다.

반응형
LIST

>

>>

>>>


Naive Bayes는 번역하면 '순진한 베이즈'이다.

명칭만큼이나 간단한 방법이다. 

수학적 배경지식없이 이것을 이해하기 위해서, 간단한 예를 들어보자.


노래가사만 보고 '트와이스' 노래인지, '윤도현' 노래인지 맞출 수 있을까?


혹시, 이들을 모르는 사람들이 있다면, '트와이스'는 요즘 유명한 걸그룹이고,

'윤도현'은 예전부터 꾸준히 유명한 남자가수다.



예를 들어, 다음 가사 3문장이 주어졌다면, 누구 노래일까?

1) Love is timing

2) Love is coming coming
3) You're my heart shaker shaker


트와이스 달콤사랑 테마를 아는 사람이라면 '트와이스'라고 단박에 알아차렸을 것이다.


다음 가사라면 어떨까?

1) 나는 아주 작은 애벌레 살이 터져 허물벗어 
2) 나는 상처 많은 번데기 추운 겨울이 다가와 
3) 이젠 나의 꿈을 찾아 날아 날개를 활짝 펴고 


터프가이 윤도현 이미지를 아는 사람은 '윤도현' 임을 알았을 것이다.


그렇다면, 

'Naive Bayes' 방식으로 동작하는 '뇌'를 가진 사람이 정답을 맞추는 과정은 다음과 같다.


아래 3문장이 주어졌을 때, '트와이스'인지, '윤도현'인지 맞춰보자.

1) Love is timing

2) Love is coming coming
3) You're my heart shaker shaker


1)번 문장에 대해서, '트와이스 노래라고 생각하는가'라는 질문을 던지고,

1점 (전혀 아니다)부터 5점 (매우 그렇다) 점수를 매기게 한다.


트와이스에 대해 알고 있는 사람이면, 4점 정도 주었을 것이다.


'윤도현 노래라고 생각하는가'라는 질문을 던졌을 때는,

아마도 2점 정도 주었을 것이다.


 

 '트와이스'노래라고 생각하는가

 '윤도현'노래라고 생각하는가?

 Love is timing

 4

 

 

 

 

 

 


2번 문장을 보면,  걸그룹 특유의 동어반복 'coming coming'이 나오니까

'트와이스' 5점, 윤도현도 가끔 같은 소리 지르니까 3점



 

 '트와이스'노래라고 생각하는가

 '윤도현'노래라고 생각하는가?

 Love is timing

 4

 Love is coming coming

 

 

 



마지막 3번 문장도 비슷한 식으로 하면



 

 '트와이스'노래라고 생각하는가

 '윤도현'노래라고 생각하는가?

 Love is timing

 4

 Love is coming coming

 You're my heart shaker shaker



이제 점수를 모두 '곱'해 보면

트와이스 점수는 100점,

윤도현 점수는 18점

왜 '더하지'않고 '곱'하는가 하고 의문이 생기지만, 일단 패스. 


결론적으로 가사의 주인공은 점수가 더 높은 '트와이스'로 결정!

이것이 Naive Bayes 가 동작하는 방식이다.


다만, 애매한 것은 노래가사에 대해 '몇 점을 줄지 어떻게 알지?'이다.

점수를 엉망으로 준다면, 정답을 맞출 수 없을 것이다.

예를 들어, 점수를 위의 예와 정반대로 주면 엉뚱하게 '윤도현' 정답이 나올테니 말이다.


점수를 제대로 주기 위해서는 '학습(learning), 훈련(training)'이 필요하다. 

쉽게 설명하자면 '트와이스' 노래를 많이많이 들어보면서 자주 나오는 단어나, 문장패턴을 필기해 가면서 익히는 것이다. 

'윤도현' 노래도 그렇게 하고.


그래서, 가사가 주어지면, 예전에 학습한 것을 참고해서 적절한 점수를 부여하면 된다. 


Naive Bayes 방법 자체는 간단한데, '점수'를 주는 방법을 잘 배우는 것이 핵심이다. 이러한 학습하는 방법에 대해서는 다음 기회에...







반응형
LIST


ㅇ Gamma correction은 이미지의 밝기를 조절하는 방법입니다.
  - 일반적으로 이미지를 밝게 하려면 픽셀값 (pixel intensity)을 증가시키면 됩니다.
  - 반대로 어둡게 하려면 감소시키면 됩니다.
  - 가장 간단한 방법으로는 모든 픽셀값을 같은 크기 만큼 증가시키면 이미지가 밝아집니다.
  - 하지만 gamma correction은 동일한 값 만큼 증가시키지는 않습니다.
  - Gamma correction은 이미지의 어두운 부분에 집중합니다.
  - 밝게 하거나, 어둡게 할 때,어두운 부분의 변화폭을 밝은 부분보다 크게 합니다.
  - 이러한 특징에 대해서는 뒤에서 그래프와 함께 자세히 설명하겠습니다.
 
ㅇ Gamma correction에서 gamma 값은 양수로서, 그 크기에 따라 밝기가 조정됩니다.
 - Gamma > 1이면, 이미지를 밝게 하는 것이고,
  - 0 < Gamma < 1이면, 어두워집니다.
  - Gamma 값은 양수 ( > 0)여야 합니다.
  - 만약 Gamma = 1인 경우, 이미지 밝기는 변하지 않습니다..

ㅇ Gamma correction은 컬러와 gray 이미지에 모두 적용 가능합니다.
  - Intensity를 가진 gray 이미지는 물론,
  - RGB 컬러이미지에도 적용 가능합니다. 
  - 이 때는, 각 채널별로 gamma correction을 적용해야 합니다.




ㅇ Gamma correction 공식을 살펴보겠습니다.
  - I (x,y)를 이미지(x,y)에서의 pixel intensity라고 하고
  - \gamma > 0가 주어지면, 
  - 아래 식은 correction 후의 pixel intensity값을 나타냅니다.
ㅇ 이 때 I(x,y) 값은 [0, 1.0]에 있어야 합니다.
따라서, 0 <= pixel intensity <= 255 일 경우에는,
[0 1]로 scaling한 후에 correction을 수행하고,
 다시 [0 255]로 scaling합니다.




ㅇ Gamma correction 적용 이미지를 살펴봅시다.
  - Gamma < 1 일 때 (=0.6), 전반적으로 사진이 어두워지며
  - Gamma > 1 일 때 (= 2, 3), 사진이 밝아지는 것을 
  - 아래 2가지 사진 예에서 correction결과를 볼 수 있습니다.







ㅇ Pixel intensity별로 correction 후에 값이 어떻게 변하는지 살펴보겠습니다.
  - Gamma 값에 따라 달라지는 이미지 brightness를 그래프로 그려 보았습니다.

ㅇ gamma > 1 인 경우,
- 아래 그래프에서, I(x,y) = 0.3이 
- gamma correction 이후에 0.55로 변환
- 어두웠던 것 (0.3)이 밝은 것(0.55)로 변환되었습니다.



ㅇ 0 < gamma < 1 인 경우, 
- 밝았던 부분 (= 0.8)이 gamma correction에 의해
- 어두운 부분 (=0.31)로 되었습니다.
- 비율로 따지자면, 밝았던 부분이 어두워지는 비율이 훨씬 크다


ㅇ Gamma correction은 이미지의 어두운 부분을 밝은 부분보다 더 많이 변화시키는 특징을 가집니다. 
  - 즉, intensity가 낮은 부분, 즉 어두운 부분에 대해서
  - 값의 변화정도가 더 크다.

0 < Gamma < 1 일 때,
intensity I [0, 1.0]별로,
correction이후의 값 I'과의 ratio를 비교해보겠습니다.
아래 그래프는 I' / I를 보여준 것입니다.
Intensity가 낮은 pixel들이 변환 후에 더 낮은 비율을 가지게 됨을 알 수 있습니다.
이로부터, gamma correction은,
이미지를 어둡게 할 때, 어두운 쪽의 픽셀들이 더 많이 어두워진다는 것을 알 수 있습니다.



Gamma > 1 일때,
낮은 intensity의 부분들이 correction 후에
값이 증가되는 비율이 높은 intensity부분들보다 더 크다는 것을 알 수 있습니다.
아래 결과를 잘 보면,
밝은 부분 (intensity가 큰 부분)은 gamma 값의 변화에 크게 영향을 받지 않음을 볼 수 있습니다.
하지만, 어두운 부분은 gamma값의 영향에 따라 값이 크게 변하는 것을 볼 수 있습니다.




ㅇ Gamma correction 구현
  - MATLAB으로 gamma correction을 수행하는 함수를 구현해 보았습니다.
  - 인수로 correction을 수행할 컬러이미지 (I)와
  - gamma를 받습니다.
    . 이 함수는 color이미지에 대해서만 동작합니다. 
  - 반환값은 gamma correction된 이미지입니다. 

  
function [img] = myGammaCorrection(I, gamma)
    
    %
    % double precision으로 바꾼다.
    % 값들이 rescaling되어 0 <= pixel <= 1.0으로 바뀐다.
    %
    img = im2double(I);
   

    % gamma correction
    agamma = 1/gamma;
    for i=1:3
        img(:,:,i) = img(:,:,i).^agamma;
    end
    
   
    % 값들이 다시 [0, 255]로 rescaling된다.
    img = im2uint8(img);
    
end







반응형
LIST

ㅇ 네이버에서 제공하는 실시간 인기 검색어를 프로그램에서 자동으로 가져오는 방법에 대해서 설명합니다.
  - 여기서는 실시간 검색어 제공 페이지의 html을 간략히 분석하고,
  - python 언어를 이용해서 검색어를 추출하는 방법을 설명합니다.

ㅇ 우선, 네이버에서 제공하는 실시간 인기검색어 URL과 웹브라우저에서 보이는 내용을 캡쳐해 보았습니다.
  -  이 URL에서 실시간으로 검색어 순위를 제공합니다.
  - 제공되는 형태는 아래와 같습니다만, 여기서는 일부분만 화면 캡쳐했기 때문에
  - 실제로는 훨씬 더 많은 내용이 보여집니다.


ㅇ 주의할 점은
  - 네이버 페이지 개편 작업 등을 하면 URL이 바뀔 수도 있고,
  - URL은 맞아도, 내용이나 포맷이 변경될 수도 있습니다.
  - 그 경우에는 여기서 설명하는 내용이 맞지 않을 수도 있습니다.
  - 하지만, 여기서 설명하는 방법을 이해하면,
  - 그러한 변경에 맞춰 추출하는 방법을 수정할 수 있습니다.

HTML 분석: 추출하는 방법을 설명하기 전에, 
  - 실시간 검색어 순위 페이지의 HTML 코드를 볼 줄 알아야합니다.
  - 아래에는 검색어 순위에 해당하는 부분의 코드만 캡쳐해 놓았습니다.
  - ul tag 들 중에서, class attribute가 'rank_list'를 가진 것이 실시간 검색어를 가지고 있습니다. 
  - 일단 이 ul tag를 찾기만 하면, 
  - 그 안에 span tag들이 여러 개 들어있는데, 이 tag의 값들이 모두 검색어에 해당합니다.
  - span tag들은 모두 20개 있고,
  - 이것은 실시간 상위 검색어 20에 해당합니다.


ㅇ Beautiful Soup
  - python에서는 HTML 문서 파싱을 아주 쉽게 할 수 있는 beautiful soup 모듈을 제공합니다.
  - 이 모듈은 html 문서를 tree구조로 만들에서,
  - 손쉽게 원하는 영역을 찾고, 특정 값을 추출할 수 있도록 합니다.
  - 이 프로그램도 beautifulsoup을 이용할 것입니다.

ㅇ 프로그램 구조
  - 아주 간단합니다.
  - 네이버 실시간 검색어 페이지를 다운로드 받고,
  - 결과를 파싱해서, 실시간 검색어 20개를 찾아내어 출력합니다.

ㅇ 실시간 검색어 페이지 다운로드
  - Python에서 제공하는 requests 모듈을 이용하면 1줄로 짤 수 있습니다.
  - 아래 코드의 line 30에 해당하는 부분입니다.

ㅇ HTML 파싱
  - 이제 Beautifulsoup이 등장할 순서입니다.
  - 다운로드 받은 웹페이지를 파싱하는데, 
  - 이것도 1줄이면 됩니다.
  - 아래 코드의 line 32에 해당합니다.

ㅇ 검색어 부분 추출
  - 이것도 간단하게 생각하면 됩니다.
  - 앞서 HTML 구조에서 설명했듯이, ul tag 들 중에서 class attribute가 'rank_list'인 것만 찾으면 된다.
  - 이 과정은 line 37 ~ 48에 해당합니다.
   
ㅇ 검색어만 출력하기
  - ul tag 안에 들어있는
  - span tag 들을 모두 찾으면,
  - 그 tag의 contents가 바로 실시간 검색어입니다.
  - span tag는 모두 20개이고, 상위 20개 인기 검색어에 해당합니다.
  - 이 과정은 line 50 ~ 55에 해당합니다.

ㅇ 파싱과정에서 다소 지저분한 디테일들과 한글처리 관련 주의사항이 있는데,
  - 아래 코드의 comment에 설명해 놓았습니다.
  - 코드는 바로 붙여넣기 한 후에 실행할 수 있습니다.
  - 다만, Beautifulsoup이 없다면, 먼저 설치해야 합니다.
    . 설치 방법에 대해서는 인터넷을 검색하면 됩니다. 

 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
# -*- coding: utf-8 -*-

# 네이버 실시간 검색어 크롤링 
# 
# 네이버에서 제공하는 실시간 인기 검색어 상위 20개를
# 자동추출하는 프로그램 
#
# 크롤이 대상 웹페이지: 아래 주소에서 실시간 검색어 정보를 얻을 수 있다.
# URL: http://datalab.naver.com/keyword/realtimeList.naver?where=main
# 
# 찾는 방법: 읽어 온 문서를 파싱해서 검색어를 찾는 과정은 다음과 같다.
# ul tag를 모두 찾고,
# class attribute가 rank_list인 것을 찾고,
# span tag안에 들어있는 실시간 검색어를 추출
#
#
# 사용모듈
# requests: HTTP request와 response, 네트워크를 통해웹페이지 접속
# BeautifulSoup: 웹페이지를 파싱 
#
# 주의사항: 
# Beautiful soup이 반환하는 값은 배열 안에 들어가 있음
# index[0]이 맨 앞에 들어 있는 값임.
#
#
import requests
from bs4 import BeautifulSoup

# 실시간 검색어를 제공하는 웹이지를 읽음
response = requests.get('http://datalab.naver.com/keyword/realtimeList.naver?where=main')
# 웹페이즈를 파싱
dom = BeautifulSoup(response.text, 'html.parser')

#print(response.text)  # for debugging

# ul tag를 모두 찾음
result = dom.find_all('ul')

#print result # for debugging

for res in result:
    
    #print(str(res['class']))  # for debugging
    #print "-------------------------\n"  # for debugging

    # 실시간 검색어를 출력한다.
    # class attribute의 값이 rank_list인 경우에,
    if res['class'][0] == 'rank_list':
        # 검색어가 들어있는 span tag를 모두 찾아서,
        keywords = res.find_all('span')
        # span tag의 contents 중 첫번째 [0] 요소를 출력하면 된다.
        # 한글을 올바르게 출력하기 위해서는 
        # 이 파일의 맨 위에 utf-8 coding임을 코멘트로 명시
        for key in keywords:  # 상위 20개의 실시간 검색어가 들어있다.
            print key.contents[0]
        break


ㅇ 위의 Python 코드를 실행한 결과입니다.
  - 20개의 실시간 인기 검색어가 나온 것을 볼 수 있습니다.







반응형
LIST






ㅇ 히스토그램 매칭 (Histogram matching)
  - 이미지의 색분포를 다른 이미지와 유사하게 하는 것을 말합니다.
  - color mapping 또는 color transfer라고도 합니다.
  - 예를 들어 아래와 같이 두 장의 이미지가 주어졌다고 합시다.
    . 왼쪽의 이미지는 바닷가를 촬영한 이미지이고,  푸른색 계통이 주요 색입니다.
    . 오른쪽은 포도밭을 촬영한 것이고, 녹색이 주요 색입니다.
    . 이제, 두 사진간에 히스토그램 매칭을 시도해 봅시다.

 


ㅇ 아래는 포도밭 이미지를 바닷가 이미지의 주요색으로 바꾼 것입니다.


ㅇ 아래 이미지는 반대로, 바닷가 이미지를 포도밭의 주요색으로 바꾼 것입니다.
  - 옥색 같았던 바다가  포도밭의 녹색과 노랑색으로 바뀌었습니다. 녹조가 생긴 것처럼 말이죠


ㅇ 또 다른 예를 보겠습니다.
  - 왼쪽은 도시야경 이미지이고, 주요색이 검정색입니다.
  - 오른쪽은 정원 이미지이고, 주요색이 녹색입니다.

   


ㅇ 히스토그램 매칭을 한 아래 이미지는
  - 정원 이미지에 도시야경의 주요 색인 검정색을 씌운 것입니다.
  - 신록으로 푸르렀던 나뭇잎들이 밤하늘 처럼 되어버렸습니다.


ㅇ 이번에는 반대의 경우입니다. 
  - 도시야경 이미지에 정원의 주요 색인 녹색을 씌운 것입니다.
  - 어두웠던 밤하늘이 녹색 계열로 바뀌었습니다. 묘한 분위기를 느낄 수 있습니다.


ㅇ 히스토그램 매칭의 원리
  - 이것은 이미지 (T)의 주요색을 다른 이미지 (R)의 주요 색로 바꾸는 것으로 
  - T의 색을 발생빈도수에 따라서 R의 색으로 바꾸는 것입니다.
  - 예를 들어, T에서 가장 많이 나타나는 색을 R에서 가장 빈도수가 높은 색으로 바꾼다고 생각하면 됩니다.
  - 마찬가지로 두 번째로 자주 나타나는 색을 R에서 빈도수가 두 번째로 높은 색으로 바꾸고,
  - 이런 식으로 빈도수의 순서에 따라서 바꿔나가면 되는 것입니다.

ㅇ 함수 LUT( )
  - OpenCV에서는 히스토그램 매칭을 쉽게 할 수 있도록  함수 LUT( )를 제공합니다.
  - 이 함수는 Lookup Table의 약자입니다.
  - 함수 원형은 아래 그림와 같습니다.
    . src는 입력이미지이고,
    . lut는 크기 256인 1차원 배열이고
    . dst는 결과이미지입니다.
    . 여기서 중요한 것은 lut인데,
    . lut[i]는 i 번째 색에 대해서 대체할 색을 의미합니다.
    . 예를 들어 설명해 보겠습니다.
    . src(x,y)의 색이 i라면, dst(x,y)의 색은 lut[i]가 되는 것입니다.
    


ㅇ 히스토그램 매칭 상세과정
  - 컬러이미지에 대해서 히스토그램 매칭을 수행하는 과정을 살펴보겠습니다.
  - 아래 소스코드에서 해당 라인 번호도 참고로 적어 놓습니다.

  - 121-133:
    . 두 장의 이미지를 입력으로 받습니다.
    . 하나는 히스토그램 매칭이 수행되어야 할 타겟 (T)이미지이고,
    . 다른 하나는 주요색을 추출할 레퍼런스 (R) 이미지 입니다.

  - 14-17:
    . T와 R 이미지 모두 컬러이미지이므로 R, G, B 채널별로 나눕니다.

  - 29-44:
    . 그리고, 채널별로 히스토그램을 구합니다. 이 때 함수 calcHist( )를 사용합니다.
    . 계산된 히스토그램에 대해서 normalize를 수행해서 최대값이 1.0이 되도록 합니다. 

  - 49-67:
    . Normalized된 히스토그램에 대해서 cumulative distribution function (CDF)를 계산합니다.
    . CDF에 대해서 다시 normalize를 수행해서 최대값이 1.0이 되도록 합니다.

  - 72-93:
    . 이것을 이용해서 Lookup table을 만드는데,
    . 빈도수 비율이 가장 유사한 색끼리 매칭한다고 보면됩니다.
    . 보다 정확하게는
    . 이미지 T 색 c의 빈도수에 해당하다는 이미지 R의 색 d를 결정해서
    . lookup 테이블 lut에 채워넣습니다.
    . 이것을 코드로 쓰면 lut[c] = d; 와 같이 됩니다.

  - 95:
    . 함수 LUT()를 이용해서 채널별로 히스토그램 매칭을 수행

  - 98: 
    . 완성된 채널을 합성해서 이미지를 만들어 내는 과정

ㅇ OpenCV 소스코드
  - 위에서 설명한 히스토그램 매칭과정을 C++ 소스코드로 구현한 것입니다.

  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
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

// reference와 target은 변경시키지 않을 것이므로 const
// result는 변경결과를 담는다.
void histMatch(const Mat &reference, const Mat &target, Mat &result)
{
	const float HISTMATCH = 0.000001;  // threshold of histogram difference
	double min, max;

	vector<Mat> ref_channels;  // 벤치마킹 이미지를 색채널별로 분리해서 저장
	split(reference, ref_channels);
	vector<Mat> tgt_channels;  // 색변경 대상 이미지를 채널별로 분리
	split(target, tgt_channels);
	
	int histSize = 256;  // histogram을 256 level로 작성
	float range[] = { 0, 256 };  // 최소와 최대 bin
	const float *histRange = { range };
	bool uniform = true;

	for (int i = 0; i < 3; i++)
	{
		Mat ref_hist, tgt_hist;  // 생성된 히스토그램 저장
		Mat ref_hist_accum, tgt_hist_accum;  // 히스토그램의 CDF계산

		calcHist(&ref_channels[i], 1, 0, Mat(), ref_hist, 1, &histSize, &histRange, uniform, false);
		calcHist(&tgt_channels[i], 1, 0, Mat(), tgt_hist, 1, &histSize, &histRange, uniform, false);

		minMaxLoc(ref_hist, &min, &max);  // 히스토그램에서 최소값과 최대값을 구한다.
		if (max == 0)
		{
			cout << "ERROR: max is 0 in ref_hist" << endl;
		}
		normalize(ref_hist, ref_hist, min / max, 1.0, NORM_MINMAX);  // 히스토그램 값을 0 ~ 1까지로 normalize한다.

		minMaxLoc(tgt_hist, &min, &max);  // 히스토그램에서 최소값과 최대값을 구한다.
		if (max == 0)
		{
			cout << "ERROR: max is 0 in tgt_hist" << endl;
		}
		normalize(tgt_hist, tgt_hist, min / max, 1.0, NORM_MINMAX);  // 히스토그램 값을 0 ~ 1까지로 normalize한다.

		//
		// CDF를 계산한다.
		//
		ref_hist.copyTo(ref_hist_accum);  // 복사본을 만든다.
		tgt_hist.copyTo(tgt_hist_accum);

		float *src_cdf_data = ref_hist_accum.ptr<float>();  // pointer로 access하면 성능을 높일 수 있다.
		float *dst_cdf_data = tgt_hist_accum.ptr<float>();

		for (int j = 1; j < 256; j++)
		{
			src_cdf_data[j] += src_cdf_data[j - 1];
			dst_cdf_data[j] += dst_cdf_data[j - 1];
		}

		//
		// 계산된 CDF를 normalize한다.
		//
		minMaxLoc(ref_hist_accum, &min, &max);
		normalize(ref_hist_accum, ref_hist_accum, min / max, 1.0, NORM_MINMAX);
		minMaxLoc(tgt_hist_accum, &min, &max);
		normalize(tgt_hist_accum, tgt_hist_accum, min / max, 1.0, NORM_MINMAX);

		// 
		// Histogram matching을 수행
		//
		Mat lut(1, 256, CV_8UC1);  // Lookup table을 만든다.
		uchar *M = lut.ptr<uchar>();
		uchar last = 0;
		for (int j = 0; j < tgt_hist_accum.rows; j++)
		{
			float F1 = dst_cdf_data[j];

			//
			// 벤치마킹이미지에서 유사한 CDF 값을 갖는 픽셀 intensity를 찾는다.
			//
			for (uchar k = last; k < ref_hist_accum.rows; k++)
			{
				float F2 = src_cdf_data[k];
				if (abs(F2 - F1) < HISTMATCH || F2 > F1)  // 유사한 CDF이거나, 
				{
					M[j] = k;  // 변경대상 이미지의 intensity j는 intensity k로 변환
					last = k;  // 다음 검색을 시작할 위치
					break;  // 다음 intensity로
				}

			}
		}

		LUT(tgt_channels[i], lut, tgt_channels[i]);  // Lookup table을 이용한 색깔 변화
	}  // end of for

	merge(tgt_channels, result);  // 3개 채널들을 합쳐서 이미지를 재생성

}

// test 1
#define REF_IMG "img_osaka_night.jpg"
#define TGT_IMG "img2_garden.jpg"

// test 2
//#define REF_IMG "img2_garden.jpg"
//#define TGT_IMG "img_osaka_night.jpg"

// test 3
//#define REF_IMG "img2_beach.jpg"
//#define TGT_IMG "img2_grapefarm.jpg"

// test 4
//#define REF_IMG "img2_grapefarm.jpg"
//#define TGT_IMG "img2_beach.jpg"

int main(int argc, char** argv)
{

	Mat ref = imread(REF_IMG, IMREAD_COLOR); 	// 색깔을 벤치마킹할 이미지, BGR 포맷으로 읽는다.
	if (ref.empty() == true)
	{
		cout << "Unable to read reference image" << endl;
		return -1;
	}
	Mat tgt = imread(TGT_IMG, IMREAD_COLOR); 	// 색깔을 변경할 이미지
	if (tgt.empty() == true)
	{
		cout << "Unable to read target image" << endl;
		return -1;
	}
	Mat dst = tgt.clone();  // 색깔 변경 결과를 담을 이미지

	namedWindow("Reference", WINDOW_KEEPRATIO);  // 벤치마킹 이미지 표시창, 비율따라 크기 조정
	namedWindow("Target", WINDOW_KEEPRATIO); // 변경대상 이미지
	namedWindow("Result", WINDOW_KEEPRATIO);  // 변경결과를 담을 이미지
	
	imshow("Reference", ref);
	imshow("Target", tgt);

	histMatch(ref, tgt, dst);
	imshow("Result", dst);

	waitKey();
	return 0;
}









반응형
LIST


ㅇ Brightness(밝기)와 Contrast조절

ㅇ 영상처리 기술들 중에서 간단하면서도 직관적인 것이 brightness와 contrast 조절입니다.
  - 우선 둘 중에 직관적으로 이해하기 쉬운 brightness부터 살펴 보겠습니다.

ㅇ Brightness는 영상을 밝기를 뜻합니다.
  - 밝기를 조절하기 위해서는 픽셀의 intensity를 바꾸면 됩니다.
  - Intensity값을 증가시키면, 영상이 밝아집니다.
  - 반대로 감소시키면, 어두워집니다.

ㅇ Contrast는 영상에서 밝은 부분과 어두운 부분의 차이를 의미합니다.
  - 방금 설명했던 brightness보다는 직관적으로 이해가 하기가 쉽지 않을 겁니다.
  - 예를 들어 보겠습니다.
  - 이미지 안의 대부분 픽셀들의 intensity가 비슷하다면,
  - 어떤 물체가 있는지 쉽게 알 수가 없습니다.
  - 이런 경우가 contrast가 낮은 것입니다.
  - 반대로, 이것이 높다는 것은 픽셀들간의 Intensity 차이가 많이 난다는 겁니다.
  - 이 경우, 물체의 모양이나 윤곽을 한 눈에 알아볼 수 있을 겁니다.
  - 해가 비치는 부분과 그림자 진 부분을 쉽게 구분할 수 있는 것 처럼 말입니다.

ㅇ 그러면, brightness 조절은 어떻게 할까요?
  - 쉽습니다. 모든 픽셀들의 intensity에 모두 같은 값만큼 더하거나 빼면 됩니다.
  - 더하면 이미지가 환해져서 brightness가 증가됩니다.
  - 빼는 것은 더하는 것의 반대 경우이구요.

ㅇ Brightness를 조절한 이미지의 예를 보겠습니다.
  - 우선 아래 그림은 조절 전의 이미지입니다.
  - 이미지가 두 구역으로 나뉘어져 있고, 왼쪽 편 픽셀들의 intensity가 오른편보다 큰것을 알 수 있습니다.

- 입력이미지


  - Intensity에 같은 양수를 더해 brightness를 증가시킨 결과입니다.
  - 왼편은 거의 흰색에 가까울 정도로 밝아졌고,
  - 오른편도 그만큼 밝아졌습니다.
  - 이렇듯 brightness 조절은 쉽습니다.
- brightness를 증가한 경우
- 좌측과 우측 모두 환해진 것을 알 수 있다.


ㅇ Contrast 증가의 예를 살펴보겠습니다.
  - 동일한 입력이미지를 사용했을 때, 
  - 왼편은 흰색으로 매우 밝아졌지만,
  - 오른편은 별 차이가 없습니다.
  - 대신 왼쪽과 오른쪽의 intensity 차이가 분명해져서,  선명해졌습니다.

- contrast를 증가한 경우,
- 좌측은 환해졌지만, 오른쪽은 원래보다 더 어두워졌다.
- 밝았던 부분은 더 밝아지고, 
- 어두웠던 부분은 더 어두워지기 때문에,
- 두 부분간의 대비 (contrast)가 증가했다.


ㅇ Contrast 증가의 원리
  - 이것은 밝은 부분과 어두운 부분의 차이를 크게 하는 것을 목적으로 하여,
  - 핵심은 곱하기입니다.
  - 즉, intensity에 값을 곱하는 겁니다.
  - 왜 이러한 곱셈이 contrast를 증가시키는지 알아보겠습니다.
  - 예를 들어, 두 개의 픽셀 A, B가 있고, 
  - 각각의 intensity A = 100, B = 50 라고 하겠습니다.
  - Contrast조절을 위해 두 픽셀에 같은 1.5를 곱한다고 하면
  - A = 150, B= 75이 됩니다.
  - 곱셈에 의해 A는 50만큼 증가하였고, B는 25만큼 증가하였습니다.
  - 이로 인해 밝은 부분인 A가 B보다 더 환해졌기 때문에, A와 B간의 차이가 증가하여 contrast가 증가됩니다.
  -자세히 살펴보면, 조절전에는 두 픽셀간의 intensity차이 (A - B)가 50 = (100- 50)이었다면,
  - 조절후에는 75로 1.5배 만큼 늘어났습니다.


ㅇ 실제 이미지를 이용해서 brightness와 contrast 조절 실험을 진행해 보겠습니다.
  - 입력이미지는 아래와 같습니다.
- 도시의 야경을 gray로 촬영한 입력이미지 


  - Brightness를 증가시켜보겠습니다.
- Brightness를 증가시키면
- 이미지가 전체적으로 밝아진다.
- 이는 모든 픽셀의 intensity값이 증가했기 때문입니다.


  - 위의 실험에서 조절 전후 이미지의 히스토그램이 어떻게 변하는지 살펴보겠습니다.
  - 이것을 보면, brightness 조절이 어떤 역할을 하는지 보다 정확히 알 수 있습니다.
- 아래 그림에서
- 1번 히스토그램은 입력이미지의 히스토그램이고,
- 2번은 brightness 증가 후의 것입니다.
- 봉우리가 오른쪽으로 이동한 것이 관찰되는데,
- 픽셀들의 intensity가 전반적으로 증가했기 때문입니다.


  - 이번에는 contrast를 증가시켜보겠습니다.
- Contrast가 증가되면,
- 어두웠던 부분은 더 어두워지고,
- 밝았던 부분은 더 밝아지면서
- 두 부분이 좀 더 극명하게 대비되는 효과를 볼 수 있습니다.


  - Contrast 변화가 히스토그램에 어떠한 영향을 끼치는지 살펴보겠습니다.
- 아래 히스토그램들은
- 1번은 입력이미지, 2번은 contrast 증가 후의 것입니다.
- 2번에서 어두운 픽셀의 개수가 크게 증가하였다는 것을 볼 수 있다.
- 밝은 픽셀의 수는 별로 증가하지 않았는데,
- 이는 contrast 적용공식의 작동 방식 때문입니다.
- 여기에 대해서 좀 더 자세히 알아보겠습니다.


ㅇ Contrast와 Brightness를 조절하는 공식
  - 위의 예에서 보았듯이,
  - 조절하는 공식의 기본 아이디어는 
  - brightness의 경우는 픽셀 intensity를 전체적으로 증가 혹은 감소하는 것이고,
  - contrast는 intensity에 비례하여 값을 변화시키는 것입니다.
  - 이것을 수학공식으로 표현하면 다음과 같습니다.

- x가 픽셀의 intensity라고 할 때
- 1) contrast를 조절하기 위해서는 적절한 alpha를 계산하여 곱한다.
- 2) brightness를 조절하기 위해서는 beta를 더한다.
- 그렇다면, alpha와 beta는 어떻게 계산하는 것일까요?


ㅇ alpha와 beta의 결정
  - 우선 contrast와 brightness는 둘 다 1 ~ 200까지 변동가능하다고 가정하겠습니다.
  - 그리고 100은 입력이미지 상태그대로를 의미한다고 하겠습니다. 
  - 따라서, < 100 이면, contrast와 brightness가 감소되는 것을 의미하고,
  - > 100이면, 증가하는 것을 의미합니다.
  - 아래 코드는 alpha (=a)와 beta (=b)를 결정하는 소스코드입니다. 


ㅇ Brightness 조절에 따른 alpha와 beta값의 변화
  - brightness를 1부터 200까지 조절할 때, alpha와 beta가 어떻게 변하는지 그래프를 그려보겠습니다.
  - (brightness == 100)은 조절하지 않음.
  - (brightness > 100)은 이미지를 환하게
  - (brightness < 100)은 이미지를 어둡게
  - 아래 그래프에서 x축은 brightness (1 ~ 200)을 나타내고,
  - y축은 alpha와 beta값입니다.

- brightness 조절은 alpha값을 변화시키지는 않습니다.
- alpha = 1을 유지합니다.



- beta값은 brightness에 정비례하여 증가 혹은 감소합니다.


ㅇ Contrast 변화에 따른 alpha와 beta의 조절
  - 이번에는 brightness는 100으로 고정시키고 ( 변경시키지 않는다는 뜻입니다.),
  - contrast를 1 ~ 200까지 변화시키면서, alpha와 beta가 어떻게 변하는지 보겠습니다.
  - 실제 그래프에서는 1 ~ 180까지만 표시했습니다.
  - 이번에도 contrast = 100은 변화가 없다는 것입니다.
  - x축은 contrast의 값, y축은 alpah와 beta값입니다.

- Contrast가 증가할 수록
- alpha값이 빠르게 증가합니다.


- beta값은 contrast와는 반대로 움직입니다.
- 이것은 alpha값에 의한 변화를 보상하기 위해서입니다.
- 예를 들어, contrast > 100인 경우,
- alpha값이 1보다 커져서, intensity에 곱해서 나오는 결과도 커집니다.
- 따라서, beta는 음수가 되어 이 결과를 조금 작게 할 필요가 있습니다.
- contrast < 100인 경우는 반대로 이해하면 되겠습니다.


ㅇ 위그래프를 생성해내기 위한 MATLAB 소스코드

ㅇ 구현
  - Brightness와 contrast 이미지 처리기술을 OpenCV를 이용해서 구현해 보겠습니다.
  - 앞서 설명했듯이 픽셀 intensity에 alpha를 곱하고, beta를 더하는 연산이 필요합니다.
  - 이러한 연산을 편하게 할 수 있는 함수를 OpenCV가 제공하는데,
  - Mat 클래스의 메소드인 convertTo( )입니다.

- convertTo 메소드는, 각 픽셀에 곱할 alpha와 더할 beta를 인수로 받습니다.
- 인수 m은 계산결과를 담은 Mat 객체입니다.
- 이 함수를 이용하면 위에서 설명한 조절을 쉽게 구현할 수 있습니다.



ㅇ Brightness와 Contrast 이미지 프로세싱 C++ 구현 코드 
  - 슬라이드바 (Slide bar)를 이용해서 brightness와 contrast를 조절하는 프로그램을 짜 보았습니다.
  - 각각의 조절에 따른 이미지 변화를 관찰할 수 있는 편리한 프로그램입니다. 

  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
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

//
// 슬라이드 바의 위치에 해당하는 값
// 최소 0에서 최대 200까지 움직인다.
//
int init_brightness = 100;
int init_contrast = 100;

Mat image;

const char* keys = { "{@imageName1| img_osaka_night.jpg |input image file} \\
{@imageName2| fruits.jpg|input image file} \\
{N | 1.0 | this is double value} \\
{help h ? | | this is help}" };


void updateBrightnessContrast(int, void*)
{
	int histSize = 255;
	int var_brightness = init_brightness - 100;
	int var_contrast = init_contrast - 100;

	double a, b;
	if (var_contrast > 0)
	{
		double delta = 127.0*var_contrast / 100;
		a = 255.0 / (255.0 - delta * 2);
		b = a*(var_brightness - delta);
	}
	else
	{
		double delta = -128.0*var_contrast / 100;
		a = (256.0 - delta * 2) / 255.0;
		b = a*var_brightness + delta;
	}

	cout << "a: " << a << " b: " << b << endl;

	Mat dst, hist;
	//
	// image의 각 픽셀값을 변경하여, dst를 만들어 화면에 표시
	// pixel =  a*pixel + b
	//
	image.convertTo(dst, CV_8U, a, b);
	imshow("image", dst);

	// dst의 히스토그램을 계산한다.
	calcHist(&dst, 1, 0, Mat(), hist, 1, &histSize, 0);

	Mat histImage = Mat::ones(200, 320, CV_8U) * 255;  // 320 * 200의 흰색이미지를 만든다.

	normalize(hist, hist, 0, histImage.rows, CV_MINMAX, CV_32F);
	
	int binW = cvRound((double)histImage.cols / histSize);

	for (int i = 0; i < histSize; i++)
	{
		Point leftUp = Point(i*binW, histImage.rows);
		Point rightDown = Point((i + 1)*binW, histImage.rows - cvRound(hist.at<float>(i)));
		rectangle(histImage, leftUp, rightDown, Scalar::all(0), -1, 8, 0);
		imshow("histogram", histImage);
	}
}


/*
   read in an image as a gray
*/
int main(int argc, char** argv)
{
	CommandLineParser parser(argc, argv, String(keys));
	parser.about("This is test program for commandlineparser of opencv");
	string inputImage1 = parser.get<string>("@imageName1");

	image = imread(inputImage1, IMREAD_GRAYSCALE);
	if (image.empty() == true)
	{
		cout << "Unable to read the image " << inputImage1 << "." << endl;
		return -1;
	}
	namedWindow("image", WINDOW_KEEPRATIO);
	namedWindow("histogram", WINDOW_KEEPRATIO);

	createTrackbar("brightness", "image", &init_brightness, 200, updateBrightnessContrast);
	createTrackbar("contrast", "image", &init_contrast, 200, updateBrightnessContrast);

	// 이 함수를 호출해야
	// 이미지를 화면에 표시할 수 있다.
	//
	updateBrightnessContrast(0, 0);

	waitKey();
	return 0;
}














반응형
LIST






ㅇ 명령행 인수 (Command line argument)


  - 예를 들어, 어떤 프로그램이 실행될 때 사용자가 파일 이름을 주면
  - 그 파일을 열어 작업한다고 가정하자. 
  - 이름을 넘겨주는 가장 쉬운 방법은 파일 이름을 소스코드에 하드코딩하는 것인데,
  - 단점은 이름을 바꿀 때 마다 프로그램 빌드를 다시 해야 하기 때문에 불편하다. 
  - 더 쉬운 방법은 실행할 때마다 '인수 (argument)'로 주는 것이다.
  - 예를 들어 'text.exe'라는 프로그램이 있다고 하고, 실행할 때, 아래와 같이 입력하여 인수를 넘겨줄 수 있다. 

>> test.exe image1 image2 -help -N=10.0

  - 위 명령어 라인을 하나하나 분석해 보면,
  - 실행파일 이름은 test.exe이고,
  - 여기에 인수로 image1, image2, -help, -N=10.0 등을 넘겨준다는 뜻이다.
  - 이렇게 실행명령과 함께 주어지는 인수들을 '명령행 인수 (command line argument)'라고 한다.
  - 그러면 프로그램은 이들 인수들을 인식해서,
  - 첫 번째와 두 번째 인수는 각각 image1과 image2이고,
  - -help라는 인수가 주어졌으며,
  - -N 인수로 지정되는 값은 10.0이라는 것을 알아햐 한다. 
  - 이러한 인식을 프로그래밍하는 것은 어렵지는 않지만 문자열 처리를 해야해서 귀찮기만 한다.
  - 하지만, 희소식이 있다.

ㅇ OpenCV에서는 이러한 처리를 위해 CommandLineParser 클래스를 제공하는데,

  - 사용하는 법은, 이 클래스에 미리 어떠한 인수들이 들어올 수 있는지를 알려주면
  - 실제로 입력된 인수들 중에 해당하는 것들을 추출해 준다.
  - 우선, 가능한 인수들이 어떤 것들인지 지정하는 방식에 대해서는 아래 설명을 보자

ㅇ 인수처리를 지정하는 방법

  - 아래 설명하는 포맷에 따라 지정하는데, 다소 복잡하니, 
  - 우선 예부터 살펴보자.
"{@imageName1| fruits.jpg|input image file} \\
{@imageName2| fruits.jpg|input image file} \\
{N count |1.0|this is double value} \\
{help h ?||this is help}"
  - @imageName1과 @imageName2는 첫 번째와 두 번째 입력되는 인수와 매칭되며,
  - 만약 입력이 안되면 fruits.jpg의 값을 default로 갖는다.
  - 그리고 'input image file'은 이 인수에 대한 설명으로 help 메시지에 의해 출력된다.
  - 인수 N과 count는 서로 같은 것이고, 
  - 명령행에서 입력될 때는 앞에 dash ('-')를 반드시 붙여서, -N=1.0 혹은 -count=1.0과 같이 입력해야 한다.
  - 특히, 여기서는 -N (혹은 -count)가 입력되지 않아도 -N=1.0이 입력된 것으로 처리된다.
  - 인수 help, h, ?는 help message 출력과 관계가 있고, 
  - 명령행에서  -help, -h, -?는 모두 동일하며, 
  - 이 인수의 경우 default value가 없으나, 
  - 이 인수가 입력되면  자동으로 생성되는 인수별 설명을 출력하는 함수 printMessage()를 출력하는 것이 일반적이다.
  - 이러한 help인수가 입력되었는지를 알기 위해서는 함수 has()를 이용하여
  - 예를 들어, has("help"), has("h"), has("?") 의 반환값이 true인지를 확인하면 된다.

ㅇ 인수처리 지정방법 

  - 이제 포맷을 살펴보면서 전체적으로 이해해 보자
  - 지정내용은 큰 따옴표 (")로 둘러쳐진 문자열로 표현되는데,
  - 각 인수들은 중괄호 ({  })로 구분된다.
  - 그리고 중괄호 내부는 vertical bar(|)에 의해 3부분으로 구분되는데,
  - 첫 번째 부분은 '인수 이름', 두 번째 부분은 'default value',  세 번째 부분은 설명이다.
  - default value라는 것은 인수를 입력하지 않았을 경우에 그 인수의 값이 된다.
  - 설명이라는 것은, 그 인수에 대한 설명으로 이것은 help 메시지에 의해 자동 출력된다.
  - 인수이름 중에서 @으로 시작하는 것은 인수 위치와 관련된 것으로
  - 가장 앞에 있는 인수는 첫 번째 나타나느 @이름의 인수가 되는 것이다.
  - 인수이름에 여러 개의 이름을 공백으로 구분하여 입력할 수 있는데, 이들은 모두 동일한 인수를 가리킨다. 
  - 그리고 @이름이 아닌 인수이름은 명령행에서 입력할 때 -이름으로 입력된다.
  - 인수지정의 한 예는 아래 그림과 같다.

ㅇ CommandLineParser 클래스의 메소드들

  - 지정된 인수내용에 따라 보다 쉽게 인수들을 구분해 낼 수 있고,
  - 보기 편한 help message를 자동생성하기 위해서,
  - 이 클래스는 다음과 같은 메소드들을 제공한다.
  - help message는 인수사용방법을 설명하는 출력문을 말한다.
  - 이에 대해서는 앞에서 간략히 설명했었고, 뒤의 예에서는 실제 help message가 어떻게 나오는지 볼 수 있다.
  - 우선 help message 생성에 관한 메소드들부터 살펴보면,

ㅇ 메소드 about(string msg) of CommandLineParser

  - 이 메소드를 이용하면, 
  - help 메시지를 출력할 때 보이는 초반부의 설명 메시지를 설정할 수 있으며,
  - 이것은 아래 실행 예에 나와 있다.

ㅇ 메소드 printMessage() of CommandLineParser

  - 각 인수에 대한 설명을 자동으로 생성하여 출력하는 메소드로,
  - 각 인수별로 지정한 설명들과 default value가 설명에 포함된다. 

ㅇ 메소드 get<type>() of CommandLineParser 

  - 입력된 인수 중에서 특정 인수값을 추출할 때 사용되며
  - 예를 들어, get<double>("N")은
  - -N=100.0 이라면,  인수 N에 의해 설정된 인수 100.0을 반환한다.
  - '<double>'은 추출된 값의 data type을 말한다.
  - 요약하자면, 이 메소드는 명령행에 입력된 문자열에서 인수들의 값을 추출하는 역할을 하게 된다.

ㅇ 예제 프로그램

   - 명령행에서 다음과 같은 인수들을 입력했다고 가정하자

- 1) 첫 번째 인수이고 값은 test.jpg
- 2) 두 번째 인수이고, 값은 test2.jpg
- 3) 세 번째 인수는 --help 로 이는 -help와 같다.

  
  - 이러한 인수를 처리하는 프로그램은 아래와 같다.
    . line 12- 15: 처리하는 인수에 대한 지정 문자열
 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
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int init_brightness = 100;
int init_contrast = 100;

Mat image;

const char* keys = { "{@imageName1| fruits.jpg|input image file} \\
{@imageName2| fruits.jpg|input image file} \\
{N | 1.0 | this is double value} \\
{help h ? | | this is help}" };


int main(int argc, char** argv)
{
	CommandLineParser parser(argc, argv, String(keys));
	parser.about("This is test program for commandlineparser of opencv by kyungkoo jun");
	string inputImage1 = parser.get<string>("@imageName1");
	string inputImage2 = parser.get<string>("@imageName2");

	cout << "argument1 is : " << inputImage1 << endl;
	cout << "argument2 is : " << inputImage2 << endl;
	cout << "argument N is : " << parser.get<double>("N") << endl;

	if (parser.has("help") == true)
	{
		cout << "have help" << endl;
		parser.printMessage();
	}
	else
	{
		cout << "no help" << endl;
	}
	return 0;
}

ㅇ 위 프로그램의 실행결과
- 1) line 22, 23에서 추출한 두 개의 인수를 출력
- 2) 인수 N은 입력되지 않았지만, default value로 1이 지정
- 3) --help가 입력되었고 (line 29)
- 4) 자동생성되는 help 메시지는 각 인수별 설명을 자동으로 만들어 낸다.
특히, 메시지의 초반부에는 line 21에서 메소드 about()로 설정한 문자열이 출력된다.







반응형
LIST






ㅇ Contrast Enhancement
  - 이미지에서 너무 밝은 부분이나, 어두운 부분은 자세한 내용을 보기가 어렵다.
  - 비슷한 intensity를 갖는 픽셀들이 한 곳에 모여있기 때문이다.
    . intensity란, 예를 들어, gray image에서 픽셀은 0(검정색)부터 255(흰색) 값을 갖는데, 이 값을 의미하는 것이다.
    . color image는 RGB, 3개의 채널을 갖는다면, 하나의 픽셀은 각 채널별로 intensity를 갖게 된다.
    . 여기서는 gray image만 생각하자.
  - 이를 개선하는 것을 contrast enhancement라고 한다.
  - 차이 또는 대조 (contrast)를 개선 (enhance)한다는 뜻으로,
  - 차이가 안 나던 픽셀들의 intensity를 바꿔서 차이가 나도록 한다는 것이다.
  - 이것을 위해서 사용되는 방법이 histogram equalization인데, 이에 대해서 살펴보면,,,

ㅇ 히스토그램 (Histogram)
  - 이것은 intensity별로 픽셀들의 개수를 나타내는 그래프이다.

ㅇ Histogram modeling, Histogram transfer
  - 픽셀들의 intensity를 변경하게 되면,
  - 이에 따라 히스토그램도 변경되는데, 이를 histogram modeling 또는 histogram transfer라고 한다.
  - 이러한 변경은 앞에서 설명했듯이, 잘 안 보이는 부분을 잘 보이게 한다든지 하는 contrast enhancement를 하기 위해서이다.

ㅇ Histogram equalization
  - histogram modeling (tranfer)는 intensity를 변경하는 것이기 때문에,
  - 여러 가지 방법들을 생각해 볼 수 있는데,
  - 그 중에서도 contrast enhancement를 목적으로 하는 대표적인 방법이 histogram equalization이다.
  
ㅇUniform histogram 
  - histogram equalization의 목적은 intensity들이 골고루 사용되도록 하는데 있다.
  - 비슷한 intensity들만 많이 사용되면, 이미지에서 그 부분은 잘 안보이기 때문이다.
  - 그래서, 궁극적인 목적이자, 가장 바람직인 equalization은
  - intensity들이 모두 동일한 빈도수로 사용되는 것이다. (하지만 이것은 목표일 뿐이고, 실제는 그렇지 않다.)

ㅇ Histogram Equalization의 효과

- 아래와 같은 히스토그램이 있다고 하자.
- x축은 intensity이고, y축은 값들의 빈도수이다.
- 예를 들어, intensity 2는 빈도수가 30이고,
- 최대 빈도수를 갖는 intensity는 4인 것을 알 수 있다.
- 그런데,
- intensity들이 2 ~ 4에 몰려있는 것을 볼 수 있다. (1번으로 표시)
- Histogram equalization은 이러한 집중을 완화시키는 것으로, 다음 그림을 보자.


- 위의 히스토그램을 완벽하게 equalization하면, 
- 아래와 같이 모든 intensity들이 동일한 빈도수를 갖게된다.
- 하지만, 실제에서는 이러한 것을 달성하기가 어렵고,
- OpenCV에서는 CDF (cumulative distribution function)을 이용한 방법을 이용한다.


ㅇ OpenCV의 histogram equalization 지원
  - equalizeHist( )라는 함수를 제공하는데,
  - 이 함수에 이미지를 인수로 주면, equalized된 이미지를 돌려준다.

void equalizeHist( InputArray src, OutputArray dst): gray image, src 8 bit single channel image
  - 함수 링크
 - src: 입력이미지, dst: equalized된 출력이미지

  - 이 함수는 Cumulative Distribution Function (CDF)를 이용해서 equalization을 수행한다.


ㅇ CDF를 이용한 Histogram Equalization 원리
  - 단계별 수행내용을 그림으로 나타냈다.

- 1 단계
- Histogram에 대해 cumulative sum을 계산한다.
- 좌측에서 우측으로 가면서,
- 각 값들에 대해, 자신과 이전값들의 빈도수 합을 구하는 것이다.
- 값이 커지면서, 빈도수가 점차 증가하는데,
- 이는 이전값들의 빈도수에 자신의 빈도수를 더해 나가기 때문이다.


- 2단계
- 위의 결과에 대해 y축의 값을 normalize한 것으로,
- 빈도수들을 0 ~ 10까지로 rescaling한 것이다.
- 예를 들어, 위의 그램에서 값3에 해당하는 빈도수가 80정도 였는데,
- 이는 4로 normalize된다.
- 이렇게 하는 이유는.... 다음 그림을 보면


- 3단계
- 위에서 normalization을 하면
- 값에 대한 맵핑(mapping)을 얻을 수 있는데,
- 무슨 의미냐 하면,
- 아래 그림과 같이, 
- 값 2를 mapping해서 2로, (번호 1)
- 값 3은 4로 (번호 2)
- 값 4는 10으로 (번호 3)으로 바꾸는 대응을 알 수 있다는 것이다.
- 이 mapping을 이용해서,
- 기존 값을 새로운 값으로 변경시키는 것이다.
- 왜, 이런 mapping을 수행하냐면,,,, 다음 그림에서


- 새로운 값으로 바꾸고 난 후에,
- 히스토그램을 다시 그리면, 아래 그림과 같다.
- 기존에는 값 2 ~ 4에 모두 몰려있었는데,
- 새로운 그림에서는 2 ~ 10까지 펼쳐져 있게 된다.
- 즉, 완벽한 equalization은 아니지만, 
- 그래도, 히스토그램이 전체적으로 퍼지는 효과를 얻게 되는 것이다.



ㅇ MATLAB 소스코드
  - 위의 결과들을 얻기 위한 프로그램
 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
%
% 히스토그램 equalization
%
clear all;
close all;

% 테스트 데이터
% intensity - value
% data = [0 1;
%     1 2;
%     2 30;
%     3 50;
%     4 2;
%     5 2;
%     6 2;
%     7 30;
%     8 50;
%     9 2;
%     10 1;
%     ];

data = [0 1;
    1 2;
    2 30;
    3 50;
    4 100;
    5 2;
    6 2;
    7 1;
    8 1;
    9 2;
    10 1;
    ];

max_intensity = size(data,1)-1;

% 바그래프로 데이터를 출력
figure;
bar(data(:,1), data(:,2));
title('Original Histogram');
ylim([0, max(data(:,2)+10)]);

disp(sum(data(:,2)))

% cumulative 합을 계산
% intensity - cumulative 합
data_temp = data;
data_temp(:,2) = cumsum(data(:,2));
data1 = data;
data1(:,2) = round(max_intensity*cumsum(data(:,2))/sum(data(:,2)));

figure;
bar(data_temp(:,1), data_temp(:,2));
title('Cumulative Sum');
ylim([0, max(data_temp(:,2)+10)]);

% 바그래프로 cumulative합을 출력
figure;
bar(data1(:,1), data1(:,2));
title('Normalized Cumulative Sum');
ylim([0,max_intensity+1]);

% equalized data
equData = data;
equData(:,2) = zeros(max_intensity+1, 1);

for k=1:max_intensity+1
    newIntensity = data1(k,2);
    newIntensityIdx = newIntensity+1;
    equData(newIntensityIdx, 2) = equData(newIntensityIdx,2)+ data(k,2); 
end

figure;
bar(equData(:,1), equData(:,2));
title('Equalized Histogram');

disp(sum(equData(:,2)))






반응형
LIST





ㅇ OpenCV에서 제공하는 함수 중에 normalize( )가 있다.
  - 이 함수는 값들을 새로운 범위로 변환해주는 역할을 한다.
  - 예를 들어, 원래 값들이 1부터 10부터 사이에 있다면, 
  - 이들을 0부터 1사이 값들로 변환한다.
  - 변환 값들은 원래 값에 비례한 크기를 갖게 된다.

ㅇ 아래 프로그램은 원래 값들이 2 ~ 7일 때, 이를 1 ~ 10사이의 값으로 변환하는 예이다.
- 1) 실수형 값 10개가 주어졌을 때 이를 Mat로 만든다.
- 2) normalize함수의 인수로 입력값을 의미
- 3) 출력값을 의미
- 4) 변환방식이 NORM_MINMAX일 때, 변환 최소값
- 5) 변환 최대값
- 6) 변환 방식을 의미한다.


ㅇ 함수 normalize에 의해 변환된 결과는 아래와 같다.
- 변환 전의 원래 데이터이다
- 값들이 2 ~ 7까지의 값들을 갖는다.

- 변환범위가 0 ~ 1일 때의 결과이다.
- 원래 값에서 최소값이었던 2가 변환 후에는 0으로, 
- 최대값이었던 7이 1로 변환된 것을 볼 수 있다.

- 변환범위가 1 ~ 10일 때의 결과이다.
- 원래 값에서 최소값이었던 2가 변환 후에는 1으로, 
- 최대값이었던 7이 10로 변환된 것을 볼 수 있다.


ㅇ normalize를 이용한 예제 프로그래이다.
 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
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
	float data[10] = { 3, 4, 5, 6, 7, 3, 4, 2, 5, 3 };

	Mat m(1, 10, CV_32F, data);

	normalize(m, m, 1.0, 10.0, NORM_MINMAX);

	for (int r = 0; r < m.rows; r++)
	{
		for (int c = 0; c < m.cols; c++)
		{
			printf("%.2f ", m.at<float>(r, c));
		}
	}
	cout << endl;
	
	return 0;
}

  - line 9-11: 1차원배열을 이용하여 Mat 값을 만든다
  - line 13: normalize 수행
  - line 15-19: 변환 후의 값을 출력한다

ㅇ normalize()의 간단한 용도
  - Gray image의 픽셀값의 범위를 조절하여, 이미지를 전체적으로 밝게 할 수 있다.
  - 아래 야경이미지는 전체적으로 어둡다.
  - normalize를 이용해서 픽셀들의 값을 128 ~ 255로 바꾸면, 이미지 전체가 환해지도록 할 수 있다.

- 함수 normalize()를 이용하여
- 위 이미지의 픽셀값들을 128 ~ 255로 변환한 결과


ㅇ 위 결과 관련한 소스코드

 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
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


int main(int argc, char** argv)
{

	Mat gimage, nimage;
	Mat src = imread("img_osaka_night.jpg");
	resize(src, src, Size(), 0.15, 0.15);

	cvtColor(src, gimage, CV_BGR2GRAY); 

	normalize(gimage, nimage, 128, 255, NORM_MINMAX);

	imshow("Original", gimage);
	imshow("Normalized:128-255", nimage);

	cout << ("Press any key to exit...\n");
	waitKey();

	return 0;
}

  - line 17: 픽셀값을 128 ~ 255로 변환하기 때문에, 전체적으로 환해진다.



반응형
LIST





ㅇ OpenCV (3.2.0)을 이용한 파노라마 생성

  - OpenCV에서는 여러 장의 사진을 합쳐서 하나의 파노라마 사진을 만들어내기 위한
  - Stitcher 클래스를 제공한다.
  - 이 클래스는 사진을 하나로 합치는 옵션을 2개 제공하는데,
  - SCANS와 PANORAMA이다.

ㅇ 다음 3장의 사진들이 주어졌을 때,


ㅇSCANS 모드로 파노라마를 만들면, 
 


ㅇ PANORAMA 모드로 만들면,


ㅇ C++ 구현 소스코드

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

int main()
{
     // 파노라마를 구성하는 사진들을 읽어서 vector에 저장한다.
     vector<Mat> imgs;

     // 3장의 이미지 이름들. sIMG_4939, ..., sIMG_4941
     int img_start_num = 4939;
     int img_end_num = 4941;

     // 파일들을 읽어서 이미지 벡터에 저장
     for (int i = img_start_num; i <= img_end_num; i++)
     {
          string _fName = "sIMG_";
          _fName = "panorama_sea/"+_fName + to_string(i) + ".jpg";
          Mat _img = imread(_fName.c_str());

          if (_img.empty() == true)
          {
               cout << "unable to read " << _fName << endl;
               return -1;
          }
          imgs.push_back(_img);
     }

     Mat pano; // 파노라마 결과
   
     // 파노라마 이미지 생성 
     Ptr<Stitcher> stitcher = Stitcher::create(Stitcher::PANORAMA, false);
     Stitcher::Status status = stitcher->stitch(imgs, pano);

     if (status != Stitcher::OK)
     {
          cout << "Can't stitch images, error code = " << int(status) << endl;
          return -1;
     }

     // 결과를 파일로 저장
     imwrite("panorama_320_scans.jpg", pano);
     cout << "Panorama creation ended successfully." << endl;
     return 0;
}



반응형
LIST

'Computer Vision by OpenCV' 카테고리의 다른 글

Histogram Equalization의 원리  (0) 2017.04.11
OpenCV normalize  (0) 2017.04.07
OpenCV Mat 이미지 형식 출력하기  (0) 2017.01.14
Connected Component Analysis  (1) 2016.07.11
Mathematical morphology (모폴로지 연산)  (0) 2016.07.11




횡단보도 바닥에
LED로 신호등을 표시하는 기술을 개발했다.
걸으면서 스마트폰 보는 사람들이
신호를 볼 수 있도록


반응형
LIST




ㅇ 이클립스에서 콘솔창으로 출력을 하면 한글이 깨질 때가 있다.

ㅇ 인코딩 설정이 제대로 되지 않았기 때문인데,

ㅇ 이를 고치려면 다음과 같이 하면 된다.

- 콘솔창 출력에서 한글이 깨지면 아래와 같이 나온다. 



- 프로젝트를 선택하고,
- 오른쪽 마우스버튼을 클릭하고
- Run As / Run Configuration을 클릭




- 새로운 창 (Run configurations)이 열리면,
- 맨 오른쪽 탭 (Common)을 선택하고,
- Encoding 부분을 보면 한글과 맞지 않는 인코딩형식이 설정되어 있는 것을 볼 수 있다.




- 보통 한글은 UTF-8 인코딩으로 설정한다.




- 설정 완료후 출력을 확인하면,
- 아래와 같이 한글이 제대로 출력된 것을 볼 수 있다.




반응형
LIST










반응형
LIST

'취업' 카테고리의 다른 글

면접 자리에서: 면접요령  (0) 2015.04.02


C언어 프로그래밍을 할 수 있는 환경 갖추기

  - 1) Visual Studio 2017 설치

  - 2) VS2017을 사용하여 간단한 프로그램 작성하여 실행하기


ㅇ 인터넷 브라우저에 다음 주소를 입력하여 다운로드 사이트로 이동



ㅇ Visual studio 2017 RC (Release Candidate) 버전을 다운로드
  - 아래 그림의 "무료 다운로드"를 클릭


ㅇVisual Studio (짧게 줄여서 VS) 2017도 여러 가지 버전이 있는데
  - 이 중에서 Visual Studio Community 2017 RC 버전을 다운로드
  - 학생 교육용으로 사용되는 간단한 버전
  - 나머지 Professional과 Enterprise버전은 상용 개발에 사용하고, 다른 기능이 많음



ㅇ 다운로드받은 파일을 실행하면 설치단계로 넘어가는 데,
  - 1) 'C++를 사용한 데스크톱 개발' 선택
  - 2) 설치할 위치를 선택: 기본적으로 제시되는 것을 그대로 놔둔다
  - 3) 설치버튼을 누른다.
  - 이후에는 네트워크를 통해 파일을 다운로드 받으며 설치가 진행되기 때문에 다소 시간이 걸린다.


ㅇ 설치완료 화면
  - 실행버튼을 눌러 시작


ㅇ VS를 실행하면 로그인하라고 나오는데
  - 나중에 로그인을 눌러서 진행


ㅇ 에디터 색상 테마를 고를 수 있다.
  - 자신의 마음에 드는 것을 선택


ㅇ VS가 시작되는 모습
  - 처음엔 시간이 걸릴 수도 있다.


ㅇ VS의 시작화면


ㅇ VS를 사용하는 첫 단계는 프로젝트 생성
  - 파일 --> 새로 만들기 --> 프로젝트 선택


ㅇ 프로젝트 옵션을 설정
  - Win32 콘솔 응용 프로그램을 선택
  - 이름: 자유롭게 지정
  - 위치: 파일이 저장될 위치를 지정, 본인이 원하는 위치를 지정하거나, 기본 위치를 선택
  - '솔루션용 디렉토리 만들기'를 체크: 프로그래밍 관련된 파일들을 한군데 모을 수 있다.
  - 확인 버튼을 눌러서 다음 단계로 진행


ㅇ 응용프로그램 마법사가 시작되면
  - 다음 버튼을 눌러서 진행


ㅇ 마법사에서 다음과 같이 선택하고
  - 콘솔응용프로그램
  - 빈 프로젝트를 체크
  - 마침 버튼을 눌러서 진행


ㅇ 프로젝트가 만들어지면 솔루션 탐색기에 ConsoleApplication1이라는 프로젝트 이름이 나타난다.
  - 프로젝트는 소스파일, 헤더파일, 리소스 파일 등으로 구성된다.


ㅇ 프로그래밍을 하기 위해
  - 소스파일을 오른쪽 마우스버튼 클릭
  - 추가 --> 새 항목 선택


ㅇ새항목 추가 화면에서
  - C++ 파일 선택하고,
  - 소스코드 파일이름을 입력: 확장자는 .cpp, 한글이름을 해도 된다.


ㅇ솔루션 탐색기에 방금 생성한 '소스.cpp'가 보인다.


ㅇ 솔루션 탐색기에서 '소스.cpp'를 선택하면
  - 프로그램 소스를 입력할 수 있는 창이 열린다.


ㅇ 아래 프로그램을 그대로 입력한다.
  - 화면에 'hello.world'를 출력하는 프로그램이다.
  - 글자들의 색깔은 자동으로 입혀진다.
  - 대소문자를 구분해야 하고
  - 가능하면 띄어쓰기도 보이는대로...


ㅇ 작성한 프로그램을 실행하려면
  - 디버그 --> 디버그하지 않고 시작을 선택
  - 단축키는 Ctrl+F5


ㅇ 소스코드를 실행가능한 형태로 만드는 '빌드'가 시작
  - 예 버튼을 누른다.
  - 이 화면을 보이지 않게 하려면 '이 대화 상자를 다시 표시 안 함' 체크


ㅇ 실행결과
  - 화면에 검은색 창, 이것을 콘솔창이라고 하는데,
  - 여기에 'hello.world'가 출력된다.

반응형
LIST





Mat 클래스에 저장된 이미지의 타입을 출력하는 utility함수는 다음과 같다.


The following is a utility function you can use to identify the types of OpenCV matrices at run-time.



void showType(Mat &m) 
{
	string r;
	int type = m.type();

	uchar depth = type & CV_MAT_DEPTH_MASK;
	uchar chans = 1 + (type >> CV_CN_SHIFT);

	switch (depth) 
	{
		case CV_8U:  r = "8U"; break;
		case CV_8S:  r = "8S"; break;
		case CV_16U: r = "16U"; break;
		case CV_16S: r = "16S"; break;
		case CV_32S: r = "32S"; break;
		case CV_32F: r = "32F"; break;
		case CV_64F: r = "64F"; break;
		default:     r = "User"; break;
	}

	r += "C";
	r += (chans + '0');

	printf("Matrix: %s %d(w)x%d(h) \n", r.c_str(), m.cols, m.rows);
}



반응형
LIST

alice.txt


위 파일을 이용해서 Huffman coding 프로그램의 동작을 테스트합니다.


반응형
LIST


태양광 비행기


태양광으로 충전하면서 일본에서 하와이까지 7,211 km를 날다.

시속 60km로 117시간 52분 (약 5일)동안 날아간 비행기.

2016.6.28 ~ 2016.7.3



낮에는 10km 상공에서 태양광 충전, 밤에는 2km 상공에서 비행

비행기는 무게 2톤,



조종사는 1명.

냉장고만한 공간에서 설 수도 없고, 용변도 그자리에서 해결

요가, 명상, 호흡법을 훈련하여 좁은 공간에서도 견딜 수 있도록 대비



5일 비행 끝 착륙 몇 시간 전부터,

조종사는 지루한 비행이 끝나는 것보다,

10년간 준비한 비행의 마지막 시간을 즐겼다고..



지구 한 바퀴를 도는 것이 목적.

지난 4월 아랍에미레이트의 아부다비에서 출발


2016년 7월 현재, 하와이에서 미국 애리조나주로 비행을 준비중




이미지출처: solar impulse





반응형
LIST




Connected Component Analysis 


이미지 안의 물체를 찾아내어 분석하기 위한 목적으로 thresholding에 기반한 이진화 기술들이 개발되었으며, 이진화 결과에서 노이즈를 제거하고, 결과들을 보다 정교화하기 위해서 mathematical morphology 기술들이 개발되었다. Morphology까지 거친 이미지들을 이용하면 물체를 구분 (segmention)하고, 물체를 인식 (object recognition)할 수 있는데, 그 전에 픽셀들을 물체 단위로 구분하여야 한다. 

Connectedness

하나의 물체를 구성하는 픽셀들은 이미지에서 서로 연결된 형태로 나타나게 되는데, 픽셀들이 연결되었는지를 판단하는 방법에는 2가지가 있다. 하나는 4-adjacency인데, 이것은 픽셀의 상하좌우만 연결로 인정하는 것이고, 다른 하나인 8-adjacency는 대각선에 대해서도 연결을 인정하는 것이다. 

문제는 이미지에 4, 8 adjacency를 적용했을 때 제대로된 결과를 얻기가 어려운 경우가 생긴다는 것이다. 예를 들어, 다음과 같은 이미지가 있다고 하자.


여기에 4나 8 adjacency를 적용했을 때, 원하는 결과를 얻기가 어렵다. 아래에서 좌측에 있는 그림은 8 adjacency를 적용한 결과인데, 백그라운드들이 하나로 연결된 것으로 분류한다. 하지만, 명확히는 백그라운드는 2개의 중앙링에 의해서 3부분으로 나누어져야 한다. 우측에 있는 그림은 4 adjacency를 기반으로 해서 찾은 connected component들인데, 이것은 전혀 원하는 결과가 아니다.
 


하지만, 4와 8 adjancency를 번갈아가며 적용하는 방법을 사용하면 조금 낫다. 아래 그림이 원하는 결과이다. 백그라운드가 흰색으로 구분되었고, 초록색, 빨간색으로 두개의 링이 구분되었으며, 살구색과 파란색으로 링 사이의 공간이 제대로 구분되었음을 알 수 있다. 이것을 얻기 위해서, 4, 8 adjacency를 번갈아가며 적용하였다. 맨 바깥쪽에는 4, 바깥쪽 링에는 8, 이런 식으로 교대로 적용한 결과이다.


Connected Component 찾아내기
Binary image에서 물체를 구성하는 연결된 픽셀들을 찾아내는 알고리즘은 예상 외로 간단하다. 알고리즘은 2-pass로 구성되는데, 첫 번째 패스에서는 4 혹는 8 adjacency 어느 것을 이용하건 간에, 좌에서 우로, 위에서 아래로 한 픽셀씩 검사하면서 previous neighboring pixel과 동일한 label을 지정하면 된다. 만약 최초 픽셀이라면 새로운 label을 할당하면 된다. x로 표시된 픽셀의 previous neighboring pixel들은 아래 그림과 같이. 위쪽 혹은 왼쪽에 위치해있는 픽셀들로서, 왼쪽 그림은 8 adjacency의 경우, 오른쪽 그림은 4 adjacency의 경우의 previous neighboring pixel들을 보여준다.

두 번째 패스에서는 다른 label이지만 같은 영역에 속하는 label들을 찾아서, label을 하나로 합치는 단계를 수행한다. 통합할 label을 찾는 것은, 어느 픽셀에 대해서 previous neighborng label이 두 개 이상일 경우, 이 들 label들은 서로 같은 label이므로 하나로 합쳐져야 된다. label 합치는 과정을 쉽게 하기 위해서, label이 정수라고 한다면, 서로 다른 label들을 합칠 때는, 낮은 숫자를 갖는 label로 통합하도록 하면된다.

OpenCV에서는 connected component를 찾기 위한 함수로 findCountours를 제공한다. 이 함수의 원래 목적은 이미지에서 물체의 윤곽선을 구성하는 픽셀들을 구한다. 사용방법은 아래와 같다.

vector<vector<Point>> contours;
- 수행 결과 여러 개의 윤곽선들이 나오게 되는데, 이 윤곽선들이 배열을 구성하고, 각 배열의 요소는 윤곽선인데, 윤곽선은 이를 구성하는 픽셀들으 배열이다.

vector<Vec4i> hierarchy;
- 윤곽선마다 고유의 번호를 매겨, 윤곽선 간의 관계를 표시한다. 각 윤곽선은 4개의 정보를 갖는데, 첫 번째는 다음 윤곽선, 두 번째는 이전 윤곽선, 세 번째는 안에 포함된 윤곽선, 네 번째는 밖을 감싸고 있는 윤곽선. 만약 이러한 정보가 필요없다면 음수로 채워진다.

findContours(binary_image, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
contour에 의해 물체 윤곽선을 구했다면, openCV함수를 이용해서 물체들을 각각 다른 색으로 칠할 수 있다. 예를 들어 다음 코드를 이용하면 된다.
Scalar colour(255, 0, 0);
blue색을 지정


drawContours(contours_image, contours, contour, colour, CV_FILLED, 8, hierarchy);
contours_image: 물체 윤곽선이 그려지는 이미지
contours: 함수 findContours를 이용해서 구한 윤곽선들
contour: 어느 윤곽선인지를 지정하는 숫자
CV_FILLED: 윤곽선 내부를 채운다.
hierarchy: 윤곽선 간의 관계로 함수 findContour에 의해 구해진 것이다. 이 정보는 윤곽선 안에 또 다른 윤곽선이 포함되어 있을 경우, 내부 윤곽선을 칠하는 범위에서 제외하기 위해서 사용된다.

위 함수들을 이용한 예는 아래와 같다. 맨 왼쪽 사진은 입력이미지 위에다가 발견된 contour들을 청색으로 표시한 것이다. 중간 것과 이진화 이미지이며, 여기에 closing morphology (20x20 isotropic structuring element 사용) 연산을 적용한 것이 오른쪽 결과이다.


위 결과를 얻기 위한 프로그램의 소스코드는 아래와 같다. line 34 ~ 36의 함수 imshowAfterResize( )는 인수로 주어진 (이 경우 320) 값으로 이미지의 폭을 resizing한 후 display하는 함수로 자체 제작한 함수이다.


 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
int main()
{
	Mat org_img;
	org_img = imread(IMG_NAME);

	if (org_img.empty() == true)
	{
		cout << "Unable to read " << IMG_NAME << endl;
		return 0;
	}

	Mat gray_img;
	cvtColor(org_img, gray_img, CV_BGR2GRAY);
	Mat bin_img;
	adaptiveThreshold(gray_img, bin_img, 255.0, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 101, 0);

	Mat closed_img;
	Mat structuring_element(20, 20, CV_8U, Scalar(1));
	morphologyEx(bin_img, closed_img, MORPH_CLOSE, structuring_element);

	// find contours
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;

	Scalar colour(255, 0, 0);
	Mat copied_closed_img = closed_img.clone();
	findContours(copied_closed_img, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);

	for (int i = 1; i < contours.size(); i++)
	{
		drawContours(org_img, contours, i, colour, CV_FILLED, 8, hierarchy);
	}

	imshowAfterResize("original image", org_img, 320);
	imshowAfterResize("bin image", bin_img, 320);
	imshowAfterResize("closed image", closed_img, 320);

	waitKey(0);
	return 0;
}


반응형
LIST

Mathematical morphology (모폴로지)

Thresholding에 의해 얻어진 binary image를 좀 더 가공하여 완성도를 높이기 위해서는  morphology를 이용한다. 가공의 의미는 noise나 너무 작은 feature들을 제거하는 것을 의미한다. 

Morphology는 structuring element (SE)라는 것을 이용하는데, 이것은 마치 bit mask와 같은 것이다. Bit-mask가 1차원인데 반해, SE는 2차원이다. SE를 이미지 위에 슬라이딩 시켜가면서 logical operation을 수행하여 결과 이미지를 만든다. original image는 변하지 않고, 새로운 이미지가 만들어진다. 이 분야는 독자적인 학회를 가지고 있을 정도로 이론적으로 정립되어 있다. 

SE는 여러 가지 모양을 가질 수 있는데, 일반적으로 정사각형 모양이면 isotropic이라고 한다. SE를 구성하는 픽셀 들 중의 하나를 origin이라고 한다. 이 origin을 이미지의 각 픽셀마다 매칭시켜가면서 morphology가 수행된다. 보통은 이미지의 좌상단에 origin을 두고, 이후에는 좌에서 우로, 위에서 아래로 움직여가면서 순차적으로 수행한다. 수행결과가 나오면 origin 위치의 이미지 픽셀의 값이 결정된다. 아래 그림은 3x3, 5x5 isotropic SE들의 예를 보여주며, 가운데 x로 표시한 픽셀이 origin이다.


대표적인 morphological operation에는 dilation, erosion, opening, closing 등이 있다. 여기서는 각각에 대해서 알아본다.


1. Dilation

사전적인 의미로는 확장하다, 크게하다라는 의미로, 이미지 상의 물체를 확대시키는 효과를 가진다. 쉽게 이해하자면, SE origin과 매칭된 이미지 상의 픽셀을 SE의 모든 다른 픽셀들로 확대한다고 보면 된다. 아래 그림에서는 3x3 isotropic SE을 이용하여 dilation을 수행할 때 어떠한 결과가 생기는지를 간략히 보여준다. 

OpenCV 는 dilation을 위한 함수 dilate(binary_image, dilated_image, Mat())를 제공한다. 이 함수에 의해서, binary_image는 default 3x3 SE를 이용하여 dilation되고, 그 결과는 dialated_image에 저장된다. 다른 SE를 사용하려면, 예를 들어, 5x5 isotropic이라면, 아래와 같이 한다.
Mat structuring_element(5, 5, CV_8U, Scalar(1));
dilate(binary_image, dilated_image, structuring_element);

아래 두 개의 예는 dilation의 적용결과를 보여준다. 그림 1과 그림 2는 동일한 컬러 입력이미지에 대해서, 두 가지 thresholding방식을 적용하고, dilation (3x3 isotropic structuring element)을 수행한 결과이다.

그림 1은 왼쪽의 입력이미지에 대해 Otsu threshoding을 이용하여 중간결과를 얻었다. 입력 이미지 전체의 조명상황이 고르치 않기 때문에, 하나의 threshold를 이용하는 Otsu방법의 결과에는 한계가 있다. 좌하단 영역이 소실된 것을 알 수 있다. 여기에 dilation을 수행하여 오른쪽 결과를 얻었는데, 글자들이 굵어진 것을 알 수 있다.

[그림 1: Otsu thresholding을 이용한 binary image에 dilation을 적용한 결과]


그림 2는 영역별로 다른 threshold를 사용하는 adaptive thresholding을 적용한 결과로 고르지 않은 조명상황에도 불구하고, 이미지 전체가 고루 반영되는 것을 볼 수 있다. (중간이미지). 하지만 전체적으로 noise가 상당히 많은 것을 볼 수 있다. 여기에 dilation (3x3 isotropic SE)을 적용하면 noise의 상당부분이 사라진 것을 볼 수 있고, 글자체도 전반적으로 굵어진 것을 알 수 있다. 

[그림 2: Adaptive thresholding (block size=51)을 이용한 binary image에 dilation을 적용한 결과]


2. Erosion
Dilation의 반대로, 이미지상의 물체를 축소(shrinking)하는 역할을 한다. 이를 위해 SE를 완전히 포함할 수 있는 이미지상의 픽셀들만 결과 이미지에 남게 된다. 그 결과, 물체크기가 줄어들게 되고, 이미지상의 noise나 narrow feature 등이 사라지게 되는 효과가 있다. Erosion의 또다른 용도는 매칭으로도 사용할 수 있다는 것이다. SE를 찾고자 하는 패턴으로 지정하고 erosion을 실행하면결과이미지는 패턴이 발견되는 위치들이 된다.
OpenCV는 이를 지원하는 함수 erode를 가지고 있다. 예를 들어, default 3x3 isotropic structuring element 에 의한 erosion은 다음과 같이 수행한다.

erode(binary_image, eroded_image, Mat());

다른 형태의 SE를 사용하려면, 예를 들어 5x5 isotropic SE인 경우 아래와 같다. 
Mat structuring_element(5,5, CV_8U, Scalar(1));
erode(binary_image, eroded_image, structuring_element);

아래 그림 3은 dilation 예에서 사용한 동일한 입력이미지에 대해서 erosion을 수행한 결과이다. 중간이미지는 이전과 마찬가지로 adaptive thresholding (block size = 51)을 적용하였다. 오른쪽 결과는 erosion결과를 보여주는데, 3x3 SE를 온전히 품을 수 있는 흰색영역이 적다보니 대부분에서 글자가 사라지는 부정적인 결과를 보여주고 있다. 


[그림 3: Adaptive thresholding (block size=51)을 이용한 binary image에 erosion을 적용한 결과]

3. Opening

​Opening은 erosion과 dilation의 조합이다. 이미지에 erosion을 먼저 적용한 후, 그 결과에 다시 dilation을 적용하는 것이다. 이 때, 같은 structuring element를 이용한다. 그 결과, 이미지에서 noise와 narrow feature들이 제거되지만, 물체의 크기는 보존된다.


OpenCV는 opening을 위한 함수를 제공한다. 예를 들어, 5 by 5 isotropic structuring element를 이용한 opening은 다음과 같다.

Mat five_by_five_element (5, 5, CV_8U, Scalar(1));
- 5 x 5 isotropic structuring element만들기
morphologyEx(binary_image, opened_image, MORPH_OPEN, five_by_five_element);
- opening 수행


아래 그림 4는 opening을 적용한 결과이다. 위의 예제들과의 차이점이라면, 원본크기의 이미지에 opening을 모두 적용한 후 이미지 크기를 줄였다는 것이다. 중간이미지와 오른쪽 이미지를 비교해 보면, 중간이미지의 흰색이 많이 없어지고 검정색이 늘었다는 것을 볼 수 있다. 이것은 5x5 SE가 포함될 흰색부분이 많이 없기 때문에 그렇게 된 것이다.

[그림 4: Adaptive thresholding (block size=101)을 이용한 binary image에 opening을 적용한 결과. 5x5 isotropic structuring element를 적용.]


4. Closing

Mathematical morphology에서 erosion과 dilation은 서로 dual operation의 관계라고 하는데, 이는 erosion이 물체 픽셀들을 깍아내는 것이라면, dilation은 덧붙여나가는 것과 같이 서로 정반대의 역할을 하기 때문이다. Erosion에 의해서는 물체가 작아지지만, dilation은 물체가 커지게 된다. erosion과 dilation간의 dual 관계와 마찬가지인 것이 openning과 closing이다.

Closing은 opening과 유사하지만 두 동작의 적용순서가 거꾸로이다. 우선 dilation을 적용하여 결과를 얻은 후 거기에 erosion을 수행한다. 역시 마찬가지로 같은 structuring element를 이용한다. Closing은 narrow gap등을 메꾼 후, 물체 경계를 smoothing하는 효과가 있으되, 물체의 원래 크기가 보존된다.

OpenCV는 closing을 위한 함수를 제공한다. 예를 들어, 5 by 5 isotropic structuring element를 이용한 closing은 다음과 같다.

Mat five_by_five_element (5, 5, CV_8U, Scalar(1));
- 5 x 5 isotropic structuring element만들기
morphologyEx(binary_image, closed_image, MORPH_CLOSE, five_by_five_element);
- closing 수행

아래 그림 5는 closing 연산을 적용한 결과이다. 중간이미지는 위의 opening의 경우와 동일하지만, closing연산의 결과 (맨오른쪽 이미지)에서 글씨가 훨씬 더 잘 인식되는 것을 알 수 있고, 배경에 해당하는 검은 색이 대부분 사라진 것을 알 수 있다.

[그림 5: Adaptive thresholding 결과에 closing을 적용한 결과]


5. Closing followed by Opening
위의 closing의 결과에서 볼 수 있듯이, opening보다는 noise 제거 측면에서 효과가 좋다. 따라서 일반적으로 opening과 closing은 연속적으로 적용되어 이용된다. 이미지에 closing을 먼저 적용하고, 다시 opening을 수행하는데,  같은 structuring element를 이용한다. 이렇게 하면, 이미지를 전체적으로 깨끗하게 정리하는 효과가 있다.

아래 그림은 closing과 opening을 순차적으로 적용한 결과이다. 우측끝에서 두 번째가 closing을 적용한 결과이고, 맨 오른쪽이 opening까지 적용한 결과이다. 두 결과는 대체적으로 동일해 보이지만, 글자인식면에서 맨 끝의 "Test" 글자가 opening이 후에 보다 선명해진 것을 볼 수 있다. (아래 그림을 확대해서 확인)

[그림 6: closing과 opening을 차례로 적용한 결과]


6. Morphology on Gray or Color Image
기본적으로 morphology는 binary image에 적용된다. 하지만 grey-scale 이나 컬러이미지에도 적용이 가능하다. Grey-scale 이미지에 적용하는 방법은 조금 복잡한데, 각 grey-level별로 이미지를 따로 만든 후, morphology를 적용하고 이미지들을 하나로 합친다. 즉, grey-level 1을 가지는 픽셀들만 모아서 이미지1를 만들고, 이런 식으로 이미지2, 이미지3,..., 이미지 255를 만든다. 단, 이미지 0은 따로 만들 필요가 없다. 그리고 각 이미지i에 대해서 morphology 연산을 수행한 후, 각 결과이미지들을 ORing 하여 하나로 합친다.

컬러이미지에 morphology연산을 적용하는 방법도 gray-level 이미지와 유사하되, R, G, B채널별로 나누어서 각 채널을 grey-scale 이미지인 것 처럼 처리하고, 그 결과들을 합친다.

OpenCV에서 제공하는 morphology 관련 함수들은 grey-scale과 컬러이미지에 대해서도 동작한다. 방식은 위에서 설명한 방식을 따른다. 이 함수들을 사용하기 위해서는 입력영상으로 grey-scale과 컬러이미지으로 지정하면 된다.


아래 그림 7과 8은 컬러이미지에 각각 erosion (그림 7)과 dilation (그림 8)을 적용한 결과이다. 우선 그림 7부터 살펴보면, erosion은 흰색 (밝은 색)을 깍아내는 경향이 있으므로 어두운 색인 글씨들이 더 굵어지고 선명해진 것을 볼 수 있다. 그림 8의 dilation은 흰색이 확장되는 경향이 있으므로 어두운 색인 글씨들이 가늘어지고 끊어지는 것을 볼 수 있다.


[그림 7: Erosion on Color image]


[그림 8] Dilation on Color Image









반응형
LIST

+ Recent posts