Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
mmi
gridj
Commits
bf78e965
Commit
bf78e965
authored
May 28, 2015
by
Keith Schulze
Browse files
Initial commit of working prototype for lung scoring
parents
Changes
6
Hide whitespace changes
Inline
Side-by-side
.gitignore
0 → 100644
View file @
bf78e965
# Created by https://www.gitignore.io
### Jython ###
*.pyc
*.class
### SublimeText ###
# cache files for sublime text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# workspace files are user-specific
*.sublime-workspace
# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
*.sublime-project
# sftp configuration file
sftp-config.json
### SBT ###
# Simple Build Tool
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control
target/
lib_managed/
src_managed/
project/boot/
.history
.cache
build.sbt
0 → 100644
View file @
bf78e965
name
:=
"GridJ"
organization
:=
"edu.monash.gridj"
version
:=
"0.1-SNAPSHOT"
javacOptions
++=
Seq
(
"-source"
,
"1.6"
,
"-target"
,
"1.6"
)
resolvers
++=
Seq
(
"imagej.releases"
at
"http://maven.imagej.net/content/repositories/releases"
,
"imagej.snapshots"
at
"http://maven.imagej.net/content/repositories/snapshots"
,
"imagej.public"
at
"http://maven.imagej.net/content/groups/public"
)
libraryDependencies
++=
Seq
(
"net.imagej"
%
"imagej"
%
"latest.integration"
,
"sc.fiji"
%
"Jython_Interpreter"
%
"2.0.0-SNAPSHOT"
)
enablePlugins
(
SbtImageJ
)
\ No newline at end of file
project/plugins.sbt
0 → 100644
View file @
bf78e965
addSbtPlugin
(
"net.sf.ij-plugins"
%
"sbt-imagej"
%
"2.0.0"
)
\ No newline at end of file
src/main/java/edu/monash/gridj/Jython_Launcher.java
0 → 100644
View file @
bf78e965
package
edu.monash.gridj
;
import
ij.plugin.PlugIn
;
import
Jython.Refresh_Jython_Scripts
;
public
class
Jython_Launcher
implements
PlugIn
{
public
void
run
(
String
arg
)
{
new
Refresh_Jython_Scripts
().
runScript
(
getClass
().
getResourceAsStream
(
arg
));
}
}
\ No newline at end of file
src/main/resources/plugins.config
0 → 100644
View file @
bf78e965
Plugins
>
GridJ
,
"GridJ"
,
edu
.
monash
.
gridj
.
Jython_Launcher
(
"/scripts/gridj.py"
)
\ No newline at end of file
src/main/resources/scripts/gridj.py
0 → 100644
View file @
bf78e965
# Copyright (c) 2015 Keith Schulze, Monash Micro Imaging, Monash University.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# Changelog:
# v1.1 - Added code to allow grid to automatically id tissue in H&E stain.
import
math
from
java.awt
import
Color
,
Font
from
javax.swing
import
(
ButtonGroup
,
BoxLayout
,
JButton
,
JFrame
,
JRadioButton
,
JLabel
,
JPanel
,
JSeparator
,
SwingConstants
,
WindowConstants
)
from
java.awt.event
import
KeyAdapter
,
KeyEvent
from
ij
import
IJ
,
WindowManager
from
ij.gui
import
OvalRoi
,
GenericDialog
from
ij.measure
import
Measurements
,
ResultsTable
from
ij.plugin.frame
import
RoiManager
from
ij.util
import
Tools
HARTS
=
"Hart's Stain"
HE
=
"H & E Stain"
class
CatOvalRoi
(
OvalRoi
):
'''
Categorised Oval Roi. Adds a classification parameter to the OvalRoi class
to allow categorisation of each Roi.
'''
AIRSPACE
=
1
TISSUE
=
2
SEPTAL_CREST
=
3
OPACITY
=
0.5
def
__init__
(
self
,
x
,
y
,
width
=
20
,
height
=
20
):
'''Constructor for Categorised Oval ROI - classification defaults to
airspace'''
OvalRoi
.
__init__
(
self
,
x
,
y
,
width
,
height
)
self
.
classification
=
self
.
AIRSPACE
# self.setStrokeColor()
self
.
setFillColor
(
Color
(
0.0
,
0.0
,
0.0
,
self
.
OPACITY
))
def
get_classification
():
return
self
.
classification
def
set_classification
(
classification
):
self
.
classification
=
classification
class
CatRMKeyAdapter
(
KeyAdapter
):
def
keyPressed
(
self
,
event
):
kc
=
event
.
getKeyCode
()
rm
=
CatRoiManager
.
getInstance
()
roi
=
rm
.
getSelectedRoisAsArray
()[
0
]
if
kc
==
KeyEvent
.
VK_1
:
roi
.
classification
=
CatOvalRoi
.
AIRSPACE
rm
.
runCommand
(
"Set Fill Color"
,
"#80"
+
Tools
.
c2hex
(
Color
(
0.0
,
0.0
,
0.0
))[
1
:])
rm
.
as_button
.
setSelected
(
True
)
elif
kc
==
KeyEvent
.
VK_2
:
roi
.
classification
=
CatOvalRoi
.
TISSUE
rm
.
runCommand
(
"Set Fill Color"
,
"#80"
+
Tools
.
c2hex
(
Color
(
0.0
,
0.6
,
0.0
))[
1
:])
rm
.
t_button
.
setSelected
(
True
)
elif
kc
==
KeyEvent
.
VK_3
:
roi
.
classification
=
CatOvalRoi
.
SEPTAL_CREST
rm
.
runCommand
(
"Set Fill Color"
,
Tools
.
c2hex
(
Color
(
1.0
,
0.0
,
0.0
)))
rm
.
sc_button
.
setSelected
(
True
)
event
.
consume
()
class
CatRoiManager
(
RoiManager
):
'''
Subclass of RoiManager that supports categorised ROIs
'''
RESULT_TITLE
=
"Septal Crest Count Summary"
def
__init__
(
self
,
image
,
result_table
):
super
(
CatRoiManager
,
self
).
__init__
()
self
.
image
=
image
self
.
load_image_key_handlers
()
self
.
result_table
=
result_table
self
.
frame
=
JFrame
(
'Classify ROIs'
,
defaultCloseOperation
=
WindowConstants
.
DISPOSE_ON_CLOSE
,
size
=
(
200
,
300
))
self
.
frame
.
windowClosing
=
self
.
wclose
panel
=
JPanel
()
panel
.
layout
=
BoxLayout
(
panel
,
BoxLayout
.
Y_AXIS
)
class_label
=
JLabel
(
"ROI Classification"
)
font
=
Font
(
class_label
.
font
.
fontName
,
Font
.
BOLD
,
12
)
class_label
.
font
=
font
rb_group
=
ButtonGroup
()
self
.
as_button
=
JRadioButton
(
'Airspace'
,
actionPerformed
=
self
.
classify_airspace
)
self
.
t_button
=
JRadioButton
(
'Tissue'
,
actionPerformed
=
self
.
classify_tissue
)
self
.
sc_button
=
JRadioButton
(
'Septal Crest'
,
actionPerformed
=
self
.
classify_septal_crest
)
rb_group
.
add
(
self
.
as_button
)
rb_group
.
add
(
self
.
t_button
)
rb_group
.
add
(
self
.
sc_button
)
panel
.
add
(
class_label
)
panel
.
add
(
self
.
as_button
)
panel
.
add
(
self
.
t_button
)
panel
.
add
(
self
.
sc_button
)
sep
=
JSeparator
(
SwingConstants
.
HORIZONTAL
)
panel
.
add
(
sep
)
measure_label
=
JLabel
(
"Measurements"
)
measure_label
.
font
=
font
self
.
measure_btn
=
JButton
(
"Measure"
,
actionPerformed
=
self
.
measure
)
panel
.
add
(
measure_label
)
panel
.
add
(
self
.
measure_btn
)
self
.
frame
.
add
(
panel
)
self
.
frame
.
pack
()
self
.
frame
.
visible
=
True
def
load_image_key_handlers
(
self
):
canvas
=
self
.
image
.
getWindow
().
getCanvas
()
kls
=
canvas
.
getKeyListeners
()
map
(
canvas
.
removeKeyListener
,
kls
)
canvas
.
addKeyListener
(
CatRMKeyAdapter
())
def
wclose
(
self
,
event
):
self
.
close
()
def
close
(
self
):
self
.
frame
.
dispose
()
self
.
super__close
()
def
valueChanged
(
self
,
event
):
roi
=
self
.
getSelectedRoisAsArray
()[
0
]
if
roi
.
classification
==
CatOvalRoi
.
AIRSPACE
:
self
.
as_button
.
setSelected
(
True
)
elif
roi
.
classification
==
CatOvalRoi
.
TISSUE
:
self
.
t_button
.
setSelected
(
True
)
elif
roi
.
classification
==
CatOvalRoi
.
SEPTAL_CREST
:
self
.
sc_button
.
setSelected
(
True
)
return
self
.
super__valueChanged
(
event
)
def
classify_airspace
(
self
,
event
):
roi
=
self
.
getSelectedRoisAsArray
()[
0
]
roi
.
classification
=
CatOvalRoi
.
AIRSPACE
self
.
runCommand
(
"Set Fill Color"
,
"#80"
+
Tools
.
c2hex
(
Color
(
0.0
,
0.0
,
0.0
))[
1
:])
def
classify_tissue
(
self
,
event
):
roi
=
self
.
getSelectedRoisAsArray
()[
0
]
roi
.
classification
=
CatOvalRoi
.
TISSUE
self
.
runCommand
(
"Set Fill Color"
,
"#80"
+
Tools
.
c2hex
(
Color
(
0.0
,
0.6
,
0.0
))[
1
:])
def
classify_septal_crest
(
self
,
event
):
roi
=
self
.
getSelectedRoisAsArray
()[
0
]
roi
.
classification
=
CatOvalRoi
.
SEPTAL_CREST
self
.
runCommand
(
"Set Fill Color"
,
Tools
.
c2hex
(
Color
(
1.0
,
0.0
,
0.0
)))
def
move_rois_to_overlay
(
self
,
image
):
self
.
moveRoisToOverlay
(
image
)
def
tabulateResults
(
self
,
image
,
rt
,
airspace_count
,
tissue_count
,
septal_crest_count
):
rt
.
incrementCounter
()
rt
.
addValue
(
"Title"
,
image
.
getTitle
())
rt
.
addValue
(
"Airspace"
,
airspace_count
)
rt
.
addValue
(
"Tissue"
,
tissue_count
)
rt
.
addValue
(
"Septal Crest"
,
septal_crest_count
)
rt
.
addValue
(
"Total"
,
airspace_count
+
tissue_count
+
septal_crest_count
)
def
measure
(
self
,
event
):
airspace_count
=
0
tissue_count
=
0
septal_crest_count
=
0
rois
=
self
.
getRoisAsArray
()
for
roi
in
rois
:
if
roi
.
classification
==
CatOvalRoi
.
AIRSPACE
:
airspace_count
+=
1
elif
roi
.
classification
==
CatOvalRoi
.
TISSUE
:
tissue_count
+=
1
elif
roi
.
classification
==
CatOvalRoi
.
SEPTAL_CREST
:
septal_crest_count
+=
1
else
:
print
"Whoops, a ROI has an unknown classification"
self
.
tabulateResults
(
self
.
image
,
self
.
result_table
,
airspace_count
,
tissue_count
,
septal_crest_count
)
print
"Successfully tabulated"
self
.
result_table
.
show
(
self
.
RESULT_TITLE
)
def
calculate_row_number
(
image
,
row_step_size
,
scaled_units
):
row_number
=
0
if
scaled_units
:
cal
=
image
.
getCalibration
()
step
=
cal
.
getRawY
(
row_step_size
)
row_number
=
math
.
ceil
((
image
.
getHeight
()
-
step
)
/
step
)
else
:
row_number
=
math
.
ceil
(
(
image
.
getHeight
()
-
row_step_size
)
/
row_step_size
)
return
row_number
def
calculate_column_number
(
image
,
column_step_size
,
alternate_rows_offset
,
scaled_units
):
col_number
=
0
offset_constant
=
1
if
alternate_rows_offset
:
offset_constant
=
2
if
scaled_units
:
cal
=
image
.
getCalibration
()
step
=
cal
.
getRawX
(
column_step_size
)
col_number
=
math
.
ceil
(
(
image
.
getWidth
()
-
offset_constant
*
step
)
/
step
)
else
:
col_number
=
math
.
ceil
(
(
image
.
getWidth
()
-
offset_constant
*
column_step_size
)
/
column_step_size
)
return
col_number
def
is_odd
(
x
):
return
x
&
1
def
create_grid
(
image
,
spot_diameter
,
row_step_size
,
column_step_size
,
alternate_rows_offset
,
scaled_units
,):
row_number
=
calculate_row_number
(
image
,
row_step_size
,
scaled_units
)
column_number
=
calculate_column_number
(
image
,
column_step_size
,
alternate_rows_offset
,
scaled_units
)
row_offset
=
row_step_size
/
2
col_offset
=
column_step_size
/
2
if
scaled_units
:
cal
=
image
.
getCalibration
()
row_step_size
=
cal
.
getRawY
(
row_step_size
)
column_step_size
=
cal
.
getRawX
(
column_step_size
)
row_offset
=
cal
.
getRawY
(
row_offset
)
col_offset
=
cal
.
getRawX
(
col_offset
)
rois
=
[]
if
alternate_rows_offset
:
offset_constant
=
1
for
row
in
xrange
(
int
(
row_number
)):
if
row
==
0
or
not
is_odd
(
row
):
offset_constant
=
1
else
:
offset_constant
=
2
y
=
row_offset
+
(
row
*
row_step_size
)
for
col
in
xrange
(
int
(
column_number
)):
x
=
(
col_offset
*
offset_constant
)
+
(
col
*
column_step_size
)
roi
=
CatOvalRoi
(
x
,
y
,
spot_diameter
,
spot_diameter
)
rois
.
append
(
roi
)
else
:
for
row
in
xrange
(
int
(
row_number
)):
y
=
row_offset
+
(
row
*
row_step_size
)
for
col
in
xrange
(
int
(
column_number
)):
x
=
col_offset
+
(
col
*
column_step_size
)
roi
=
CatOvalRoi
(
x
,
y
,
spot_diameter
,
spot_diameter
)
rois
.
append
(
roi
)
return
rois
def
populate_roi_manager
(
image
,
rois
,
result_table
):
crm
=
CatRoiManager
(
image
,
result_table
)
for
roi
in
rois
:
crm
.
addRoi
(
roi
)
crm
.
runCommand
(
"Show All"
)
return
crm
def
process_image
(
orig_image
,
tissue_type
):
image
=
orig_image
.
duplicate
()
IJ
.
run
(
image
,
"8-bit"
,
""
)
IJ
.
run
(
image
,
"Subtract Background..."
,
"rolling=50 light"
)
IJ
.
run
(
image
,
"Gaussian Blur..."
,
"sigma=2"
)
if
tissue_type
==
HARTS
:
IJ
.
setAutoThreshold
(
image
,
"Mean"
)
elif
tissue_type
==
HE
:
IJ
.
setAutoThreshold
(
image
,
"Triangle"
)
else
:
raise
Exception
(
"Oops this shouldn't happen, "
"please contact the developer."
)
IJ
.
run
(
image
,
"Convert to Mask"
,
""
)
return
image
def
score_roi
(
mask
,
roi
,
overlapLimit
=
0
):
roi
.
setImage
(
None
)
mask
.
setRoi
(
roi
)
stats
=
mask
.
getStatistics
(
Measurements
.
AREA_FRACTION
|
Measurements
.
LIMIT
,
2
)
return
stats
.
areaFraction
>
overlapLimit
def
mark_tissue
(
image
,
tissue_type
,
rois
,
overlapLimit
=
0
):
bin_mask
=
process_image
(
image
,
tissue_type
)
for
roi
in
rois
:
if
score_roi
(
bin_mask
,
roi
,
overlapLimit
):
roi
.
classification
=
CatOvalRoi
.
TISSUE
roi
.
setFillColor
(
Color
(
0.0
,
0.6
,
0.0
,
0.5
))
bin_mask
.
flush
()
def
run
():
image
=
IJ
.
getImage
()
result_table
=
None
win
=
WindowManager
.
getFrame
(
CatRoiManager
.
RESULT_TITLE
)
if
win
is
not
None
:
result_table
=
win
.
getTextPanel
().
getResultsTable
()
else
:
result_table
=
ResultsTable
()
# Grid Parameters
spot_diameter
=
12
row_step_size
=
50
column_step_size
=
50
alternate_rows_offset
=
False
scaled_units
=
False
# Analysis Parameters
classify_tissue
=
True
roi_overlap
=
50
tissue_types
=
[
HARTS
,
HE
]
gd
=
GenericDialog
(
"Analysis Parameters"
)
gd
.
addMessage
(
"Grid Paramaters"
)
gd
.
addNumericField
(
"Spot diameter"
,
spot_diameter
,
1
)
gd
.
addNumericField
(
"Row step size"
,
row_step_size
,
1
)
gd
.
addNumericField
(
"Column step size"
,
column_step_size
,
1
)
gd
.
addCheckbox
(
"Offset rows"
,
alternate_rows_offset
)
gd
.
addCheckbox
(
"Use scaled units"
,
scaled_units
)
gd
.
addMessage
(
"Analysis Parameters"
)
gd
.
addCheckbox
(
"Classify tissue"
,
classify_tissue
)
gd
.
addNumericField
(
"ROI overlap limit (%)"
,
roi_overlap
,
0
)
gd
.
addRadioButtonGroup
(
"Tissue type"
,
tissue_types
,
len
(
tissue_types
),
1
,
HARTS
)
gd
.
showDialog
()
if
gd
.
wasCanceled
():
return
spot_diameter
=
gd
.
getNextNumber
()
row_step_size
=
gd
.
getNextNumber
()
column_step_size
=
gd
.
getNextNumber
()
alternate_rows_offset
=
gd
.
getNextBoolean
()
scaled_units
=
gd
.
getNextBoolean
()
classify_tissue
=
gd
.
getNextBoolean
()
roi_overlap
=
gd
.
getNextNumber
()
/
100
tissue_type
=
gd
.
getNextRadioButton
()
rois
=
create_grid
(
image
,
spot_diameter
,
row_step_size
,
column_step_size
,
alternate_rows_offset
,
scaled_units
)
if
classify_tissue
:
mark_tissue
(
image
,
tissue_type
,
rois
,
roi_overlap
)
rm
=
populate_roi_manager
(
image
,
rois
,
result_table
)
run
()
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment