국소적인 휘도 분포에 의한 전자현미경상의 영역 검출 ~Local standard deviation filter를 이용하여~

개요



일반적인 이진화(예를 들면 오쓰의 방법)가 잘 되지 않는 경우에도 사용할 수 있는 영역 검출 방법, Local standard deviation filter의 소개입니다.

SEM(전자현미경)상으로부터의 영역 검출을 예로서, 실제의 사용 방법을 해설합니다!

소개 ~ 일반적인 영역 검출 방법 ~



예를 들어 아래와 같은 이미지에서 영역을 감지하고 싶습니다.
(from 31 downloadable sample images and stacks - ImageJ )

화상 처리에 익숙한 사람이라면, 가우시안 필터로 적당히 노이즈 처리⇒오츠의 방법으로 2치화를 하는 것이 아닐까요?

이 흐름을 Python(OpenCV)로 써 보면 이런 느낌입니다.
#画像の読み込み
img = cv2.imread('blobs.png',0)

#(5×5)のガウシアンフィルタで平滑化
img = cv2.GaussianBlur(img,(5,5),0)
#大津の方法で二値化
__,th = cv2.threshold(img,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

#二値化領域をRGB化
dec = cv2.cvtColor(th, cv2.COLOR_GRAY2RGB)
dec[np.where(th == 255)] = [255,0,0]

#元画像をRGB化して重ね合わせ
img_color = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
dst = cv2.addWeighted(img_color, 0.5, dec, 0.5, 0)

#結果を保存
cv2.imwrite('result.png',dst)

결과는 아래와 같습니다.

영역 검출을 할 수 있으면, 나머지는 목적에 따라서, 이 부분의 면적 분포나 애스펙트비등을 분석할 수 있게 됩니다.

SEM 이미지에 적용



그런데, 미세 조직의 관찰에 자주 이용되는 SEM(주사형 전자 현미경)으로 촬영된 화상의 경우는, 좀처럼 영역 검출이 잘 되지 않는 경우가 많습니다.
예를 들어 아래 꽃가루의 이미지를 예로 생각해 봅시다.
(from wikimedia commons: Pollen 1000x - SEM MUSE )



이 이미지를 비슷한 방식으로 이진화하면 다음과 같이 됩니다.



잘 분리되지 않았습니다.

SEM 관찰상은 시료의 모서리에서 밝아지는 특징( 가장자리 효과 )이 있어, 영역의 내외에서 콘트라스트가 명확하게 나뉘지 않는 경우가 많습니다.

ImageJ와 같은 소프트를 사용해 2치화의 임계치(Threshold)의 값을 여러가지 바꾸어 보면 알 수 있습니다만, 실제로, 이 화상을 휘도만으로 분리하는 것은 불가능합니다.



이미지의 로컬 표준 편차를 이용한 영역 검출



그러나 사람의 눈으로 보면 꽃가루의 영역과 배경이 명확하게 나뉘어져 보이는 것도 확실합니다. 왜 그것이 아는가 하면, 꽃가루의 표면에는 특징적인 모양이 있기 때문이군요.

이와 같이, 표면의 패턴·질감(텍스처)의 차이를 잘 사용하는 방법으로서, 화상의 국소적인 표준 편차(Local standard deviation filter)를 이용한 영역 분리가 존재합니다.

이것은 가우시안 필터와 같은 공간 필터링과 마찬가지로 주목 화소로부터 설정한 폭의 내부 영역에 주목한다.



예를 들어 중앙값 필터는 이 영역 내의 픽셀의 중앙값을, 가우시안 필터는 중심으로부터의 거리의 가중 평균을 취하는 조작을 실시합니다.

반면 Local standard deviation filter는 이미지의 국소 텍스처를 비교하기 위해이 영역의 밝기의 표준 편차를 취하는 작업을 수행합니다.

Local std filter를 Python에 구현



MATLAB에서는 stdfilt이라는 함수가이 작업에 해당하지만 OpenCV에는 구현되지 않았기 때문에 다음과 같이 직접 함수로 구현했습니다.
def LocalStdFilter(img,width):

    if(width%2 == 0):
        print('width size should be odd.')
        return None

    trim_width = int((width - 1)/2)
    h,w = img.shape[:2]
    img_trim = img[trim_width:h-trim_width,trim_width:w-trim_width]
    result = np.zeros_like(img_trim)
    for n,i in enumerate(tqdm(img_trim)):
        for m,j in enumerate(i):
            y,x = n+trim_width,m+trim_width
            img_window = img[y-trim_width:y+trim_width,x-trim_width:x+trim_width]
            result[n,m] = np.std(img_window)
    result = np.pad(result,(trim_width,trim_width),'constant')

    return result

이것을 사용하여 영역 감지를 시도합니다.
필터의 크기는 우선 21로 설정했습니다.
(이미지 사이즈는 반으로 하고 있습니다)

# 画像の読み込みとリサイズ
img = cv2.imread('Pollen_1000x.png',0)
h,w = img.shape[:2]
img = cv2.resize(img, (int(w/2),int(h/2)))

# Local standard deviation filterの適用
result = LocalStdFilter(img,21)

# Std = 5を閾値に二値化
__,th = cv2.threshold(result,5,255,cv2.THRESH_BINARY)

# モルフォロジー変換で検出領域を縮小後、RGBに変換
th[np.where(th == 255)] = 1
th = cv2.erode(th,np.ones((9,9),np.uint8),iterations = 1)
dec = cv2.cvtColor(th, cv2.COLOR_GRAY2RGB)
dec[np.where(th == 1)] = [255,0,0]

# 元画像と重ね合わせて表示
img_color = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
dst = cv2.addWeighted(img_color, 0.5, dec, 0.5, 0)


1장당 15초 정도 걸립니다. 꽤 느립니다 ....
이제 결과를 봅니다.
fig = plt.figure(figsize=(9,7),dpi=200)
plt.subplot(221)
plt.title('Raw Image',fontsize=14)
plt.imshow(img)
plt.subplot(222)
plt.title('Local Std Filter',fontsize=14)
plt.imshow(result)
plt.subplot(223)
plt.title('Thresholding (Std = 5)',fontsize=14)
plt.imshow(dec)
plt.subplot(224)
plt.title('Result',fontsize=14)
plt.imshow(dst)



꽃가루의 영역이 잘 감지되었습니다!
했어

요약



보통의 2치화에서는 영역의 분리가 어려운 것 같은 SEM상에서도 사용할 수 있는 Local standard deviation filter의 소개를 실시했습니다.

이번은 영역의 특징량으로서 표준편차(std)를 이용했습니다만, 다른 특징량(2차원 FFT라든지)을 사용해 봐도 재미있을지도 모르겠네요.

솔직히 파이썬으로 돌리는 것은 무겁지만 빠지면 강한 필터입니다.
꼭 시험해보세요!

좋은 웹페이지 즐겨찾기