Category: Python

  • RGB vs. BGR

    최근의 일반적인 이미지 프로그램들은 색상을 저장할 때 RGB 순서대로 저장을 한다. 그런데 OpenCV에서 imread로 파일을 불러올 때에는 BGR 순서대로 불러오는 것 같다. 인터넷 검색을 해보면 초기에 카메라 업체들이 파일을 저장할 때 BGR를 이용했기 때문에 이런것 같다는 내용을 찾아볼 수 있다. 어제 염색 색상이 다르게 보이길래 모니터 색상이 문제인가 싶었는데, 그것치고는 너무 심하여 아마 이 순서에 따른 문제로 판단했다. cvtColor로 쉽게 변환할 수 있다.

    cv2.cvtColor(cv2.imread("seq2337_1.tif"), cv2.COLOR_BGR2RGB)
    

    진짜 웃긴건 drawContours로 박스를 그릴 때 색상은 RGB 순서대로 들어간다. 아래와 같이 입력하면 빨간색 테두리가 생긴다. 하하하…

    cv2.drawContours(src, [np.int64(cv2.boxPoints(rect))], 0, [255, 0, 0])
  • 슬라이드 개체 인식

    보다 자세하게는 현미경 소견에서 관찰되는 개체를 인식할 수 있는가에 대한 것이다. 일단 고전적인 방법으로 접근하여 보았다. 인터넷 동호회의 정보를 바탕으로 모멘트(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])
    

    분석 속도는 꽤 빠르다.

  • 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는 그냥 채우는 것이다.

  • Enlarging smaller images before inputting into convolutional neural network: zero-padding vs. interpolation

    CNN에서 모델에 들어가는 이미지의 크기는 항상 일정해야 한다. 이미지의 크기가 작으면 요구하는 크기에 맞게 키워야 한다. 그 때 사용하는 방법이 사방을 0으로 둘러싸는 방법(zero-padding)과 비율에 맞게 키우는 방법이 있다. 이미지를 비율에 맞게 크게 하는 것은 어떤 방법에 따라서 임의의 값을 생성한다는 것이고 이를 보간법(interpolation)이라고 한다.

    이 논문에서는 zero-padding과 interpolation 사이에 성능의 차이는 없다고 말하고 있다. 다만, zero-padding 된 부위가 계산 속도를 빠르게 해줄 수 있어서 더 좋지 않겠냐고 제시한다. 아직 이와 관련된 다른 이야기들은 찾기 못했다. 이와 같은 문제가 별로 상관이 없거나 아니면 작은 크기에 맞는 모델을 학습했다는 뜻이 아닐까 생각된다. 논문으로 쓸 만큼의 가치가 없는 것이 가장 적당한 이유가 아닐까 싶다.

    Journal of Big Data. 2019;6(1):98.