Hello everyone! I’m in the process of trying to get something running through Dask and PIMS. We’re adapting code from a different lab (check out the published repo here) and working on getting it to run on videos. It was originally written to be run on a bunch of individual images.
I was told recently that Dask already uses PIMS for reading in videos which is very nice! However, I’m running into a couple problems that I haven’t been able to solve.
One of the first things I’ve tried to do is make a class that I can operate upon and later write out different parameters from the analysis to a configuration file. I called it Shoat because it’s apparently the name for a baby Hog and the purpose of this repository is to perform HOG calculations on cropped images. I thought that was funny. I’m not particularly good at making classes, so if you have any advice I’m happy to learn and take criticism!
Here’s what that whole thing looks like:
class Shoat:
def __init__(self, video_path, pixels_per_cell, cells_per_block,
orientations, transform_sqrt, nframes=60):
self.video_path = video_path
self.pixels_per_cell = pixels_per_cell
self.cells_per_block = cells_per_block
self.transform_sqrt = transform_sqrt
self.orientations = orientations
self.first_frame = None
self.low_ycrop = None
self.high_ycrop = None
self.low_xcrop = None
self.high_xcrop = None
self.shape = None
self.dtype = None
self.nframes = nframes
@property
def get_cropped_coordinates(self):
crop_coordinates = cv2.selectROI("ROI Selection", first_frame)
cv2.destroyAllWindows()
self.low_ycrop = crop_coordinates[1]
self.low_xcrop = crop_coordinates[0]
self.high_ycrop = crop_coordinates[3]
self.high_xcrop = crop_coordinates[2]
@property
def get_video_metadata(self):
with pims.ImageIOReader(self.video_path) as imgs:
self.shape = (len(imgs),) + imgs.frame_shape
self.dtype = np.dtype(imgs.pixel_type)
self.first_frame = imgs.get_frame(0)
def create_hogs(self):
ar = dask_image.imread.imread(self.video_path, nframes=self.nframes)
return ar
You’ll notice that Shoat
doesn’t currently perform any HOG calculations or cropping of images. I’m running into a couple problems.
First, I’d like for my Dask reader to use ImageIO which seems like a nice package because it’s running ffmpeg under the hood. Since we have the framerate used to record the video, I feel like using ImageIO for reading data into a Dask Array would be a good choice. I’m still pretty stuck getting this working.
The first thing is that my videos are currently being encoded with color channels despite my camera only being capable of monochrome recordings. I’m not sure how to make scikit-image
specify that I just want to encode my video as a series of grayscale images yet. Since the RGB channel doesn’t have any additional data, I thought the first thing that would be good to do would be to simply read the video with the as_grey
method like this inside my create_hogs
function:
def create_hogs(self):
sfname = str(self.video_path)
grayscale_video = pims.as_grey(pims.ImageIOReader(sfname))
ar = dask_image.imread.imread(grayscale_video, self.nframes)
return ar
Unfortunately when I do this, I run into this error that I’m stuck at:
Input In [236], in Shoat.create_hogs(self)
42 strfname = str(self.video_path)
44 grayscale_video = pims.as_grey(pims.ImageIOReader(strfname))
---> 46 ar = dask_image.imread.imread(grayscale_video, self.nframes)
48 ar.shape
51 return ar
File ~/miniconda3/envs/facial_expression/lib/python3.8/site-packages/dask_image/imread/__init__.py:47, in imread(fname, nframes, arraytype)
44 import cupy
45 arrayfunc = cupy.asanyarray
---> 47 with pims.open(sfname) as imgs:
48 shape = (len(imgs),) + imgs.frame_shape
49 dtype = np.dtype(imgs.pixel_type)
File ~/miniconda3/envs/facial_expression/lib/python3.8/site-packages/pims/api.py:186, in open(sequence, **kwargs)
183 eligible_handlers = set(h for h in all_handlers
184 if ext and ext in map(_drop_dot, h.class_exts()))
185 if len(eligible_handlers) < 1:
--> 186 raise UnknownFormatError(
187 "Could not autodetect how to load a file of type {0}. "
188 "Try manually "
189 "specifying a loader class, e.g. Video({1})".format(ext, sequence))
191 def sort_on_priority(handlers):
192 # This uses optional priority information from subclasses
193 # > 10 means that it will be used instead of than built-in subclasses
194 def priority(cls):
UnknownFormatError: Could not autodetect how to load a file of type original repr:
<framessequencend>
axes: 3
axis 'x' size: 1280
axis 'y' size: 1024
axis 't' size: 45270
pixel datatype: uint8. Try manually specifying a loader class, e.g. Video((ImageIOReader,) processed through proc_func. Original repr:
<FramesSequenceND>
Axes: 3
Axis 'x' size: 1280
Axis 'y' size: 1024
Axis 't' size: 45270
Pixel Datatype: uint8)
I’ve tried looking up how to solve these two errors but have come up short. For reference, my video is in the .avi
format.
For the first exception it finds (UnknownFormatError: Could not autodetect how to load a file of type original repr:
), I’m pretty confused because when I load it like this:
video = pims.as_grey(pims.ImageIOReader(self.image_path)
The video loads properly! Also when it loads the video in the get_video_metadata
function, the package has no problem.
For the second exception, I’ve been looking over the docs and I can’t seem to find how do that manual specification in the documentation.
Once I can get things into a Dask array of grayscale images, I need to do 2 things:
- Crop each image
- Perform a HOG calculation on each image
I think this would be best done with the dask.delayed
functionality, but I’ve never done that before so I’m not sure how to do it. The original code uses joblib
and Parallel
. I got that to work on the pims image reader, but it only did it one frame at a time. I’m pretty sure that Dask will do it in a way that’s a bit more controlled and probably faster. The coolest part will be that I can save both the HOG arrays and also their outputs as images to their own zarr arrays maybe and overlay them upon my original videos as one time series!
Any advice from Dask world?