재료 및 방법 image j

과학적 이미지 처리를위한 스크립트 가능한 Java 앱

ImageJ 는 이미지 분석을위한 Java 기반 응용 프로그램입니다.

ImageJ는 온라인 애플릿 또는 여기에서 찾을 수있는 다운로드 가능한 응용 프로그램 중 하나로 실행됩니다. ImageJ는 8 비트, 16 비트 및 32 비트 이미지를 표시, 편집, 분석, 처리, 저장 및 인쇄 할 수 있으며 TIFF, GIF, JPEG, BMP, DICOM, FITS 및 "원시"와 같은 대부분의 주요 형식을 지원합니다.

ImageJ의 주요 용도는 그래픽 디자이너에게 유용 할 수있는 영역 및 픽셀 값을 계산할 수 있다는 것입니다. 그러나 거리와 각도를 측정하고, 밀도 히스토그램과 라인 프로파일 플롯을 생성 할 수 있습니다. 또한 명암 조작, 선명하게하기, 부드럽게하기, 가장자리 감지 및 중간 값 필터링과 같은 다른 표준 이미지 처리 기능도 지원합니다.

비록 ImageJ가 Java에서 약간 느릴 수 있지만 완전히 자유로운 이미지 분석 도구입니다.

이미지> 스택> 도구> 슬라이스 라벨 제거 명령을 추가했습니다. 프로세스> 배치> 변환 명령은 크기를 줄일 때의 평균입니다. Michael Doube에게 감사드립니다. 스택이 TIFF 형식으로 저장되는 동안 이미지 창이 닫히는 경우 예외가 발생할 수있는 버그가 수정되었습니다. Valerio Mussi 덕분에 이미지 원점이 왼쪽 상단에 있지 않을 때 ROI 관리자가 예상대로 작동하지 않는 버그가 수정되었습니다. Tomas Karlsson 덕분에 가끔 "+"및 "-"키보드 단축키 (확대 / 축소)가 미국 이외의 키보드에서 예상대로 작동하지 않는 버그가 수정되었습니다. "open = & dir", "size = & min- & max"및 "range = & first- & last"와 같은 옵션을 사용하여 run () 매크로 호출을 실패하게하는 버그가 수정되었습니다. RankFilters.rank () 메소드가 실패하게 한 1.45j 회귀를 수정했습니다.

변경

  • 이미지> 스택> 도구> 슬라이스 라벨 제거 명령을 추가했습니다. 프로세스> 배치> 변환 명령은 크기를 줄일 때의 평균입니다. Michael Doube에게 감사드립니다. 스택이 TIFF 형식으로 저장되는 동안 이미지 창이 닫히는 경우 예외가 발생할 수있는 버그가 수정되었습니다. Valerio Mussi 덕분에 이미지 원점이 왼쪽 상단에 있지 않을 때 ROI 관리자가 예상대로 작동하지 않는 버그가 수정되었습니다. Tomas Karlsson 덕분에 가끔 "+"및 "-"키보드 단축키 (확대 / 축소)가 미국 이외의 키보드에서 예상대로 작동하지 않는 버그가 수정되었습니다. "open = & dir", "size = & min- & max"및 "range = & first- & last"와 같은 옵션을 사용하여 run () 매크로 호출을 실패하게하는 버그가 수정되었습니다. RankFilters.rank () 메소드가 실패하게 한 1.45j 회귀를 수정했습니다.

장점

  • 여러 플랫폼에서 작동합니다.
  • 무료 사용
  • 픽셀 측정, 히스토그램 그립니다.

단점

  • 자바 때문에 느리고 반응이 느릴 수 있습니다.

같은 ROI 그리기

[ImageJ] 같은 위치 및 같은 크기의 ROI 설정하여 측정하기

1895년 X-ray가 발견되고 120년이 지난 요즘은 필름이 아닌 디지털 영상으로 출력된다. 그만큼 편리해지기도 하였고, 방사선사인 우리로서는 영상의 변화를 측정함으로서 논문을 쓸 수 있는 소재가 더 많아졌다는 긍정적 효과가 더 많아진게 사실이다. 하지만 컴퓨터에 대해 두려움이 있고, 컴퓨터를 잘 안다 하여도 측정 툴을 모르거나 기본적인 언어가 뒷받침 되어주지 않는다면 아무리 긍정적인 효과가 있다고 하더라도 모두 무용지물일 것이다.

