슬라이드 개체 인식

보다 자세하게는 현미경 소견에서 관찰되는 개체를 인식할 수 있는가에 대한 것이다. 일단 고전적인 방법으로 접근하여 보았다. 인터넷 동호회의 정보를 바탕으로 모멘트(moments)에 대한 것을 검색하기 시작했고, 컨투어(contour)가 내가 원하는 기능이라는 것을 확인했다. 유채색을 고려하는 알고리즘은 못 찾았다. 그래서, 고전적인 방법으로 해보기로 했다. 이 방법에 있어서

가장 중요한 것은 분석 대상이 잘 구분되는 gray scale 이미지 변환이다.

OpenCV를 이용하여 시도해 보았던 방법은 RGB를 HSV로 변경하여 low와 high 범위내의 색상을 추출하는 것인데, 영 시원찮았다. CellProfiler를 이용하여 추출한 흑백 이미지를 이용했다. 이 경우에도 1-channel이 아니었기 때문에 변환시켜 준다.

src = cv2.imread("seq2337_1gray.tiff", cv2.IMREAD_GRAYSCALE)

Python의 경우 함수에서 결과를 여러개 나오는 경우가 있다. 이럴 경우 순서에 맞게 출력하거나, 불필요하게 들어오는 것은 대충 처리하거나 하는 것 같다.

분석 대상이 아닌 배경 이미지는 확실하게 지워줘야 한다. threshold의 경우 첫 번째꺼는 쓰지 않는 정보이다. 2번째는 low값, 3번째는 high 값, 4번째는 출력 방식이다.

ret, gray_th = cv2.threshold(src, 100, 255, cv2.THRESH_BINARY)
아니면
gray_th = cv2.threshold(src, 100, 255, cv2.THRESH_BINARY)[1]

Contours를 찾아주는 것은 아래와 같이 하면 된다. 2번째와 3번째는 contours를 설정하는 방법에 대한 것이다. 나는 findContours의 결과 중 첫 번째 Contours에 위치에 대한 정보만 있으면 된다. R과 Python의 중요한 차이점으로 처음이 1이 아닌 0이다.

contours = cv2.findContours(
             gray_th, cv2.RETR_TREE, cv2.CHAIN_APPROX_TC89_KCOS)[0]

Contours가 여러개 나오기 때문에 반복문을 이용해서 [i]가 있다. 이미지는 항상 똑바르게 서있거나 누워 있지 않기 때문에, 기울어진 직사각형으로 contours를 찾는 minAreaRect를 이용했다.

rect = cv2.minAreaRect(contours[i])

minAreaRect에서는 결과값이 3개가 나온다. 첫 번째 항목은 그림 파일에서 중앙 위치에 대한 것으로 (X, Y) 형태이다. 두 번째는 직사각형의 (폭, 넒이)이다. 가로 길쭉과 세로 길쭉 형태를 구분하지는 않는다. 세 번째는 기울어지는 정도이다. 시계 방향이면 양수, 시계 반대 방향이면 음수인 것 같다. 나의 경우에는 폭과 넓이만 필요해서 w와 h로 결과를 받았다.

(w, h) = rect
[1]

이렇게 찾은 contour가 올바른게 확인해 보려면 역시 봐야 한다. drawContours로 할 수 있다. 첫 번째는 이미지, 두번째는 좌표이다. minAreaRect로는 사각형을 그리는게 어려우니까 boxPoints로 그릴 수 있는 4개의 좌표로 변환한다. 픽셀에 소수점은 없으니까 NumPy를 이용하여 정수로 변환한다. 3번째는 첫 번째 contour를 뜻하는 0을 기술한다. 4번째는 색상이다. 추가 옵션으로 두께 같은 것이나, 모든 contour에 대한 것도 있다. 원하는 대로 잘 찾아졌는지 Pyplot으로 그려보면 된다.

cv2.drawContours(src, [np.int64(cv2.boxPoints(rect))], 0, [255, 0, 0])

분석 속도는 꽤 빠르다.

ephemeris

저렴한 블루투스 이어폰을 구입했다. 너무 저렴해서 음질이 걱정되기는 했다. 전원을 켜고 끌 때 어색한 한국어 안내 소리가 차라리 거슬리지 방송을 청취에는 별 차이가 없는 것 같다.

DenseNet

최신 모델에 대한 공부는 안했으나, 현재 진행중인 논문에서 사용한 이미지 학습 모델 중 DenseNet-161이 가장 성능이 좋아서 이를 이용하여 후속 논문을 진행할 예정이다. 따라서, DenseNet을 좀 배워보기로 했다. DenseNet과 관련된 논문은 다음과 같다.

