made lots of tests to attempt to use opencv's calcHist function

Due to the lack of documentation, I had to do a lot of tests to find out
how to use opencv in jython, as the interface is not the same as in
python...
In the end, I managed to work out a set of arguments that deosn't cause
an exception.
This commit is contained in:
Guillaume Raffy 2019-04-11 20:23:20 +02:00
parent de1e837d60
commit 361fb552e7
1 changed files with 289 additions and 22 deletions

311
lipase.py
View File

@ -5,7 +5,15 @@
# It is the duty of the scripting framework to harvest
# the 'name' parameter from the user, and then display
# the 'greeting' output parameter, based on its type.
from ij import IJ
from ij import IJ # pylint: disable=import-error
from ijopencv.ij import ImagePlusMatConverter # pylint: disable=import-error
from ijopencv.opencv import MatImagePlusConverter # pylint: disable=import-error
from ij import ImagePlus # pylint: disable=import-error
import org.bytedeco.javacpp.opencv_core as opencv_core # pylint: disable=import-error
import org.bytedeco.javacpp.opencv_imgproc as opencv_imgproc # pylint: disable=import-error
from org.bytedeco.javacpp.opencv_imgcodecs import imread # pylint: disable=import-error
# greeting = "Hello, " + name + "!"
@ -36,6 +44,7 @@ from ij import IJ
# - cond[5678] : condition non réalistes
import json
class Sequence(object):
def __init__(self, catalog, sequence_id, micro_manager_metadata_file_path):
self.catalog = catalog
@ -44,7 +53,8 @@ class Sequence(object):
print(micro_manager_metadata_file_path)
with open(micro_manager_metadata_file_path, "r") as mmm_file:
self.mmm = json.load(mmm_file, encoding='latin-1') # note : the micromanager metadata files are encoded in latin-1, not utf8 (see accents in comments)
self.mmm = json.load(mmm_file, encoding='latin-1') # note : the micromanager metadata files are encoded in latin-1, not utf8 (see accents in comments)
@property
def num_frames(self):
summary = self.mmm['Summary']
@ -79,20 +89,51 @@ class Sequence(object):
return '/'.join(self.micro_manager_metadata_file_path.split('/')[:-1])
def get_image_file_path(self, channel_index, frame_index, z_index=0):
'''
:param int channel_index:
:param int frame_index:
:param int z_index:
'''
assert frame_index < self.num_frames
assert channel_index < self.num_channels
summary = self.mmm['Summary']
frame_info = self.mmm['FrameKey-%d-%d-%d' % (frame_index, channel_index, z_index) ]
frame_info = self.mmm['FrameKey-%d-%d-%d' % (frame_index, channel_index, z_index)]
rel_file_path = frame_info['FileName']
return self.get_root_path() + '/' + rel_file_path
def get_channel_index(self, channel_id):
'''
:param str channel_id:
'''
summary = self.mmm['Summary']
channel_index = summary['ChNames'].index(channel_id)
return
return channel_index
def get_black(self):
''' returns the black sequence related to the the sequence self
:return Sequence:
'''
seqid_to_black = {
'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0': 'res_soleil2018/DARK/DARK_40X_60min_1 im pae min_1/Pos0',
'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2': 'res_soleil2018/DARK/DARK_40X_60min_1 im pae min_1/Pos0',
}
white_sequence = seqid_to_black[self.sequence_id]
return self.catalog.sequences[white_sequence]
def get_white(self):
''' returns the white sequence related to the the sequence self
:return Sequence:
'''
seqid_to_white = {
'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0': 'res_soleil2018/white/white_24112018_2/Pos0',
'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2': 'res_soleil2018/white/white_24112018_2/Pos0',
}
white_sequence = seqid_to_white[self.sequence_id]
return self.catalog.sequences[white_sequence]
def open_in_imagej(self):
#ip = IJ.createHyperStack(title=self.sequence_id, width=self.width, height= self.height, channels=1, slices=1, frames=self.get_num_frames(), bitdepth=16)
# ip = IJ.createHyperStack(title=self.sequence_id, width=self.width, height= self.height, channels=1, slices=1, frames=self.get_num_frames(), bitdepth=16)
hyperstack = IJ.createHyperStack(self.sequence_id, self.width, self.height, self.num_channels, self.num_slices, self.num_frames, self.num_bits_per_pixels)
for channel_index in range(self.num_channels):
for frame_index in range(self.num_frames):
@ -101,58 +142,284 @@ class Sequence(object):
# print(src_image_file_path)
src_image = IJ.openImage(src_image_file_path)
# print(src_image.getProperties())
hyperstack.setPositionWithoutUpdate(channel_index+1, slice_index+1, frame_index+1)
hyperstack.setPositionWithoutUpdate(channel_index + 1, slice_index + 1, frame_index + 1)
hyperstack.setProcessor(src_image.getProcessor())
hyperstack.show()
for channel_index in range(self.num_channels):
hyperstack.setPositionWithoutUpdate(channel_index+1, 1, 1)
hyperstack.setPositionWithoutUpdate(channel_index + 1, 1, 1)
IJ.run("Enhance Contrast", "saturated=0.35")
return hyperstack
class ImageCatalog(object):
def __init__(self, raw_images_root):
self.raw_images_root = raw_images_root
self.sequences = { }
self.sequences = {}
# nb : we use the path as sequence id because the "Comment" field in the summary section of the metadata file is not guaranteed to be unique (eg they are the same in res_soleil2018/white/white_24112018_1/Pos0 and in res_soleil2018/white/white_24112018_2/Pos0)
sequence_ids = []
sequence_ids.append('res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0')
sequence_ids.append('res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2')
sequence_ids.append('res_soleil2018/DARK/DARK_40X_60min_1 im pae min_1/Pos0')
sequence_ids.append('res_soleil2018/DARK/DARK_40X_zstack_vis_327-353_1/Pos0')
# sequence_ids.append('res_soleil2018/white/white_24112018_1/Pos0') # this sequence seems broken (only 5 images while there's supposed to be 201 frames)
sequence_ids.append('res_soleil2018/white/white_24112018_2/Pos0')
for sequence_id in sequence_ids:
micro_manager_metadata_file_path = raw_images_root + '/' + sequence_id + '/metadata.txt'
#micro_manager_metadata_file_path = '/tmp/toto.json'
self.sequences[ sequence_id ] = Sequence(self, sequence_id, micro_manager_metadata_file_path)
# micro_manager_metadata_file_path = '/tmp/toto.json'
self.sequences[sequence_id] = Sequence(self, sequence_id, micro_manager_metadata_file_path)
def __str__(self):
for sequence_id, sequence in self.sequences.iteritems():
return str(sequence_id) + ':' + str(sequence)
# self.add_micromanager_metadata(raw_images_root + '/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0/metadata.txt')
#def add_micromanager_metadata(self, micro_manager_metadata_file_path):
# def add_micromanager_metadata(self, micro_manager_metadata_file_path):
# self.sequences[ micro_manager_metadata_file_path ] = Sequence(self, micro_manager_metadata_file_path)
def test_opencv_calc_hist1():
src_image = opencv_core.Mat([128, 128])
histogram = opencv_imgproc.cvCalcHist(src_image) # pylint: disable=unused-variable
# TypeError: cvCalcHist(): expected 2 or 4 args; got 1
def test_opencv_calc_hist2():
src_image = opencv_core.Mat([128, 128])
histogram = opencv_core.Mat()
opencv_imgproc.cvCalcHist(src_image, histogram)
# TypeError: cvCalcHist(): 1st arg can't be coerced to org.bytedeco.javacpp.opencv_core$IplImage
def test_opencv_calc_hist3():
src_image = opencv_core.Mat([128, 128])
histogram = opencv_imgproc.calcHist(src_image) # pylint: disable=unused-variable
# TypeError: calcHist(): expected 6-8 or 10 args; got 1
# ah: ./opencv/src/main/java/org/bytedeco/javacpp/opencv_imgproc.java seems to contain the list of 57 allowed calcHist sets of arguments, as the number of arguments match
# [OK]graffy@pr079234:~/toto/javacpp-presets[19:45:41]>grep -A 5 calcHist ./opencv/src/main/java/org/bytedeco/javacpp/opencv_imgproc.java | sed -E 's/^[ ]*//g' | tr '\n' '`' | sed 's/,`/, /g' | tr '`' '\n' | grep '^@Namespace' | wc -l
# 57
# [OK]graffy@pr079234:~/toto/javacpp-presets[19:45:56]>grep -A 5 calcHist ./opencv/src/main/java/org/bytedeco/javacpp/opencv_imgproc.java | sed -E 's/^[ ]*//g' | tr '\n' '`' | sed 's/,`/, /g' | tr '`' '\n' | grep '^@Namespace' | head -2
# @Namespace("cv") public static native void calcHist( @Const Mat images, int nimages, @Const IntPointer channels, @ByVal Mat mask, @ByVal Mat hist, int dims, @Const IntPointer histSize, @Cast("const float**") PointerPointer ranges, @Cast("bool") boolean uniform/*=true*/, @Cast("bool") boolean accumulate/*=false*/ );
# @Namespace("cv") public static native void calcHist( @Const Mat images, int nimages, @Const IntPointer channels, @ByVal Mat mask, @ByVal Mat hist, int dims, @Const IntPointer histSize, @Const @ByPtrPtr FloatPointer ranges );
# [OK]graffy@pr079234:~/toto/javacpp-presets[19:38:39]>grep -A 5 calcHist ./opencv/src/main/java/org/bytedeco/javacpp/opencv_imgproc.java | sed -E 's/^[ ]*//g' | tr '\n' '`' | sed 's/,`/, /g' | tr '`' '\n' | grep '^@Namespace' | awk '-F,' '{print NF}' | sort --unique -n
# 6
# 7
# 8
# 10
def test_opencv_calc_hist4():
src_image = opencv_core.Mat([128, 128])
src_images = opencv_core.MatVector()
# print(dir(src_images))
src_images.push_back(src_image)
print(type(src_images))
# <type 'org.bytedeco.javacpp.opencv_core$MatVector'>
print(src_images)
# [org.bytedeco.javacpp.opencv_core$Mat[width=1024,height=1024,depth=8,channels=3]]
opencv_imgproc.calcHist(src_images, [0], None, None, [256], [0.0, 256.0])
# TypeError: calcHist(): 1st arg can't be coerced to org.bytedeco.javacpp.opencv_core$UMatVector, org.bytedeco.javacpp.opencv_core$GpuMatVector, org.bytedeco.javacpp.opencv_core$MatVector
def test_opencv_calc_hist5():
src_image = opencv_core.Mat([128, 128])
histogram = opencv_core.Mat()
# @Namespace("cv") public static native void calcHist( @Const Mat images, int nimages, @Const int[] channels, @ByVal Mat mask, @ByVal Mat hist, int dims, @Const int[] histSize, @Const @ByPtrPtr float[] ranges );
opencv_imgproc.calcHist(src_image, 1, [0], None, histogram, 1, [256], [0.0, 256.0])
# at org.bytedeco.javacpp.opencv_imgproc.calcHist(Native Method)
# at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
# at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
# at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
# at java.lang.reflect.Method.invoke(Method.java:497)
# java.lang.NullPointerException: java.lang.NullPointerException: Pointer address of argument 3 is NULL.
def test_opencv_calc_hist6():
src_image = opencv_core.Mat([128, 128])
histogram = opencv_core.Mat()
mask = opencv_core.Mat()
# @Namespace("cv") public static native void calcHist( @Const Mat images, int nimages, @Const int[] channels, @ByVal Mat mask, @ByVal Mat hist, int dims, @Const int[] histSize, @Const @ByPtrPtr float[] ranges );
opencv_imgproc.calcHist(src_image, 1, [0], mask, histogram, 1, [256], [0.0, 256.0])
# java.lang.RuntimeException: java.lang.RuntimeException: OpenCV(3.4.2) /Users/travis/build/bytedeco/javacpp-presets/opencv/cppbuild/macosx-x86_64/opencv-3.4.2/modules/imgproc/src/histogram.cpp:918: error: (-210:Unsupported format or combination of formats) in function 'calcHist'
def test_opencv_calc_hist7():
pixel_type=0
# 0 : depth=8
# 1 : depth=-2147483640
# 2 : depth=16
# 3 : depth=-2147483640
# 4 : depth=-2147483640
# 8 : depth=8, channels=2
src_image = opencv_core.Mat().zeros(128, 128, pixel_type).asMat()
print('src_image', src_image)
histogram = opencv_core.Mat()
print('histogram', histogram)
mask = opencv_core.Mat()
print('mask', mask)
# @Namespace("cv") public static native void calcHist( @Const Mat images, int nimages, @Const int[] channels, @ByVal Mat mask, @ByVal Mat hist, int dims, @Const int[] histSize, @Const @ByPtrPtr float[] ranges );
opencv_imgproc.calcHist(src_image, 1, [0], mask, histogram, 1, [256], [0.0, 256.0])
def test_opencv():
'''
'''
# https://forum.image.sc/t/use-opencv-within-jython-macro/10320/4
test_opencv_calc_hist7()
# histogram = opencv_imgproc.calcHist(images=[src_image], channels=[0], mask=None, histSize=[256], ranges=[0, 256])
# histogram = opencv_imgproc.calcHist(src_image, 0, None, 3, [256], [0.0, 256.0], True, False)
# print(dir(opencv_imgproc))
# help(opencv_imgproc.cvCalcHist)
# opencv_imgproc.calcHist(src_image, None, histogram)
# histogram = opencv_imgproc.cvCalcHist(src_image, None, [256], [0.0, 256.0])
# import inspect
# lines = inspect.getsource(opencv_imgproc.calcHist)
# opencv_imgproc.calcHist(src_image, histogram)
# there are 2 calcHist functions in /Applications/Fiji.app/jars/opencv-3.4.2-1.4.2.jar, and they take 6 or 7 arguments :
# jar -xvf /Applications/Fiji.app/jars/opencv-3.4.2-1.4.2.jar
# [1]graffy@pr079234:~/toto/jython[15:18:17]>javap ./org/opencv/imgproc/Imgproc.class | grep calcHist
# public static void calcHist(java.util.List<org.opencv.core.Mat>, org.opencv.core.MatOfInt, org.opencv.core.Mat, org.opencv.core.Mat, org.opencv.core.MatOfInt, org.opencv.core.MatOfFloat, boolean);
# public static void calcHist(java.util.List<org.opencv.core.Mat>, org.opencv.core.MatOfInt, org.opencv.core.Mat, org.opencv.core.Mat, org.opencv.core.MatOfInt, org.opencv.core.MatOfFloat);
#
# jar -xvf /Applications/Fiji.app/jars/opencv-3.4.2-1.4.2-macosx-x86_64.jar
# [OK]graffy@pr079234:~/toto/jython[19:02:28]>grep -A 5 "calcHist(" org/bytedeco/javacpp/macosx-x86_64/include/opencv2/imgproc.hpp
# CV_EXPORTS void calcHist( const Mat* images, int nimages,
# const int* channels, InputArray mask,
# OutputArray hist, int dims, const int* histSize,
# const float** ranges, bool uniform = true, bool accumulate = false );
# /** @overload
# --
# CV_EXPORTS void calcHist( const Mat* images, int nimages,
# const int* channels, InputArray mask,
# SparseMat& hist, int dims,
# const int* histSize, const float** ranges,
# bool uniform = true, bool accumulate = false );
# --
# CV_EXPORTS_W void calcHist( InputArrayOfArrays images,
# const std::vector<int>& channels,
# InputArray mask, OutputArray hist,
# const std::vector<int>& histSize,
# const std::vector<float>& ranges,
# bool accumulate = false );
# git checkout tags/1.4.2
# [OK]graffy@pr079234:~/toto/javacpp-presets[19:21:57]>git branch
# * (HEAD detached at 1.4.2)
# [OK]graffy@pr079234:~/toto/javacpp-presets[19:18:33]>grep -A 2 "void cvCalcHist" ./opencv/src/main/java/org/bytedeco/javacpp/opencv_imgproc.java
# public static native void cvCalcHist( @Cast("IplImage**") PointerPointer image, CvHistogram hist,
# int accumulate/*=0*/,
# @Const CvArr mask/*=NULL*/ );
# --
# public static native void cvCalcHist( @ByPtrPtr IplImage image, CvHistogram hist );
# public static native void cvCalcHist( @ByPtrPtr IplImage image, CvHistogram hist,
# int accumulate/*=0*/,
# @Const CvArr mask/*=NULL*/ );
# [OK]graffy@pr079234:~/toto/javacpp-presets[19:18:41]>grep -A 2 "void cvCalcHist" ./opencv/src/main/java/org/bytedeco/javacpp/helper/opencv_imgproc.java
# public static void cvCalcHist(IplImage[] arr, CvHistogram hist, int accumulate/*=0*/, CvArr mask/*=null*/) {
# org.bytedeco.javacpp.opencv_imgproc.cvCalcHist(new IplImageArray(arr), hist, accumulate, mask);
# }
# --
# public static void cvCalcHist(IplImageArray arr, CvHistogram hist,
# int accumulate/*=0*/, CvArr mask/*=null*/) {
# org.bytedeco.javacpp.opencv_imgproc.cvCalcArrHist(arr, hist, accumulate, mask);
def get_image_median_value(src_image):
return 0
def test_get_image_median_value():
image_file_path = '/Users/graffy/ownCloud/ipr/lipase/raw-images/res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0/img_000000000_DM300_327-353_fluo_000.tif'
image = imread(image_file_path)
median_value = get_image_median_value(image)
print('median value : %d' % median_value)
# double median( cv::Mat channel )
# {
# double m = (channel.rows*channel.cols) / 2;
# int bin = 0;
# double med = -1.0;
# int histSize = 256;
# float range[] = { 0, 256 };
# const float* histRange = { range };
# bool uniform = true;
# bool accumulate = false;
# cv::Mat hist;
# cv::calcHist( &channel, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate );
# for ( int i = 0; i < histSize && med < 0.0; ++i )
# {
# bin += cvRound( hist.at< float >( i ) );
# if ( bin > m && med < 0.0 )
# med = i;
# }
# return med;
# }
def find_depth_index(src_image, white_sequence):
''' finds in the image sequence white_sequence the image that correlates the best to src_image
:param cv_core.Mat src_image:
:param Sequence white_sequence:
'''
for z_index in range(white_sequence.num_slices):
white_image_file_path = white_sequence.get_image_file_path(channel_index=0, frame_index=0, z_index=z_index)
# white_image = IJ.openImage(white_image_file_path)
# imp2mat = ImagePlusMatConverter()
# white_mat = imp2mat.toMat(white_image.getProcessor())
white_mat = imread(white_image_file_path)
print white_mat
break
def process_sequence(sequence):
'''
:param Sequence sequence:
'''
white_seq = sequence.get_white()
src_image_file_path = sequence.get_image_file_path(channel_index=0, frame_index=0, z_index=0)
src_image = imread(src_image_file_path)
# src_image = IJ.openImage(src_image_file_path)
find_depth_index(src_image, white_seq)
def run_script():
raw_images_root = '/Users/graffy/ownCloud/ipr/lipase/raw-images'
catalog = ImageCatalog(raw_images_root)
print(catalog)
#catalog.sequences['GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0'].open_in_imagej()
catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2'].open_in_imagej()
#catalog.sequences['DARK_40X_60min_1 im pae min_1/Pos0'].open_in_imagej()
#catalog.sequences['white_24112018_1/Pos0'].open_in_imagej()
# catalog.sequences['GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0'].open_in_imagej()
# catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2'].open_in_imagej()
# catalog.sequences['DARK_40X_60min_1 im pae min_1/Pos0'].open_in_imagej()
# catalog.sequences['white_24112018_1/Pos0'].open_in_imagej()
process_sequence(catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2'])
if False:
sequence = catalog.sequences['GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0']
src_image_file_path = sequence.get_image_file_path(channel_index=sequence.get_channel_index('DM300_327-353_fluo'), frame_index=3)
src_image = IJ.openImage(src_image_file_path)
src_image = IJ.openImage(src_image_file_path) # pylint: disable=unused-variable
# dark_image = IJ.openImage(raw_images_root + '/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0/img_000000000_DM300_327-353_fluo_000.tif')
# src_image.show()
# assert src_image
#dark_image = IJ.openImage(raw_images_root + '/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0/img_000000000_DM300_327-353_fluo_000.tif')
#src_image.show()
#assert src_image
# If a Jython script is run, the variable __name__ contains the string '__main__'.
# If a script is loaded as module, __name__ has a different value.
if __name__ in ['__builtin__','__main__']:
run_script()
if __name__ in ['__builtin__', '__main__']:
# run_script()
# test_get_image_median_value()
test_opencv()