오늘은 정량적 측정을 위한 매우 간단하고 가볍고 사용하기 편리하면서도 무료인 ImageJ라는 프로그램을 소개하고자 한다.

재료 및 방법 image j

위의 사이트를 클릭하면 ImageJ 최신 버전을 무료로 다운 받아서 설치할 수 있다. 개인 뿐만 아니라 기관에서도 완전 무료이다.

ImageJ는 영상을 가공 및 처리할 수 있을 뿐만 아니라 다양한 분석을 할 수 있도록 많은 기능들을 제공한다.

윈도우 베이스의 32bit, 64bit 모두 지원하며, MAC OSX, Linux 에서도 설치할 수 있다.

라이센스가 무료이면서 오픈 소스를 지향하기 때문에 자기가 만든 프로그램에 삽입하여 사용할 수도 있다. 

그리고 플러그인 지원은 무한한 확장성을 제공한다.

예를들어 ImageJ의 기본 기능에 fMRI 툴이 없다면 fMRI 플러그인을 찾아서 설치만 해주면 바로 사용이 가능해진다.

그런데 사실 기본적으로 우리에게 있어서 가장 중요한 점은 DICOM 파일을 지원한다는 것이다.

최근에야 포토샵과 같은 프로그램에서도 Dicom 확장자를 지원하지만 비싼 의료용 소프트웨어 외에는 Dicom 포맷을 지원하지 않았던 것이 현실이었다.

ImageJ에는 너무도 많은 기능이 있지만 오늘은 첫번째 시간으로, 두개 이상의 영상에서 같은 위치, 같은 크기로 ROI를 설정하고자 할 때의 방법을 소개하고자 한다.

사실 PACS 또는 장비 워크스테이션에 설치 되어 있는 소프트웨어 만으로도 영상을 측정할 수 있지만 ROI를 같은 크기로 측정하기에는 많은 제약이 따르곤 했었다.

그러한 문제점을 손쉽게 해결해는 것이 바로 ImageJ에 있었다.

3.0T MRI에서 SCIC 옵션을 빼고(왼쪽), 넣고(오른쪽) 촬영한 팬텀 영상이다.

눈으로 봐도 왼쪽 영상에서 중앙부에 신호감쇄가 나타난 것을 확인할 수 있다.

참고로 ImageJ에서 영상의 크기를 조절하려면 익스플로러 축소.확대 하는 기능인 Ctrl+휠 을 사용하면 확대 및 축소가 가능하다.

먼저 왼쪽 영상에 ROI 크기를 적당하게 그렸다.

그리고 두번째 영상에도 ROI를 그리긴 했는데 위치와 크기가 제각각이다.

물론 보여주기 위해 일부러 차이나게 그리긴 했는데 모든 것을 정확하게 똑같이 그리기란 쉽지 않다.

조금 더 진보한 방법으로는 Grid Line을 사용해서 그리기도 한다.

위와 같이 그릴 수 있지만, 역시 약간의 크기나 오차가 존재한다.

이번에 소개할 방법은 과히 획기적이라고 평가하고 싶다.

물론 이 기능이 언제부터 있었는지는 모르겠으나 내가 항상 궁금해하던 기능이었던지라 이 기능을 찾았을 때 얼마나 기뻤는지 모르겠다.

바로 Restore Selection 기능이다.
우리말로 해석하면 "선택 복원" 쯤 되려나?

단축키로는 컨트롤(Ctrl) + 쉬프트(Shift) + E 로 설정되어 있다.

그런데 이유는 모르겠으나 Shift + E 만 눌러도 같은 기능이 실행된다.

방법은 다음과 같다.

두 영상을 불러온 뒤 첫번째 영상에 ROI를 그린다.

다음에는 아무곳이나 마우스 포인트를 위치 시키고 마우스 왼쪽 버튼을 클릭한다.

