6.1. ImagePlus: ImageJ의 이미지 instance
Reference
- 지난 글에서 객체지향 프로그래밍(OOP: Object Oriented Programming)의 개념을 짧게 설명했습니다.
instance란 것은Class에 정의된 대로 만들어진 객체입니다.자동차 설계도(
Class)와 자동차(instance) 관계로 보시면 비슷합니다.instance는Class에 선언된Field와Method를 가지고 있습니다.
(ex. 그랜저Class로 만들어진 철수 차instance의 후방카메라 옵션)다른
Class로 만든instance끼리는 당연히 다르고 (ex. 람보르기니 vs 그랜저)같은
Class로 만든instance라도 다른 존재입니다 (ex. 철수 차 vs 영희 차)
ImageJ에서 이미지를 읽어들이면ImagePlusinstance가 됩니다.- 공식 문서를 클릭해보시면 자체적으로
changes부터win까지, 그리고 상위 클래스인java.awt.image.ImageObserver와ij.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)를 담고 있다고 했습니다.- 그럼 이 정보를 어떻게 꺼내볼까요?
Classinstance의Field에 접근할 때는[Instance이름].[Field이름]으로 접근합니다.- 철수가 그랜저 (Class:
Grandeur)를 새로 샀다고 합시다. - 철수네 자동차 (Instance:
CScar)의 색상(Field:color)은CScar.color입니다. - 철수네 자동차 색상을 출력하라고 하려면,
print CScar.color라고 하면 됩니다.
- 철수가 그랜저 (Class:
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
21from 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이ImagePlus의GRAY8형식이기 때문에imp.type은ImagePlus.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 채널을 가집니다.
- GRAY8 : 흑백. 한
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를 가지고 있습니다.ImagePlus의getStatistics()는 4가지 사용 방법이 있습니다.- 인자 없이 실행: Calibration을 반영한 통계결과 (histogram, area, mean, min, max, st.dev, mode)
- 측정항목(
mOptions)을 특정해서 실행: 특정한 항목에 대해서만 결과를 출력합니다. - 측정항목과 구간 수 (
nBins)를 특정해서 실행: 2. + 지정된 히스토그램 bin count로 실행합니다. - 측정항목, 구간 수, 히스토그램 범위(
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
33from ij import IJ
# 1. Load Image
imp = IJ.getImage()
# 2. Image 통계 정보 분석
# 2-1. 인자 없이 실행
stats1 = imp.getStatistics()
# 3. 분석 결과 출력
# 3-1. 통계 정보 출력
print "stats1", 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 # 256 histogram의 mode
# 3-2. 히스토그램 출력
# 3-2-1. array 출력
print "histogram:", stats1.histogram()
# 3-2-2. plot 출력 1
hist1 = imp.plotHistogram()
plot1 = hist1.getImagePlus()
IJ.save(plot1, "C:\Arbeitplatz//19_hexoblog_backup\source\_posts\ImageJ-tutorial-6-Inspection\plot1.tif")
# 3-2-3. plot 출력 2
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()실행 결과로 얻어진stats1도ImageStatistics클래스입니다.ImageStatistics에서 사용하는getStatistics()는 입력으로ImageProcessor를 읽어들이므로 이미지를ImagePlus형식에서ImageProcessor형식으로 변환해줘야 합니다.ImageProcessor라는 이름만 보면 뭔가 계산을 대신 해줘야 할 것 같지만, 알고 보면ImageProcessor는ImagePlus의Field중 하나로, 이미지의 2D데이터 + 통계데이터 + 조작을 위한Method를 포함한 것이ImageProcessor입니다.3D 이상이라 볼 수 있는
stack관련한 데이터는ImageProcessor에 포함되지 않았으며, 이 것은ImageStack에 있습니다.ImageProcessor로 2D 정보만 추린 후, 여기에서 통계정보를 얻는 명령이getStatistics()입니다.getStatistics()명령을ImageStatistics에서 찾아보면, 아래와 같이 두 가지 사용법이 있습니다.getStatistics(ImageProcessor ip): 인자로ImageProcessor객체만을 입력하고,calibration되지 않은 상태의 이미지 데이터에 대한 통계를 출력합니다.ImagePlus에서 사용하는getStatistics()는 Calibration 된 이미지로, 헷갈리기 쉽습니다.getStatistics(ImageProcessor ip, int mOptions, Calbiration cal): 추가 인자로mOptions와Calibration인자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
35from ij import IJ
from ij.process import ImageStatistics as IS
# 1. Load Image
imp = IJ.getImage()
# 2. Image 통계 정보 분석
# 2-1. ImageProcessor 호출
ip = imp.getProcessor()
# 2-2. 인자 없이 분석
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
# 3-2. 히스토그램 출력
# 3-2-1. array 출력
print "histogram:", stats2.histogram()
# 3-2-2. plot 출력 1
hist1 = imp.plotHistogram()
plot1 = hist1.getImagePlus()
IJ.save(plot1, "C:\Arbeitplatz//19_hexoblog_backup\source\_posts\ImageJ-tutorial-6-Inspection\plot3.tif")
# 3-2-3. plot 출력 2
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
5options = IS.MEAN | IS.MEDIAN | IS.MIN_MAX # options에 뽑을 데이터를 정의하고
stats3 = IS.getStatistics(ip, options, imp.getCalibration()) # 함수에 인자를 넣습니다.
# 그리고 아래 출력 부분에 이걸 추가해 줍시다.
print "median:", stats3.median #중간값(median)을 뽑습니다.- 재미를 위해 여기서 하나만 더 바꿔봅시다.
1
2
3# IS.MIN_MAX 앞에 #을 붙여봅시다.
# 주석으로 처리해버리겠다(comment out) = 실행하지 않겠다는 뜻입니다.
options = IS.MEAN | IS.MEDIAN #| IS.MIN_MAX- 이제 아래 코드가 되었습니다.
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
36from ij import IJ
from ij.process import ImageStatistics as IS
# 1. Load Image
imp = IJ.getImage()
# 2. Image 통계 정보 분석
# 2-1. ImageProcessor 호출
ip = imp.getProcessor()
# 2-2. 항목 지정 분석
options = IS.MEAN | IS.MEDIAN #| IS.MIN_MAX
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
# 3-2. 히스토그램 출력
# 3-2-1. array 출력
print "histogram:", stats3.histogram()
# 3-2-2. plot 출력 1
hist1 = imp.plotHistogram()
plot1 = hist1.getImagePlus()
IJ.save(plot1, "C:\Arbeitplatz//19_hexoblog_backup\source\_posts\ImageJ-tutorial-6-Inspection\plot5.tif")
# 3-2-3. plot 출력 2
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 과 비교해보면 몇몇 차이점들이 보입니다.
mean값이 158.2 에서 27.27 로 바뀜 : Calibration이 적용된 것입니다.min과max가 0 이 되어버림:IS.MIN_MAX를 주석처리해서 데이터가 추출되지 않았습니다.
ImagePlus상태에서 데이터를 분석하는 것이 더 편하고 직관적입니다.
특별한 이유가 없으면ImageStatistics로 넘어가지 않는 것을 권장합니다.