Search code examples
pythonpython-3.xscikit-image

scikit-image threshold_multiotsu outputting black frames from video


I'm currently working on a project that takes in a video file, reads individual frames as grayscale, normalizes them, thresholds them, and then outputs them as individual .jpg files. Below I have two functions, frameCapture() and frameCaptureMulti(). The former uses cv2.threshold and cv2.THRESH_OTSU and works as intended. The latter uses threshold_multiotsu() from skimage.filters and outputs completely black frames.

import cv2
import numpy as np
from skimage.filters import threshold_multiotsu
def frameCapture(fc_path):
    fc_vidObj = cv2.VideoCapture(fc_path) # Path to video file
    fc_count = 0                          # Used as counter variable
    fc_success = 1                        # Checks if the frames were extracted

    while fc_success:
        # vidObj object calls read
        fc_success, fc_image = fc_vidObj.read()
        fc_image = cv2.cvtColor(fc_image, cv2.COLOR_BGR2GRAY) # Convert the image to grayscale
        fc_image = cv2.GaussianBlur(fc_image,(5,5),0) # Gaussian noise filtering
        (threshold, fc_image) = cv2.threshold(fc_image, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # Normalize & Segment
        cv2.imwrite("frame%d.jpg" % fc_count, fc_image) # Saves the frames
        fc_count += 1
def frameCaptureMulti(fc_path):
    fc_vidObj = cv2.VideoCapture(fc_path) # Path to video file
    fc_count = 0                          # Used as counter variable
    fc_success = 1                        # Checks if the frames were extracted

    while fc_success:
        # vidObj object calls read
        fc_success, fc_image = fc_vidObj.read()
        fc_image = cv2.cvtColor(fc_image, cv2.COLOR_BGR2GRAY) # Convert the image to grayscale
        fc_image = cv2.GaussianBlur(fc_image,(5,5),0) # Gaussian noise filtering
        fc_thresholds = threshold_multiotsu(fc_image)
        fc_regions = np.digitize(fc_image, bins=fc_thresholds)
        cv2.imwrite("frame%d.jpg" % fc_count, fc_regions) # Saves the frames
        fc_count += 1

The driver code simply runs frameCaptureMulti() on a .mp4 file on my computer.

Here's what the frames look like:
frameCapture() output frameCapture() output

frameCaptureMulti() output: frameCaptureMulti() output

I am not sure why frameCaptureMulti() produces pure black frames. I only have a few weeks experience with Python (outside of learning about ten years ago) and even less with these particular libraries, so any help as to why my code isn't producing expected output is welcome.

Pages referenced for portions of code in frameCapture and frameCaptureMulti:
https://scikit-image.org/docs/dev/auto_examples/segmentation/plot_multiotsu.html
https://www.geeksforgeeks.org/python-program-extract-frames-using-opencv/


Solution

  • I think what's happening is that CV2 gives you a binary image that is correctly saved as a frame with 0 under the threshold and 255 (white) above it. Meanwhile, threshold_multiotsu and np.digitize return an image with values 0, 1, 2, all of which look black in the 0-255 range supported by jpeg. You could use skimage.exposure.rescale_intensity to map those values to e.g. 0, 127, 255.