이때 클릭한 채로 약간이라도 드래그가 되면 ROI가 그려지므로 반드시 살짝 클릭만 해야된다.

이렇게 아무 곳이나 클릭하면 전에 그렸던 ROI가 컴퓨터 메모리에 기억이 되는 듯 하다.

다음에는 첫번째 영상을 다시 선택하고 Ctrl + Shift + E 또는 Shift + E 를 클릭한다.

그러면 첫번째 영상에 원래 설정됐던 ROI가 같은 위치, 같은 크기로 설정되는 것을 볼 수 있다.

다음 두번째 영상에도 Shift + E 를 클릭해본다.

역시 마찬가지로 같은 ROI가 생성되는 것을 확인할 수 있다.

ImageJ를 오래 전 부터 사용하는 사람들이라면 물론 쉬운 기능일 수도 있겠으나 나와 같이 잘 모르는 사람에게는 단순하지만 유용한 기능이리라 생각된다.

정리해보면,

1. ROI를 그린다.

2. 아무 곳이나 클릭한다. 

3. ROI가 사라진다.

4. Shift + E 를 클릭한다.

5. 1번에서 그렸던 ROI가 그대로 생성된다.

6. 다음 영상을 선택하고 Shift + E 를 클릭한다.

7. 역시 같은 ROI가 생성된다.

6.1. ImagePlus: ImageJ의 이미지 instance

Reference

TCP School 26) 클래스의 개념
ImagePlus

  • 지난 글에서 객체지향 프로그래밍(OOP: Object Oriented Programming)의 개념을 짧게 설명했습니다.
  • instance란 것은 Class에 정의된 대로 만들어진 객체입니다.
    • 자동차 설계도(Class)와 자동차(instance) 관계로 보시면 비슷합니다.

    • instanceClass에 선언된 FieldMethod를 가지고 있습니다.
      (ex. 그랜저 Class로 만들어진 철수 차 instance의 후방카메라 옵션)

    • 다른 Class로 만든 instance끼리는 당연히 다르고 (ex. 람보르기니 vs 그랜저)

    • 같은 Class로 만든 instance라도 다른 존재입니다 (ex. 철수 차 vs 영희 차)


  • ImageJ에서 이미지를 읽어들이면 ImagePlus instance가 됩니다.
    • 공식 문서를 클릭해보시면 자체적으로 changes부터 win까지, 그리고 상위 클래스인 java.awt.image.ImageObserverij.measure.Measurements로부터 상속받은 Field가 추가로 상당히 많습니다.
    • 그랜저 Class는 자동차 Class에 포함되므로 내연기관, 바퀴, 헤드라이트 등 자동차의 속성을 물려받습니다. 이 것을 상속(inheritance)이라고 합니다.

6.2. 이미지 분석

Reference

A Fiji Scripting Tutorial #3. Inspecting properties and pixels of an image
Jython Scripting Examples
ImageStatistics
ImageProcessor
image.sc Forum

  • ImageJ에서 이미지를 읽으면 ImagePlus형식으로 받아들인다고 했고,
    ImagePlus는 여러 정보(Field)를 담고 있다고 했습니다.
  • 그럼 이 정보를 어떻게 꺼내볼까요?
  • Class instanceField에 접근할 때는 [Instance이름].[Field이름]으로 접근합니다.
    • 철수가 그랜저 (Class: Grandeur)를 새로 샀다고 합시다.
    • 철수네 자동차 (Instance: CScar)의 색상(Field: color)은 CScar.color 입니다.
    • 철수네 자동차 색상을 출력하라고 하려면, print CScar.color라고 하면 됩니다.