arXiv preprint arXiv:1608.06993

이 쪽은 결과를 우선 내는 것을 중요시 하기 때문에 피어 리뷰를 하지 않는다. 뭐, 성능 검증이 되면 그게 바로 리뷰하고 승인일텐데 굳이 그런 형식에 얽매일 분야는 아니라고 본다. 전문 지식이 없어 블로그(https://wingnim.tistory.com/39)에서 정리된 내용을 내가 필요한 부분만 다시 정리하여 보았다.

  • ResNet은 중간중간마다 이전의 결과를 받아오는 방법을 이용하였다면, DenseNet은 이전 정보들이 더 깊은 레이어까지 정보를 전달하는 방법이다. 그래서 네트웍이 조밀해(dense)진다.
https://pytorch.org/assets/images/densenet1.png

PyTorch에서 불러오는 방법은 다음과 같다. 애시당초 홈페이지에 당당하게 불러오는 방법이 안내되어 있다.

https://pytorch.org/hub/pytorch_vision_densenet/

무엇을 pretrained 했다는 것인지는 모르나, 알아 두어야 할 것은 다음이다.

  • 3-channel RGB (3 x H x W)
  • H와 W는 최소 224 pixels
  • 값은 [0, 1]로 되도록 변환
  • normalization 시행함.
from torchvision import transforms
preprocess = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

이거는 이미지 변환과 관련된 부분인 것 같다. torchvision의 transforms를 이용한다. 따라서 자료를 찾아 보았다.

https://pytorch.org/docs/stable/torchvision/transforms.html

torchvision.transforms.Pad(padding, fill=0, padding_mode='constant')

내가 필요한 것은 원하는 만큼 padding 하는 것이다. padding 항목 위치에 길이가 4인 tuple 형식으로 입력하면 왼쪽, 위, 오른쪽, 아래에 각각 채울 수 있다고 한다. fill=0은 기본값, constant는 그냥 채우는 것이다.

Charlson Comorbidity Index

환자의 중증도를 평가하기 위해서는 보통 의무기록 검토를 통해서 각종 위험인자들을 파악한다. 그런데 심평원이나 건보공단같은 빅데이터 자료를 이용한 연구에서는 의무기록을 통한 위험인자를 파악할 수 없다. 혈액검사 결과가 기본적으로 없으니 검사 결과와 연동된 것도 할 수 없다. 이럴 때 이용할 수 있는 것이 Charlson Comorbidity Index (CCI)이다.

이전에 다른 연구를 통하여 CCI에 대하여 알게 되었다. 그 때에는 몰랐는데 나중에 다른 연구를 하면서 공동 연구자가 고혈압이 없는 것 같다는 이야기를 했었다. 그 때는 그냥 지나갔는데, 이번에 다른 연구자와 연구를 진행하게 되면서 내가 다른 연구원에게 해당 정보를 알려줄려고 하니 이 부분을 알아봐야 하겠다는 생각이 들었다. 그래서 관련 논문을 찾아 보았다.

https://pubmed.ncbi.nlm.nih.gov/3558716/
J Chronic Dis. 1987;40(5):373-83. doi: 10.1016/0021-9681(87)90171-8.
A new method of classifying prognostic comorbidity in longitudinal studies: development and validation
M E Charlson, P Pompei, K L Ales, C R MacKenzie
PMID: 3558716 DOI: 10.1016/0021-9681(87)90171-8

이 논문은 유방암 환자들의 1년 사망율을 예측하고자 한 것이었다. 그래서 여러 인자들에 대하여 분석을 하였다. 그리고 다른 코호트를 이용하여 10년간 생존 분석을 해보았고, 역시 유효한 결과를 얻었다.

고혈압은 처음에 분석 인자로 고려되었다. 하지만, adjusted relative risk가 낮아서 후속 분석 과정에서 빠지게 되었다.

기본적으로 가중치가 0, 1, 2, 3이상으로 분류하였다.

논문을 읽게 되면서 새로 알게 된 중요한 내용으로 나이가 있다. 나이 또한 매우 유의한 인자였기 때문에 이를 반영한 comorbidity-age combined risk score가 있다. 40대 이전은 0점이며 10년 단위로 1점씩 증가한다. 논문의 예시로 가중치 1점이 있는 60대는 3점이 되며, 가중치 3점이 있으면 5점으로 계산할 수 있다.