본문 바로가기

Python

[Python]Google Colab.에서 밤 하늘의 별 개수 세기

반응형

    밤하늘을 카메라로 찍어본 적이 있는가? 도심은 빛 공해가 너무 심해서 별을 보기 어렵지만, 조용한 시골로 여행을 떠나면 밤하늘을 수놓은 별들을 관찰할 수 있다.

Sample picture from unknown source

    그런데, 이 사진속에 별이 몇개인지 어떻게 알 수 있을까? 손으로 일일히 세기에는 너무 많은데, 컴퓨터로 셀 수 있지 않을까? 

    위와 같은 검은색 배경에서 아주 작은 점과 같은 별의 개수를 세는 것을 영상처리에서는 "Blob을 찾는다" 혹은 "Edge를 검출한다"고 한다. 사진속 무늬가 별이라는 사실을 잠깐 잊으면, 검은색 배경에 얼룩이 묻어있는 것 처럼 보이기도 한다. Blob을 찾는 알고리즘중에 가장 유명한 방법은 바로 Laplacian of Gaussian (LoG)이다.

아이와이어에서 재구성된 신경절 세포 - Source : https://wiki.eyewire.org/File:Ganglion_Cell.png

     LoG 알고리즘은 망막신경절(Retinal ganglion cells, RGCs) 세포를 모델링한 것이다. 망막신경절 세포는 눈에서 받은 정보를 뇌로 전달하는 임무를 가지고 있다. 그런데 이 망막신경절 세포는 눈에서 받아들인 정보를 그대로 전달하지 않고, 독특한 방식으로 시각적인 데이터를 압축한다. 이들은 다양한 정보들 중 변화가 있는 것 또는 Edge에 대한 부분에만 반응한다. 조금 더 구체적으로, 망막신경절 세포의 종류에 따라 빛이 정확하게 세포 중앙에 오거나, 혹은 아예 주변부에만 비칠 경우에 평소보다 크게 흥분하게 된다. 이를 이용해서 Edge를 검출해낸다. (관련 내용에 대해서는 bskyvision.com/132 블로그에 설명이 상세하게 잘 되어있다. 일독을 권한다.)

아무튼, 이를 이용한 LoG 알고리즘은 다음과 같은 단계를 가지고 있다. 

 

 1.  이미지를 Grayscale로 import한다. - Noise를 줄이기 위함이다. 

 2.  Gaussian Function을 이용해 blur처리 해준다. - 마찬가지로 Noise reduction을 해주기 위함이다. Gaussian Function $G(x,y) = exp(-\frac{x^2+y^2}{2\sigma^2})$ 이다. $x,y$는 각 이미지 픽셀의 좌표이고, $\sigma$는 표준편차를 의미한다. 즉, 표준편차가 커질수록 블러가 강해진다.

 3.  이 Gaussian에 Laplacian을 걸어서 local minima를 찾는다. 이 local minima가 바로 blob이다.

 4.  Convolution을 통해서 전체 이미지 픽셀에 대해 이 작업을 수행한다.

 

    이 작업을 Python을 이용해서 해볼텐데, skimage라는 module에 있는 blog_log 함수를 사용할 것이다. 그럼 결과로 $(y,x,\sigma)$ 의 ndarray형태의 blob의 위치와 크기에 관한 output을 받을 수 있다. 여기서 $\sigma$ 에 $\sqrt{2}$를 곱해주면 blob의 radius크기가 된다. 이를 이용해서 우리가 찾은 별에 동그라미를 쳐 볼 생각이다. 작업환경은 이번에도 역시 Google Colab. 이다. 

먼저 전체 코드는 다음과 같다. 

#Counting Stars
from skimage.feature import blob_log
from math import sqrt
import glob
from skimage import io
import matplotlib.pyplot as plt

#image import  
our_file=glob.glob('/content/drive/MyDrive/test.jpg')[0]

#showing original image
io.imshow(our_file)
plt.show()
im = io.imread(our_file, as_gray=True)

#Laplacian of Gaussian, blob_log returns (y,x,sigma)
blobs_log = blob_log(im, max_sigma=30, num_sigma =25, threshold=0.05)

#Counted blobs = len of blobs_log
numrows = len(blobs_log)
print("Number of stars detected : ",numrows)

#Compute Radius of blobs, We have 2D image
blobs_log[:, 2] = blobs_log[:, 2] * sqrt(2)

#Draw circles around blobs
io.imshow(our_file) #base image
plt.title("location where stars detected") #title
for blob in blobs_log:
    y, x, r = blob
    c=plt.Circle((x, y), r, linewidth=0.5, fill=False, color='yellow')
    plt.gca().add_patch(c)

plt.tight_layout()
plt.savefig('the_stars')
plt.show()

 그 결과 이렇게 출력 된다. 

Number of stars detected :  617

    사실 skimage 모듈에 있는 함수를 적용하는거라서 크게 어렵지는 않다. 주석에 적혀있는대로, 그리고 글 서두에서 설명했던 대로, 이미지를 as_gray=True로 불러오고, blob_log 함수를 적용해서 blob의 위치와 크기정보를 파악한다. 이걸 가지고 다시 radius를 계산한 뒤, 각각의 위치에 동그라미를 쳐 주면 끝이다. 

    어떤 논문에서는 이를 미세플라스틱의 계수에 시도한 사례도 있다. 엣지를 찾는 것을 많은 곳에 활용할 수 있다는 점이 흥미롭다. 아, 수식적인 부분에서도 글 초반부에 언급한 블로그가 유익할 것이다. 더 깊게 알고싶은 사람이 참고하기 좋은 사이트라고 생각한다. (bskyvision.com/133)

 

>>이번 게시글의 Colab 노트북 주소 : colab.research.google.com/drive/1zzF1luChekTBdY1JCmbb8oH3hhx7sd9P?usp=sharing