Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F64114534
TestImageAccessor.java
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Fri, May 24, 16:50
Size
16 KB
Mime Type
text/x-java
Expires
Sun, May 26, 16:50 (2 d)
Engine
blob
Format
Raw Data
Handle
17857639
Attached To
R6269 BIOP Run Macro
TestImageAccessor.java
View Options
/*-
* #%L
* Fiji's plugin for colocalization analysis.
* %%
* Copyright (C) 2009 - 2017 Fiji developers.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
package
sc.fiji.coloc.tests
;
import static
org.junit.Assume.assumeNotNull
;
import
ij.ImagePlus
;
import
ij.gui.NewImage
;
import
ij.gui.Roi
;
import
ij.io.Opener
;
import
ij.process.ImageProcessor
;
import
java.awt.Color
;
import
java.io.BufferedInputStream
;
import
java.io.InputStream
;
import
java.util.Arrays
;
import
net.imglib2.Cursor
;
import
net.imglib2.Interval
;
import
net.imglib2.Localizable
;
import
net.imglib2.Point
;
import
net.imglib2.RandomAccess
;
import
net.imglib2.RandomAccessible
;
import
net.imglib2.RandomAccessibleInterval
;
import
net.imglib2.TwinCursor
;
import
net.imglib2.algorithm.gauss.Gauss
;
import
net.imglib2.algorithm.math.ImageStatistics
;
import
net.imglib2.img.ImagePlusAdapter
;
import
net.imglib2.img.Img
;
import
net.imglib2.img.ImgFactory
;
import
net.imglib2.img.array.ArrayImgFactory
;
import
net.imglib2.type.NativeType
;
import
net.imglib2.type.logic.BitType
;
import
net.imglib2.type.numeric.RealType
;
import
net.imglib2.type.numeric.real.FloatType
;
import
net.imglib2.view.Views
;
import
sc.fiji.coloc.algorithms.MissingPreconditionException
;
import
sc.fiji.coloc.gadgets.MaskFactory
;
/**
* A class containing some testing helper methods. It allows
* to open Tiffs from within the Jar file and can generate noise
* images.
*
* @author Dan White
* @author Tom Kazimiers
*/
public
class
TestImageAccessor
{
/* a static opener for opening images without the
* need for creating every time a new opener
*/
static
Opener
opener
=
new
Opener
();
/**
* Loads a Tiff file from within the jar. The given path is treated
* as relative to this tests-package (i.e. "Data/test.tiff" refers
* to the test.tiff in sub-folder Data).
*
* @param <T> The wanted output type.
* @param relPath The relative path to the Tiff file.
* @return The file as ImgLib image.
*/
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
RandomAccessibleInterval
<
T
>
loadTiffFromJar
(
String
relPath
)
{
InputStream
is
=
TestImageAccessor
.
class
.
getResourceAsStream
(
relPath
);
BufferedInputStream
bis
=
new
BufferedInputStream
(
is
);
ImagePlus
imp
=
opener
.
openTiff
(
bis
,
"The Test Image"
);
assumeNotNull
(
imp
);
return
ImagePlusAdapter
.
wrap
(
imp
);
}
/**
* Loads a Tiff file from within the jar to use as a mask Cursor.
* So we use Img<T> which has a cursor() method.
* The given path is treated
* as relative to this tests-package (i.e. "Data/test.tiff" refers
* to the test.tiff in sub-folder Data).
*
* @param <T> The wanted output type.
* @param relPath The relative path to the Tiff file.
* @return The file as ImgLib image.
*/
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
Img
<
T
>
loadTiffFromJarAsImg
(
String
relPath
)
{
InputStream
is
=
TestImageAccessor
.
class
.
getResourceAsStream
(
relPath
);
BufferedInputStream
bis
=
new
BufferedInputStream
(
is
);
ImagePlus
imp
=
opener
.
openTiff
(
bis
,
"The Test Image"
);
assumeNotNull
(
imp
);
return
ImagePlusAdapter
.
wrap
(
imp
);
}
/**
* Creates a noisy image that is created by repeatedly adding points
* with random intensity to the canvas. That way it tries to mimic the
* way a microscope produces images. This convenience method uses the
* default values of a point size of 3.0 and produces 5000 points.
* After the creation the image is smoothed with a sigma of one in each
* direction.
*
* @param <T> The wanted output type.
* @param width The image width.
* @param height The image height.
* @return The noise image.
*/
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
RandomAccessibleInterval
<
T
>
produceNoiseImageSmoothed
(
T
type
,
int
width
,
int
height
)
{
return
produceNoiseImageSmoothed
(
type
,
width
,
height
,
3.0f
,
5000
,
new
double
[]
{
1.0
,
1.0
});
}
/**
* Creates a noisy image that is created by repeatedly adding points
* with random intensity to the canvas. That way it tries to mimic the
* way a microscope produces images.
*
* @param <T> The wanted output type.
* @param width The image width.
* @param height The image height.
* @param dotSize The size of the dots.
* @param numDots The number of dots.
* @param smoothingSigma The two dimensional sigma for smoothing.
* @return The noise image.
*/
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
RandomAccessibleInterval
<
T
>
produceNoiseImage
(
int
width
,
int
height
,
float
dotSize
,
int
numDots
)
{
/* For now (probably until ImageJ2 is out) we use an
* ImageJ image to draw circles.
*/
int
options
=
NewImage
.
FILL_BLACK
+
NewImage
.
CHECK_AVAILABLE_MEMORY
;
ImagePlus
img
=
NewImage
.
createByteImage
(
"Noise"
,
width
,
height
,
1
,
options
);
ImageProcessor
imp
=
img
.
getProcessor
();
float
dotRadius
=
dotSize
*
0.5f
;
int
dotIntSize
=
(
int
)
dotSize
;
for
(
int
i
=
0
;
i
<
numDots
;
i
++)
{
int
x
=
(
int
)
(
Math
.
random
()
*
width
-
dotRadius
);
int
y
=
(
int
)
(
Math
.
random
()
*
height
-
dotRadius
);
imp
.
setColor
(
Color
.
WHITE
);
imp
.
fillOval
(
x
,
y
,
dotIntSize
,
dotIntSize
);
}
// we changed the data, so update it
img
.
updateImage
();
// create the new image
RandomAccessibleInterval
<
T
>
noiseImage
=
ImagePlusAdapter
.
wrap
(
img
);
return
noiseImage
;
}
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
RandomAccessibleInterval
<
T
>
produceNoiseImageSmoothed
(
T
type
,
int
width
,
int
height
,
float
dotSize
,
int
numDots
,
double
[]
smoothingSigma
)
{
RandomAccessibleInterval
<
T
>
noiseImage
=
produceNoiseImage
(
width
,
height
,
dotSize
,
numDots
);
return
gaussianSmooth
(
noiseImage
,
smoothingSigma
);
}
/**
* This method creates a noise image that has a specified mean.
* Every pixel has a value uniformly distributed around mean with
* the maximum spread specified.
*
* @return a new noise image
* @throws MissingPreconditionException if specified means and spreads are not valid
*/
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
RandomAccessibleInterval
<
T
>
produceMeanBasedNoiseImage
(
T
type
,
int
width
,
int
height
,
double
mean
,
double
spread
,
double
[]
smoothingSigma
)
throws
MissingPreconditionException
{
if
(
mean
<
spread
||
(
mean
+
spread
)
>
type
.
getMaxValue
())
{
throw
new
MissingPreconditionException
(
"Mean must be larger than spread, and mean plus spread must be smaller than max of the type"
);
}
// create the new image
ImgFactory
<
T
>
imgFactory
=
new
ArrayImgFactory
<
T
>();
RandomAccessibleInterval
<
T
>
noiseImage
=
imgFactory
.
create
(
new
int
[]
{
width
,
height
},
type
);
// "Noise image");
for
(
T
value
:
Views
.
iterable
(
noiseImage
))
{
value
.
setReal
(
mean
+
(
(
Math
.
random
()
-
0.5
)
*
spread
)
);
}
return
gaussianSmooth
(
noiseImage
,
smoothingSigma
);
}
/**
* This method creates a noise image that is made of many little
* sticks oriented in a random direction. How many of them and
* what the length of them are can be specified.
*
* @return a new noise image that is not smoothed
*/
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
RandomAccessibleInterval
<
T
>
produceSticksNoiseImage
(
int
width
,
int
height
,
int
numSticks
,
int
lineWidth
,
double
maxLength
)
{
/* For now (probably until ImageJ2 is out) we use an
* ImageJ image to draw lines.
*/
int
options
=
NewImage
.
FILL_BLACK
+
NewImage
.
CHECK_AVAILABLE_MEMORY
;
ImagePlus
img
=
NewImage
.
createByteImage
(
"Noise"
,
width
,
height
,
1
,
options
);
ImageProcessor
imp
=
img
.
getProcessor
();
imp
.
setColor
(
Color
.
WHITE
);
imp
.
setLineWidth
(
lineWidth
);
for
(
int
i
=
0
;
i
<
numSticks
;
i
++)
{
// find random starting point
int
x
=
(
int
)
(
Math
.
random
()
*
width
);
int
y
=
(
int
)
(
Math
.
random
()
*
height
);
// create random stick length and direction
double
length
=
Math
.
random
()
*
maxLength
;
double
angle
=
Math
.
random
()
*
2
*
Math
.
PI
;
// calculate random point on circle, for the direction
int
destX
=
x
+
(
int
)
(
length
*
Math
.
cos
(
angle
));
int
destY
=
y
+
(
int
)
(
length
*
Math
.
sin
(
angle
));
// now draw the line
imp
.
drawLine
(
x
,
y
,
destX
,
destY
);
}
// we changed the data, so update it
img
.
updateImage
();
return
ImagePlusAdapter
.
wrap
(
img
);
}
/**
* This method creates a smoothed noise image that is made of
* many little sticks oriented in a random direction. How many
* of them and what the length of them are can be specified.
*
* @return a new noise image that is smoothed
*/
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
RandomAccessibleInterval
<
T
>
produceSticksNoiseImageSmoothed
(
T
type
,
int
width
,
int
height
,
int
numSticks
,
int
lineWidth
,
double
maxLength
,
double
[]
smoothingSigma
)
{
RandomAccessibleInterval
<
T
>
noiseImage
=
produceSticksNoiseImage
(
width
,
height
,
numSticks
,
lineWidth
,
maxLength
);
return
gaussianSmooth
(
noiseImage
,
smoothingSigma
);
}
/**
* Generates a Perlin noise image. It is based on Ken Perlin's
* reference implementation (ImprovedNoise class) and a small
* bit of Kas Thomas' sample code (http://asserttrue.blogspot.com/).
*/
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
RandomAccessibleInterval
<
T
>
producePerlinNoiseImage
(
T
type
,
int
width
,
int
height
,
double
z
,
double
scale
)
{
// create the new image
ImgFactory
<
T
>
imgFactory
=
new
ArrayImgFactory
<
T
>();
RandomAccessibleInterval
<
T
>
noiseImage
=
imgFactory
.
create
(
new
int
[]
{
width
,
height
},
type
);
Cursor
<
T
>
noiseCursor
=
Views
.
iterable
(
noiseImage
).
localizingCursor
();
double
xOffset
=
Math
.
random
()
*
(
width
*
width
);
double
yOffset
=
Math
.
random
()
*
(
height
*
height
);
while
(
noiseCursor
.
hasNext
())
{
noiseCursor
.
fwd
();
double
x
=
(
noiseCursor
.
getDoublePosition
(
0
)
+
xOffset
)
*
scale
;
double
y
=
(
noiseCursor
.
getDoublePosition
(
1
)
+
yOffset
)
*
scale
;
float
t
=
(
float
)
ImprovedNoise
.
noise
(
x
,
y
,
z
);
// ImprovedNoise.noise returns a float in the range [-1..1],
// whereas we want a float in the range [0..1], so:
t
=
(
1
+
t
)
*
0.5f
;
noiseCursor
.
get
().
setReal
(
t
);
}
//return gaussianSmooth(noiseImage, imgFactory, smoothingSigma);
return
noiseImage
;
}
/**
* Gaussian Smooth of the input image using intermediate float format.
* @param <T>
* @param img
* @param sigma
* @return
*/
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
RandomAccessibleInterval
<
T
>
gaussianSmooth
(
RandomAccessibleInterval
<
T
>
img
,
double
[]
sigma
)
{
Interval
interval
=
Views
.
iterable
(
img
);
ImgFactory
<
T
>
outputFactory
=
new
ArrayImgFactory
<
T
>();
final
long
[]
dim
=
new
long
[
img
.
numDimensions
()
];
img
.
dimensions
(
dim
);
RandomAccessibleInterval
<
T
>
output
=
outputFactory
.
create
(
dim
,
img
.
randomAccess
().
get
().
createVariable
()
);
final
long
[]
pos
=
new
long
[
img
.
numDimensions
()
];
Arrays
.
fill
(
pos
,
0
);
Localizable
origin
=
new
Point
(
pos
);
ImgFactory
<
FloatType
>
tempFactory
=
new
ArrayImgFactory
<
FloatType
>();
RandomAccessible
<
T
>
input
=
Views
.
extendMirrorSingle
(
img
);
Gauss
.
inFloat
(
sigma
,
input
,
interval
,
output
,
origin
,
tempFactory
);
return
output
;
}
/**
* Inverts an image.
*
* @param <T> The images data type.
* @param image The image to convert.
* @return The inverted image.
*/
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
RandomAccessibleInterval
<
T
>
invertImage
(
RandomAccessibleInterval
<
T
>
image
)
{
Cursor
<
T
>
imgCursor
=
Views
.
iterable
(
image
).
localizingCursor
();
// invert the image
long
[]
dim
=
new
long
[
image
.
numDimensions
()
];
image
.
dimensions
(
dim
);
ArrayImgFactory
<
T
>
imgFactory
=
new
ArrayImgFactory
<
T
>();
RandomAccessibleInterval
<
T
>
invImg
=
imgFactory
.
create
(
dim
,
image
.
randomAccess
().
get
().
createVariable
()
);
// "Inverted " + image.getName());
RandomAccess
<
T
>
invCursor
=
invImg
.
randomAccess
();
while
(
imgCursor
.
hasNext
())
{
imgCursor
.
fwd
();
invCursor
.
setPosition
(
imgCursor
);
invCursor
.
get
().
setReal
(
imgCursor
.
get
().
getMaxValue
()
-
imgCursor
.
get
().
getRealDouble
()
);
}
return
invImg
;
}
/**
* Converts an arbitrary image to a black/white version of it.
* All image data lower or equal 0.5 times the maximum value
* of the image type will get black, the rest will turn white.
*/
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
RandomAccessibleInterval
<
T
>
makeBinaryImage
(
RandomAccessibleInterval
<
T
>
image
)
{
T
binSplitValue
=
image
.
randomAccess
().
get
();
binSplitValue
.
setReal
(
binSplitValue
.
getMaxValue
()
*
0.5
);
return
TestImageAccessor
.
makeBinaryImage
(
image
,
binSplitValue
);
}
/**
* Converts an arbitrary image to a black/white version of it.
* All image data lower or equal the splitValue will get black,
* the rest will turn white.
*/
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
RandomAccessibleInterval
<
T
>
makeBinaryImage
(
RandomAccessibleInterval
<
T
>
image
,
T
splitValue
)
{
Cursor
<
T
>
imgCursor
=
Views
.
iterable
(
image
).
localizingCursor
();
// make a new image of the same type, but binary
long
[]
dim
=
new
long
[
image
.
numDimensions
()
];
image
.
dimensions
(
dim
);
ArrayImgFactory
<
T
>
imgFactory
=
new
ArrayImgFactory
<
T
>();
RandomAccessibleInterval
<
T
>
binImg
=
imgFactory
.
create
(
dim
,
image
.
randomAccess
().
get
().
createVariable
()
);
// "Binary image of " + image.getName());
RandomAccess
<
T
>
invCursor
=
binImg
.
randomAccess
();
while
(
imgCursor
.
hasNext
())
{
imgCursor
.
fwd
();
invCursor
.
setPosition
(
imgCursor
);
T
currentValue
=
invCursor
.
get
();
if
(
currentValue
.
compareTo
(
splitValue
)
>
0
)
currentValue
.
setReal
(
currentValue
.
getMaxValue
()
);
else
currentValue
.
setZero
();
}
return
binImg
;
}
/**
* A method to combine a foreground image and a background image.
* If data on the foreground image is above zero, it will be
* placed on the background. While doing that, the image data from
* the foreground is scaled to be in range of the background.
*/
public
static
<
T
extends
RealType
<
T
>>
void
combineImages
(
RandomAccessibleInterval
<
T
>
background
,
RandomAccessibleInterval
<
T
>
foreground
)
{
final
long
[]
dim
=
new
long
[
background
.
numDimensions
()
];
background
.
dimensions
(
dim
);
RandomAccessibleInterval
<
BitType
>
alwaysTrueMask
=
MaskFactory
.
createMask
(
dim
,
true
);
TwinCursor
<
T
>
cursor
=
new
TwinCursor
<
T
>(
background
.
randomAccess
(),
foreground
.
randomAccess
(),
Views
.
iterable
(
alwaysTrueMask
).
localizingCursor
());
// find a scaling factor for scale forground range into background
double
bgMin
=
ImageStatistics
.
getImageMin
(
background
).
getRealDouble
();
double
bgMax
=
ImageStatistics
.
getImageMax
(
background
).
getRealDouble
();
double
fgMin
=
ImageStatistics
.
getImageMin
(
foreground
).
getRealDouble
();
double
fgMax
=
ImageStatistics
.
getImageMax
(
foreground
).
getRealDouble
();
double
scaling
=
(
bgMax
-
bgMin
)
/
(
fgMax
-
fgMin
);
// iterate over both images
while
(
cursor
.
hasNext
())
{
cursor
.
fwd
();
T
bgData
=
cursor
.
getFirst
();
double
fgData
=
cursor
.
getSecond
().
getRealDouble
()
*
scaling
;
if
(
fgData
>
0.01
)
{
/* if the foreground data is above zero, copy
* it to the background.
*/
bgData
.
setReal
(
fgData
);
}
}
}
/**
* Creates a mask image with a black background and a white
* rectangular foreground.
*
* @param width The width of the result image.
* @param height The height of the result image.
* @param offset The offset of the rectangular mask.
* @param size The size of the rectangular mask.
* @return A black image with a white rectangle on it.
*/
public
static
<
T
extends
RealType
<
T
>
&
NativeType
<
T
>>
RandomAccessibleInterval
<
T
>
createRectengularMaskImage
(
long
width
,
long
height
,
long
[]
offset
,
long
[]
size
)
{
/* For now (probably until ImageJ2 is out) we use an
* ImageJ image to draw lines.
*/
int
options
=
NewImage
.
FILL_BLACK
+
NewImage
.
CHECK_AVAILABLE_MEMORY
;
ImagePlus
img
=
NewImage
.
createByteImage
(
"Noise"
,
(
int
)
width
,
(
int
)
height
,
1
,
options
);
ImageProcessor
imp
=
img
.
getProcessor
();
imp
.
setColor
(
Color
.
WHITE
);
Roi
rect
=
new
Roi
(
offset
[
0
],
offset
[
1
],
size
[
0
],
size
[
1
]);
imp
.
fill
(
rect
);
// we changed the data, so update it
img
.
updateImage
();
return
ImagePlusAdapter
.
wrap
(
img
);
}
}
Event Timeline
Log In to Comment