6.2.1. 이미지 기본 정보 분석

  • 이미지 파일 구조 글에서, 이미지는 x, y 2차원 공간에 놓인 픽셀들로 구성되며 이미지 파일에는 Channels, Slices, Frames 속성이 있다고 말씀드렸습니다.

  • 이 데이터들을 뽑아보겠습니다.

  • File > Open Samples > Boats.gif로 배 이미지를 엽니다.

  • 단축키 [를 눌러 아래 코드를 붙여넣고 실행합니다. 여기에서 다운로드도 가능합니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    from ij import IJ, ImagePlus


    imp = IJ.getImage()


    print "title:", imp.title
    print "width:", imp.width
    print "height:", imp.height
    print "number of pixels:", imp.width * imp.height
    print "number of slices:", imp.getNSlices()
    print "number of channels:", imp.getNChannels()
    print "number of time frames:", imp.getNFrames()

    types = {ImagePlus.COLOR_RGB : "RGB",
    ImagePlus.GRAY8 : "8-bit",
    ImagePlus.GRAY16 : "16-bit",
    ImagePlus.GRAY32 : "32-bit",
    ImagePlus.COLOR_256 : "8-bit color"}

    print "image type:", types[imp.type]
  • 위 코드를 실행하면 아래와 같이 출력됩니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9

    title: boats.gif
    width: 720
    height: 576
    number of pixels: 414720
    number of slices: 1
    number of channels: 1
    number of time frames: 1
    image type: 8-bit

    • 코드 맨 마지막 부분, print "image type:", types[imp.type] 의 결과물로 image type: 8-bit이 출력되었습니다.

    • 읽어들인boats.gif의 type이 ImagePlusGRAY8형식이기 때문에 imp.typeImagePlus.GRAY8로 치환되었고, python의 데이터 타입 중 하나인 dictionary 호출에 따라 types에서 ImagePlus.GRAY8에 해당하는 값인 8-bit가 출력된 것입니다.


    • image type은 이 이미지의 픽셀이 어떻게 구성되어 있는지를 보여줍니다.

      • GRAY8 : 흑백. 한 pixel은 8 bit ($ 2^8 = 256 $개)의 정수값(0~255)을 가질 수 있습니다.
      • GRAY16 : 흑백. 한 pixel은 8 bit ($ 2^{16} = 65536 $개)의 정수값(0~65535)을 가질 수 있습니다.
      • GRAY32 : 흑백. 한 pixel은 32 bit의 소수(float)값을 가질 수 있습니다.
      • COLOR_256: 컬러. 한 pixel은 3 개(Red, Green, Blue)의 8 bit 채널을 가집니다.
      • COLOR_RGB: 컬러. 한 pixel은 3 개(Red, Green, Blue)의 32 bit 채널을 가집니다.

6.2.2. 이미지 통계 정보 분석

  • 위에서 boats.gif는 width(가로) 720 x height(세로) 576 이므로 총 pixel 수는 414,720개입니다.

  • 이미지 처리 중 많은 작업이 명도(brightness)와 대비(contrast) 조정입니다.

  • 이미지를 조정하기에 앞서 현재 이미지의 상태를 파악해야 하는데, 작은 이미지라도 상당히 많은 수의 pixel로 이루어져 있기 때문에 전체적인 통계 데이터를 파악할 필요가 있습니다.


6.2.2.1. ImagePlus로부터 통계 정보 분석
  • ImageJ에서 이미지 데이터를 다루는 ImagePlus 객체는 getStatistics()라는 Method를 가지고 있습니다.

  • ImagePlusgetStatistics()는 4가지 사용 방법이 있습니다.

    1. 인자 없이 실행: Calibration을 반영한 통계결과 (histogram, area, mean, min, max, st.dev, mode)
    2. 측정항목(mOptions)을 특정해서 실행: 특정한 항목에 대해서만 결과를 출력합니다.
    3. 측정항목과 구간 수 (nBins)를 특정해서 실행: 2. + 지정된 히스토그램 bin count로 실행합니다.
    4. 측정항목, 구간 수, 히스토그램 범위(histMin, histMax)를 특정해서 실행: 3. + 히스토그램 범위를 지정하여 실행합니다.
  • Calibration은 이미지의 1 픽셀이 실제 길이로 얼마에 해당하는지(spatial calibration), 이미지의 gray scale이 실제 데이터로 얼마에 해당하는지(density calibration) 등의 정보를 담고 있습니다.

    • 자세한 사항은 ImageJ > Cookbook > 2. Image Calibration을 참고하시기 바랍니다.


  • 예제 이미지를 여기에서 다운로드하여 실습해봅시다.
    0~255 Gray Scale 중 0을 -2에, 254를 45에 대응하여 Calibration한 이미지입니다.

    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
    from ij import IJ


    imp = IJ.getImage()



    stats1 = imp.getStatistics()



    print "stats1", stats1
    print "area:", stats1.area
    print "mean:", stats1.mean
    print "min:", stats1.min
    print "max:", stats1.max
    print "st.dev:", stats1.stdDev
    print "mode:", stats1.mode



    print "histogram:", stats1.histogram()


    hist1 = imp.plotHistogram()
    plot1 = hist1.getImagePlus()
    IJ.save(plot1, "C:\Arbeitplatz//19_hexoblog_backup\source\_posts\ImageJ-tutorial-6-Inspection\plot1.tif")


    from ij.gui import HistogramWindow
    hist2 = HistogramWindow(imp)
    plot2 = hist2.getImagePlus()
    IJ.save(plot2, "C:\Arbeitplatz//19_hexoblog_backup\source\_posts\ImageJ-tutorial-6-Inspection\plot2.tif")
    • 실행 결과, stats1로 저장한 통계 분석 결과의 각 항목들이 출력되었습니다.

    • histogram은 구간별 count array로 출력할 수도 있고 (# 3-2-1),
      plot으로 출력할 수도 있습니다 (# 3-2-2, # 3-2-3).

    • 두 방식의 plot 출력은 디자인만 조금 다르고 본질적으로 같습니다. 원하는 것을 사용하면 됩니다.

    • getImagePlus()IJ.save()를 활용하면 plot을 그림파일로 저장할 수 있습니다.
      폴더(디렉토리)가 역슬래시(\)로 표시된 부분과 이중 슬래시(//)로 표시된 부분이 공존하는 것은 역슬래시 뒤에 숫자가 오면 ImageJ가 이상하게 인식하기 때문에 부분 변경한 것입니다.
      폴더 이름이 숫자로 시작하지 않으면 되는 문제입니다.


6.2.2.2. ImageProcessor로부터 통계 정보 분석
  • ImageJ는 이미지 통계 분석을 위해 ImageStatistics라는 모듈을 제공합니다.

    • 6.2.2.1에서 imp.getStatistics() 실행 결과로 얻어진 stats1ImageStatistics 클래스입니다.

    • ImageStatistics 에서 사용하는 getStatistics()는 입력으로 ImageProcessor를 읽어들이므로 이미지를 ImagePlus 형식에서 ImageProcessor형식으로 변환해줘야 합니다.

    • ImageProcessor라는 이름만 보면 뭔가 계산을 대신 해줘야 할 것 같지만, 알고 보면 ImageProcessorImagePlusField 중 하나로, 이미지의 2D데이터 + 통계데이터 + 조작을 위한 Method를 포함한 것이 ImageProcessor 입니다.

    • 3D 이상이라 볼 수 있는 stack 관련한 데이터는 ImageProcessor에 포함되지 않았으며, 이 것은 ImageStack에 있습니다.

    • ImageProcessor로 2D 정보만 추린 후, 여기에서 통계정보를 얻는 명령이 getStatistics()입니다.


    • getStatistics() 명령을 ImageStatistics에서 찾아보면, 아래와 같이 두 가지 사용법이 있습니다.

      1. getStatistics(ImageProcessor ip): 인자로 ImageProcessor 객체만을 입력하고, calibration되지 않은 상태의 이미지 데이터에 대한 통계를 출력합니다.

        ImagePlus에서 사용하는 getStatistics()는 Calibration 된 이미지로, 헷갈리기 쉽습니다.

      2. getStatistics(ImageProcessor ip, int mOptions, Calbiration cal): 추가 인자로 mOptionsCalibration 인자 cal을 넣을 수 있습니다. 이 때 주어진 measurement option(mOptions)과 calibration(cal)을 적용한 통계결과를 출력합니다.


  • ImagePlus에서 데이터를 뽑는 6.2.2.1 방식이 덜 복잡하고 더 간결해 보입니다.
    하지만 이런 방법도 있다는 차원에서 살펴보고 지나가겠습니다.


  • 6.2.2.1 과 동일한 작업을 ImageStatistics(ip)로 진행해 보겠습니다.

    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
    from ij import IJ
    from ij.process import ImageStatistics as IS


    imp = IJ.getImage()



    ip = imp.getProcessor()


    stats2 = IS.getStatistics(ip)

    print "stats2", stats2
    print "area:", stats2.area
    print "mean:", stats2.mean
    print "min:", stats2.min
    print "max:", stats2.max
    print "st.dev:", stats2.stdDev




    print "histogram:", stats2.histogram()


    hist1 = imp.plotHistogram()
    plot1 = hist1.getImagePlus()
    IJ.save(plot1, "C:\Arbeitplatz//19_hexoblog_backup\source\_posts\ImageJ-tutorial-6-Inspection\plot3.tif")


    from ij.gui import HistogramWindow
    hist2 = HistogramWindow(imp)
    plot2 = hist2.getImagePlus()
    IJ.save(plot2, "C:\Arbeitplatz//19_hexoblog_backup\source\_posts\ImageJ-tutorial-6-Inspection\plot4.tif")
    • 실행 결과, 6.2.2.1 과 동일한 형태로 다른 값들이 출력됩니다.

    • 예를 들면 max 값이 45.185에서 255로 바뀌었는데, Calibration이 되지 않은 값이라 그렇습니다.

    • 실제 실행시 # 3-2-2 그래프는 위 그림과 달리 바닥에 납작 엎드린 히스토그램이 출력되지만, 마우스로 빨간 원 안에 해당하는 부분을 클릭해서 상한선을 낮춰주면 전체적으로 히스토그램이 높게 표현됩니다.

  • 이번에는 ImageStatistics(ip, mOptions, cal)로 진행해 보겠습니다.

    • 위 코드에서 세 줄만 바꿔주면 됩니다.
    1
    2
    3
    4
    5
    options = IS.MEAN | IS.MEDIAN | IS.MIN_MAX  
    stats3 = IS.getStatistics(ip, options, imp.getCalibration())


    print "median:", stats3.median
    • 재미를 위해 여기서 하나만 더 바꿔봅시다.
    1
    2
    3


    options = IS.MEAN | IS.MEDIAN
    • 이제 아래 코드가 되었습니다.
    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
    from ij import IJ
    from ij.process import ImageStatistics as IS


    imp = IJ.getImage()



    ip = imp.getProcessor()


    options = IS.MEAN | IS.MEDIAN
    stats3 = IS.getStatistics(ip, options, imp.getCalibration())

    print "stats3", stats3
    print "area:", stats3.area
    print "mean:", stats3.mean
    print "median:", stats3.median
    print "min:", stats3.min
    print "max:", stats3.max
    print "st.dev:", stats3.stdDev



    print "histogram:", stats3.histogram()


    hist1 = imp.plotHistogram()
    plot1 = hist1.getImagePlus()
    IJ.save(plot1, "C:\Arbeitplatz//19_hexoblog_backup\source\_posts\ImageJ-tutorial-6-Inspection\plot5.tif")


    from ij.gui import HistogramWindow
    hist2 = HistogramWindow(imp)
    plot2 = hist2.getImagePlus()
    IJ.save(plot2, "C:\Arbeitplatz//19_hexoblog_backup\source\_posts\ImageJ-tutorial-6-Inspection\plot6.tif")
    • 실행 결과를 6.2.2.1 과 비교해보면 몇몇 차이점들이 보입니다.
      1. mean값이 158.2 에서 27.27 로 바뀜 : Calibration이 적용된 것입니다.
      2. minmax가 0 이 되어버림: IS.MIN_MAX를 주석처리해서 데이터가 추출되지 않았습니다.

    재료 및 방법 image j


  • ImagePlus상태에서 데이터를 분석하는 것이 더 편하고 직관적입니다.
    특별한 이유가 없으면 ImageStatistics로 넘어가지 않는 것을 권장합니다.

재료 및 방법 image j

도움이 되셨나요? 카페인을 투입하시면 다음 포스팅으로 변환됩니다