Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
ImageJ を使った画像解析実習
数・形態・分布の解析
第194回農林交流センターワークショップ
「 植物科学・作物育種におけるフェノーム解析
- はじめて画像解析を行う研究者のための入門実習 - 」
2015-09-18 09:15-10:...
例: 葉緑体の形の解析 (粒子解析)
二値画像
グレイスケール画像
Analyze >
Analyze Particles…
例: アクチン繊維の配向解析
共焦点画像 バンドパスフィルタ
による繊維等の強調
二値化像 細線化像
短径 / 長径
短径
長径
気孔開閉の指標
気孔に対する
アクチン繊維の角度
アクチン繊維の配向の指標
気孔
θ
気孔
θ
気孔
この場合,
...
バンドパスフィルタ
Process > FFT > Bandpass Filter...
解析対象のおおよその大きさをもとに強調する処理.
ノイズ,背景,シェーディングの影響を抑制する.
細胞表層微小管のプラス端
輝度プロファイル(左図の黄色い...
バンドパスフィルタによる焦点外物体の影響除去
原画像(明視野像) 処理後
焦点外にある細胞やナイロンメッシュの影響を抑えることができる.
BDM処理した
BY-2細胞
BY-2プロトプラスト
から得た単離液胞
アクチン繊維配向の定量と可視化
向き: 配向の平均角度
長さ: 配向の整列性
明るさ(擬似色): 繊維の密度
* stomata-actin.tif を開く.
* Image > Stacks > Z Project... >
Projection type = Max Intensity
* Process > FFT > Bandpass Filt...
実験圃場(水田)
60 cm 間隔
圃場のイネの各個体を毎週撮影し,
地上部の大きさ・形・色を経時的に
定量する実験系を確立する.
定量した形質をQTL解析に供する.
例: 圃場におけるイネの生長解析
撮像装置
実際に撮影された画像
カラーチャート
個体識別札
画像提供: 香川大学 農学部 杉田(小西)左江子 先生
画像解析上の課題
1. ウキクサのような浮遊物
2. 太陽光の直接反射
http://www.mathworks.com/matlabcentral/fileexchange/28790-colorspace-transformations
色空間
一般的なカラー画像では色彩の表現として,
RGB (発光時に用いる...
(A) http://www.jiscdigitalmedia.ac.uk/guide/colour-theory-understanding-and-modelling-colour/
(B) http://w3.kcua.ac.jp/~fu...
RGB HSV YIQ CIE L*a*b* CIE XYZ
各種色空間への変換例
→ CIE a* 軸がイネ地上部の領域抽出に向く.
カラーパネル検出ならびに葉領域の抽出と細線化
原画像 CIELAB 色空間の a* 軸 a* が閾値以下の領域
葉領域画像 細線画像カラーパネル検出結果
カラーパネルの円の直径が既知(12.5 cm)
であるため,スケールバーとして利用し,
比...
形態特徴の測定法 1/3
葉領域画像 area1: イネ地上部の面積(cm2).葉領域画像中の白画素数 × r2
perimeter4conn: イネ地上部領域の周長(cm).輪郭画像中の白画素数 × r
widthByAreaPeri4: 葉...
形態特徴の測定法 2/3
葉領域画像 skelLen: 葉の長さに関する指標(cm).細線画像中の白画素数 × r
widthByAreaSkelLen: 葉の幅に関する指標(cm).area1 ÷ skelLen
skelEnd: 葉の枚数に...
形態特徴の測定法 3/3
widthBySkelDisttrfmMean: 葉の幅に関する指標(cm).以下の手続きによって求める値.
Step 1. 細線画像(左の画像)中のi 番目の白画素 Ai について( i は 1 から skelLen...
実習
- RGB 各信号 への分解:
Image > Color > Split Channels
- その他の色空間の座標へ:
Plugins > LPX > Lpx Color >
mode = RGB2...
もしくは
Plugins >...
- 輝度を閾値とした2値化
Image > Adjust > Threshold
- 面積の測定
Analyze > Analyze Particles
- 細線化と線長の測定
Process > Binary > Skeletonize
An...
例2: ブドウ葉の病斑解析
シートフィード式スキャナ (ScanSnap) で撮影.
画像提供: 農研機構 果樹研究所 河野淳 様
http://www.scansnap.net/escaneres-by-fujitsu/scansnap/s1...
拡大図
※スケール代わりの付箋紙にも注目.
回転
縮小 + CIE L* + ノイズ抑制 縮小 + CIE a* + ノイズ抑制
入力画像
入力画像
回転
縮小 + CIE L* + ノイズ抑制 縮小 + CIE a* + ノイズ抑制
拡大図
自動二値化 自動二値化
病斑領域の二値化
自動二値化だけで 常に 必要十分に 正しく 領域抽出するのは困難.
→ ユーザの目視による二値化基準の修正 +
二値化後の除去 (主に葉脈部を除くため円形度を閾値とした)
適切 (必要十分)
偽陰性(見逃し) あり
偽陽性(採...
葉領域 ならびに 病斑領域 の決定
葉領域
病斑領域 群葉領域
葉および病斑に関する定量
縮小率: 長さ比 1/shrink に縮小する.
葉領域のノイズ抑制 (to CIEL* 像).
葉領域の自動2値化アルゴリズム
病斑領域のノイズ抑制 (to CIEa* 像)
病斑領域の自動2値化アルゴリズム
葉領域の最小面積
Phase 1...
object Ijp extends LpxPlugIn {
private val leafThrEffective = Seq('ijHuang, 'ijLi, 'ijMinimum, …
private val spotThrEffect...
// R G B
private val colorLeaf = new java.awt.Color(128, 128, 128)
private val colorSpot = new java.awt.Color(255, 255, 25...
private def measure(imp: Imp) {
if (ImgC.is(imp)) {
phase1(imp)
} else {
phase2()
}
}
private def phase1(impOrg: Imp) {
de...
require(ImgC.is(impOrg), "ImgC.is")
require(impOrg.getStackSize == 1, "getStackSize == 1")
require(shrink() >= 1, "shrink ...
// spot: CIEa*
val ipSpot = to8bit(bandpass(ipCieA, spotDenoise(), bpHiMax))
val thrSpot = new IjThresholder(spotThr.getSy...
def drawString(ipTgt: Ip, s: String, font: Font, color: Color, …
ipTgt.setColor(color)
ipTgt.setFont(font)
val width = ipT...
UtilIj.writeln(f"#shrink;¥t${shrink()}")
UtilIj.writeln(f"# areaAdjustFactor;¥t${shrink() * shrink()}")
UtilIj.writeln(f"#...
for ((blob, blobIdx) <- assocBlobs.zipWithIndex) {
blob.drawToIpByColor(ipDebug, colorSpot)
val x = blob.xsMin - 3
val y =...
private def setParam(): Boolean = {
input(sep1,
shrink, leafDenoise, leafThr, spotDenoise, spotThr, genStk,
sep2,
leafMinA...
Upcoming SlideShare
Loading in …5
×

ImageJを使った画像解析実習〜数・形態・分布の解析〜

5,433 views

Published on

@第194回農林交流センターワークショップ

Published in: Science
  • Be the first to comment

  • Be the first to like this

ImageJを使った画像解析実習〜数・形態・分布の解析〜

  1. 1. ImageJ を使った画像解析実習 数・形態・分布の解析 第194回農林交流センターワークショップ 「 植物科学・作物育種におけるフェノーム解析 - はじめて画像解析を行う研究者のための入門実習 - 」 2015-09-18 09:15-10:20 (実習75分) 実習主担当: 朽名 夏麿
  2. 2. 例: 葉緑体の形の解析 (粒子解析) 二値画像 グレイスケール画像 Analyze > Analyze Particles…
  3. 3. 例: アクチン繊維の配向解析 共焦点画像 バンドパスフィルタ による繊維等の強調 二値化像 細線化像 短径 / 長径 短径 長径 気孔開閉の指標 気孔に対する アクチン繊維の角度 アクチン繊維の配向の指標 気孔 θ 気孔 θ 気孔 この場合, 短径 / 長径=0.47 この場合, アクチン繊維の角度=54.3° 灰色: 気孔領域 黒色: アクチン繊維 シロイヌナズナ気孔 アクチン繊維
  4. 4. バンドパスフィルタ Process > FFT > Bandpass Filter... 解析対象のおおよその大きさをもとに強調する処理. ノイズ,背景,シェーディングの影響を抑制する. 細胞表層微小管のプラス端 輝度プロファイル(左図の黄色い線)
  5. 5. バンドパスフィルタによる焦点外物体の影響除去 原画像(明視野像) 処理後 焦点外にある細胞やナイロンメッシュの影響を抑えることができる. BDM処理した BY-2細胞 BY-2プロトプラスト から得た単離液胞
  6. 6. アクチン繊維配向の定量と可視化 向き: 配向の平均角度 長さ: 配向の整列性 明るさ(擬似色): 繊維の密度
  7. 7. * stomata-actin.tif を開く. * Image > Stacks > Z Project... > Projection type = Max Intensity * Process > FFT > Bandpass Filter... Filter large... = 5 Filter small... = 1 Tolerance... = 0 * Image > Adjust > Threshold... > … > Apply * Process > Binary > Skeletonize * Plugins > LPX > Lpx_LinesAngle mode = map (可視化) * Plugins > LPX > Lpx_LinesAngle mode = eachSlice (定量)
  8. 8. 実験圃場(水田) 60 cm 間隔 圃場のイネの各個体を毎週撮影し, 地上部の大きさ・形・色を経時的に 定量する実験系を確立する. 定量した形質をQTL解析に供する. 例: 圃場におけるイネの生長解析
  9. 9. 撮像装置
  10. 10. 実際に撮影された画像 カラーチャート 個体識別札 画像提供: 香川大学 農学部 杉田(小西)左江子 先生
  11. 11. 画像解析上の課題 1. ウキクサのような浮遊物 2. 太陽光の直接反射
  12. 12. http://www.mathworks.com/matlabcentral/fileexchange/28790-colorspace-transformations 色空間 一般的なカラー画像では色彩の表現として, RGB (発光時に用いる色表現,カメラに用いる色表現) や CMYK (印刷に用いる色表現) が用いられる. 一方,バイオイメージング分野では 単色からスペクトルまで 多様な色情報のデータが 扱われている.
  13. 13. (A) http://www.jiscdigitalmedia.ac.uk/guide/colour-theory-understanding-and-modelling-colour/ (B) http://w3.kcua.ac.jp/~fujiwara/infosci/colorspace/colorspace3.html その他 http://en.wikipedia.org/ よりそれぞれ引用 CIE1976 L*a*b* (CIELAB) color space L*: lightness, a* & b*: color-opponent space perceptually uniform RGB color space (A) (B) Additive primary colors: Red, Green, Blue mimic the nonlinear response of the eye
  14. 14. RGB HSV YIQ CIE L*a*b* CIE XYZ 各種色空間への変換例 → CIE a* 軸がイネ地上部の領域抽出に向く.
  15. 15. カラーパネル検出ならびに葉領域の抽出と細線化 原画像 CIELAB 色空間の a* 軸 a* が閾値以下の領域 葉領域画像 細線画像カラーパネル検出結果 カラーパネルの円の直径が既知(12.5 cm) であるため,スケールバーとして利用し, 比率 r [cm / pixel] を求める. 画像や細線化画像で測定された pixel 単位 の値を cm に換算するのに用いる. 緑色の領域は a* 軸では小さな値となる. 手作業で個体中心付近に設定した円(青色 で示す)と連結した白領域のみ抽出し, さらにモルフォロジカルフィルタ(close) によって領域境界の平滑化を行なうことで 葉領域を得た. 白黒画像の細線化アルゴリズムを適用して 得た. 他に輪郭画像を形態計測の際には作成 しているが,ここでは省略している.
  16. 16. 形態特徴の測定法 1/3 葉領域画像 area1: イネ地上部の面積(cm2).葉領域画像中の白画素数 × r2 perimeter4conn: イネ地上部領域の周長(cm).輪郭画像中の白画素数 × r widthByAreaPeri4: 葉の幅に関する指標(cm).2 × area1 ÷ perimeter4conn complexity: 形状の複雑度.perimeter4conn 2 ÷( 4 π × area1 ) 拡大 輪郭画像葉領域画像 輪郭抽出
  17. 17. 形態特徴の測定法 2/3 葉領域画像 skelLen: 葉の長さに関する指標(cm).細線画像中の白画素数 × r widthByAreaSkelLen: 葉の幅に関する指標(cm).area1 ÷ skelLen skelEnd: 葉の枚数に関する指標(個).細線画像中の端点の数 (赤い丸). skelBranch: 葉の混み具合に関する指標(個).細線画像中の分岐点の数 (黄色い丸). 拡大 細線画像葉領域画像 細線化 端点 端点 分岐点 分岐点
  18. 18. 形態特徴の測定法 3/3 widthBySkelDisttrfmMean: 葉の幅に関する指標(cm).以下の手続きによって求める値. Step 1. 細線画像(左の画像)中のi 番目の白画素 Ai について( i は 1 から skelLen まで): Step 1-1. 画素Aiに最も近い,輪郭画像(右の画像)中の白画素を探し,これを画素Bとする. Step 1-2. 画素Aiと画素Biの距離(pixel)を求めて, di とする. Step 2. di の平均値(i は 1 から skelLen まで) × 2 r を widthBySkelDisttrfmMean とする. 細線画像 輪郭画像 画素Ai 画素Bi
  19. 19. 実習 - RGB 各信号 への分解: Image > Color > Split Channels - その他の色空間の座標へ: Plugins > LPX > Lpx Color > mode = RGB2... もしくは Plugins > Filters > Color Transformer
  20. 20. - 輝度を閾値とした2値化 Image > Adjust > Threshold - 面積の測定 Analyze > Analyze Particles - 細線化と線長の測定 Process > Binary > Skeletonize Analyze > Analyze Particles or KBI_Measure > blobMeasure... feature=chain_codes_8_totalLength - 端点抽出と計数 Plugin > LPX > Lpx_Filter2d > filtersForBilevel__ > pixSpec__ > pick,,end
  21. 21. 例2: ブドウ葉の病斑解析 シートフィード式スキャナ (ScanSnap) で撮影. 画像提供: 農研機構 果樹研究所 河野淳 様 http://www.scansnap.net/escaneres-by-fujitsu/scansnap/s1100/
  22. 22. 拡大図
  23. 23. ※スケール代わりの付箋紙にも注目. 回転 縮小 + CIE L* + ノイズ抑制 縮小 + CIE a* + ノイズ抑制 入力画像
  24. 24. 入力画像 回転 縮小 + CIE L* + ノイズ抑制 縮小 + CIE a* + ノイズ抑制 拡大図
  25. 25. 自動二値化 自動二値化
  26. 26. 病斑領域の二値化 自動二値化だけで 常に 必要十分に 正しく 領域抽出するのは困難. → ユーザの目視による二値化基準の修正 + 二値化後の除去 (主に葉脈部を除くため円形度を閾値とした) 適切 (必要十分) 偽陰性(見逃し) あり 偽陽性(採り過ぎ)あり
  27. 27. 葉領域 ならびに 病斑領域 の決定 葉領域 病斑領域 群葉領域
  28. 28. 葉および病斑に関する定量
  29. 29. 縮小率: 長さ比 1/shrink に縮小する. 葉領域のノイズ抑制 (to CIEL* 像). 葉領域の自動2値化アルゴリズム 病斑領域のノイズ抑制 (to CIEa* 像) 病斑領域の自動2値化アルゴリズム 葉領域の最小面積 Phase 1: 2値化による領域分割まで Phase 2: 領域分割後のフィルタリング 病斑領域の最小円形度
  30. 30. object Ijp extends LpxPlugIn { private val leafThrEffective = Seq('ijHuang, 'ijLi, 'ijMinimum, … private val spotThrEffective = Seq('ijRenyiEntropy, 'ijYen, … private val leafThrSyms = leafThrEffective ++ (plg.threshold.Common.ijSymbols.toSet -- leaf… } private val spotThrSyms = { spotThrEffective ++ (plg.threshold.Common.ijSymbols.toSet -- spot… } private val mode = arg("kaMode", Seq('measure, 'setParam)) private val sep1 = arg("-- phase1 --") private val shrink = arg("shrink", 4) private val leafDenoise = arg("leafDenoise", 2.0) private val leafThr = arg("leafThr", leafThrSyms) private val spotDenoise = arg("spotDenoise", 1.0) private val spotThr = arg("spotThr", spotThrSyms) private val genStk = arg("genStk", false) private val sep2 = arg("-- phase2 --") private val leafMinArea = arg("leafMinAreaInOrg", 500000d) private val spotCircularity = arg("spotCircularity", 0.6) private val sep3 = arg("----") private val continuePlugIn = arg("continuePlugIn", true)
  31. 31. // R G B private val colorLeaf = new java.awt.Color(128, 128, 128) private val colorSpot = new java.awt.Color(255, 255, 255) private val colorLeafId = new java.awt.Color( 0, 198, 255) private val colorSpotId = new java.awt.Color( 0, 255, 0) private val colorImageJForeground = new java.awt.Color(255, 255, 255) private val fontLeaf = new java.awt.Font("Serif", java.… private val fontSpot = new java.awt.Font("Serif", java.… def config(argStr: String): Option[() => Any] = { def rec() { val imp = IJ.getImage input(mode) mode.getSym match { case 'measure => measure(imp) case 'setParam => { if (setParam()) rec() } } } rec() None }
  32. 32. private def measure(imp: Imp) { if (ImgC.is(imp)) { phase1(imp) } else { phase2() } } private def phase1(impOrg: Imp) { def bandpass(ip: Ip, loSd: Double, hiSd: Double): Ip = { new plg.band_pass.DiffOfGaussian(loSd, hiSd)(ip) } def bandpassMin0(ip: Ip, loSd: Double, hiSd: Double): Ip = { plg.band_pass.Min0(bandpass(ip, loSd, hiSd)) } def to8bit(ip: Ip): Ip = plg.filter2d.ToByteType.SliScale(ip) def toThrStk(ip: Ip): Ist = new plg.filter2d.ToStkByThrFromByte(A… def showAndSetCurSlice(ist: Ist, title: String, z: Int) { val impShow = new Imp(WindowManager.makeUniqueName(title), ist) impShow.setDisplayRange(0, 255, 7) impShow.updateAndDraw impShow.setSlice(z + 1) impShow.show }
  33. 33. require(ImgC.is(impOrg), "ImgC.is") require(impOrg.getStackSize == 1, "getStackSize == 1") require(shrink() >= 1, "shrink >= 1") ij.gui.Toolbar.setForegroundColor(colorImageJForeground) impOrg.setTitle(WindowManager.makeUniqueName("org")) val ipOrg = impOrg.getProcessor.rotateRight impOrg.setProcessor(ipOrg) ipOrg.setInterpolationMethod(Ip.BILINEAR) val ipShrink = ipOrg.resize((ipOrg.getWidth / shrink().toFloat).r… val (ipCieL, ipCieA, ipCieB_notUsed) = new Rgb2ColorSpace(false).procSli(ipShrink, 'CIELAB2_speedy) val (numX, numY) = UtilImg.dim(ipCieL) val bpHiMax = numX.min(numY) // leaf: CIEL* val ipLeaf = to8bit(bandpassMin0(plg.filter_pt.MathOpMultiply(-1)… val thrLeaf = new IjThresholder(leafThr.getSym).getThr(ipLeaf).get() if (genStk()) { val istLeaf = toThrStk(ipLeaf) showAndSetCurSlice(istLeaf, "leaf", thrLeaf) } else { val imp = UtilImg.show(ipLeaf, f"leaf_${thrLeaf}") IJ.setThreshold(imp, thrLeaf, 255) }
  34. 34. // spot: CIEa* val ipSpot = to8bit(bandpass(ipCieA, spotDenoise(), bpHiMax)) val thrSpot = new IjThresholder(spotThr.getSym).getThr(ipSpot).get() if (genStk()) { val istSpot = toThrStk(ipSpot) showAndSetCurSlice(istSpot, "spot", thrSpot) } else { val imp = UtilImg.show(ipSpot, f"spot_${thrSpot}") IJ.setThreshold(imp, thrSpot, 255) } } // use leafMinArea(), spotCircularity() private def phase2() { def getIpOfTitle(title: String): Ip = { val titles = UtilIj.getAllImageTitles() val hits = titles.filter(_.startsWith(title)) LpxExc.assert(hits.size == 1, f"Image '${title}' is not … val ip = WindowManager.getImage(hits.head).getProcessor LpxExc.assert(Img8.isBinary(ip), f"Image '${title}' must be … ip }
  35. 35. def drawString(ipTgt: Ip, s: String, font: Font, color: Color, … ipTgt.setColor(color) ipTgt.setFont(font) val width = ipTgt.getStringWidth(s) ipTgt.drawString(s, x - width, y) } ij.gui.Toolbar.setForegroundColor(colorImageJForeground) val ipLeaf = getIpOfTitle("leaf") val ipSpot = getIpOfTitle("spot") LpxExc.assert(UtilImg.sameDim(ipLeaf, ipSpot), "dimension mismatch") val leafMinAreaInShrinked = (leafMinArea() / (shrink() * shrink()))… val leafBlobs = { PixGrp.filterOutSmall(PixGrp.getBlobs(ipLeaf), leafMinAreaInShr… } val spotBlobs = { val ipSpotInLeaf = Img8.dupRet(ipSpot, false) { ipSpotInLeaf => for (i <- 0 until ipSpot.getPixelCount if ipLeaf.get(i) == 0) { ipSpotInLeaf.set(i, 0) } } PixGrp.getBlobs(ipSpotInLeaf).filter(_.getCircularity >= spotCircularity()) }
  36. 36. UtilIj.writeln(f"#shrink;¥t${shrink()}") UtilIj.writeln(f"# areaAdjustFactor;¥t${shrink() * shrink()}") UtilIj.writeln(f"#leafDenoise;¥t${leafDenoise()}") UtilIj.writeln(f"#leafThr;¥t${leafThr.getSym.name}") UtilIj.writeln(f"#spotDenoise;¥t${spotDenoise()}") UtilIj.writeln(f"#spotThr;¥t${spotThr.getSym.name}") UtilIj.writeln(f"#leafMinAreaInOrg;¥t${leafMinArea()}") UtilIj.writeln(f"# leafMinAreaInShrinked;¥t${leafMinAreaInShrinked}" UtilIj.writeln(f"#spotCircularity;¥t${spotCircularity()}¥n") for ((leafBlob, leafIdx) <- leafBlobs.zipWithIndex) { for (polRoi <- leafBlob.toPolygonRoi(true, true)) { plg.roi_util.Painter.drawByRoiInSitu(ipDebug, colorLeaf, polRoi) val awtRectangle = polRoi.getFloatPolygon.getBounds ipDebug.setColor(colorLeafId) val x = awtRectangle.x val y = awtRectangle.y + awtRectangle.height / 2 drawString(ipDebug, f"${leafIdx}", fontLeaf, colorLeafId, x, y) } val leafXySet = leafBlob.toXys.toSet val assocBlobs = spotBlobs.filter { spotBlob => val (centroidX, centroidY) = spotBlob.getCentroid leafXySet.contains((centroidX.round.toInt, centroidY.round.… } sortBy(- _.size)
  37. 37. for ((blob, blobIdx) <- assocBlobs.zipWithIndex) { blob.drawToIpByColor(ipDebug, colorSpot) val x = blob.xsMin - 3 val y = (blob.ysMin + blob.ysMax) / 2 + 10 drawString(ipDebug, f"${blobIdx}", fontSpot, colorSpotId, x, y) } UtilIj.writeln(f"leafIdx;¥t$leafIdx") UtilIj.writeln(f"leafArea;¥t${leafBlob.size * shrink() * shrink(… UtilIj.writeln(f"spotNum;¥t${assocBlobs.size}") val spotTotalArea = assocBlobs.map(_.size).sum UtilIj.writeln(f"spotTotalArea;¥t${spotTotalArea * shrink() * shr… for ((spotBlob, spotIdx) <- assocBlobs.zipWithIndex) { UtilIj.writeln(f" spotArea;¥t${leafIdx}-${spotIdx};¥t" + f"${spotBlob.size * shrink() * shrink()}") } UtilIj.writeln("") } null }
  38. 38. private def setParam(): Boolean = { input(sep1, shrink, leafDenoise, leafThr, spotDenoise, spotThr, genStk, sep2, leafMinArea, spotCircularity, sep3, continuePlugIn) mode.setIdx(0) continuePlugIn() } }

×