Skip to content

Commit

Permalink
Merge pull request #1576 from danforthcenter/quick_cutto
Browse files Browse the repository at this point in the history
Add "Quick Cutto" ROI function
  • Loading branch information
nfahlgren authored Aug 16, 2024
2 parents f0e9d0d + a3939ce commit 248f983
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 6 deletions.
60 changes: 55 additions & 5 deletions plantcv/plantcv/roi/quick_filter.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
"""PlantCV quick_filter module."""
import os
import cv2
import numpy as np
from skimage.measure import label
from skimage.color import label2rgb
from plantcv.plantcv import params
from plantcv.plantcv.roi import roi2mask
from plantcv.plantcv._debug import _debug
from plantcv.plantcv.logical_and import logical_and


def quick_filter(mask, roi):
"""Quickly filter a binary mask using a region of interest.
Inputs
------
mask = binary mask
roi = PlantCV ROI object
Parameters
----------
mask : numpy.ndarray
Binary mask to filter.
roi : plantcv.plantcv.classes.Objects
PlantCV ROI object.
Returns
-------
filtered_mask = Filtered binary mask
numpy.ndarray
Filtered binary mask.
"""
# Increment the device counter
params.device += 1
Expand Down Expand Up @@ -68,3 +75,46 @@ def quick_filter(mask, roi):
_debug(visual=summed, filename=os.path.join(params.debug_outdir, f"{params.device}_roi_filter.png"), cmap="gray")

return summed


def _quick_cutto(mask, roi):
"""Quickly filter a binary mask using a region of interest by cutting to each ROI.
Parameters
----------
mask : numpy.ndarray
Binary mask to filter.
roi : plantcv.plantcv.classes.Objects
PlantCV ROI object.
Returns
-------
numpy.ndarray, numpy.ndarray, int
Filtered binary mask, labeled mask, number of labels.
"""
# Increment the device counter
params.device += 1

# Store debug
debug = params.debug
params.debug = None

mask_copy = np.copy(mask).astype(np.int32)
labeled_mask = np.zeros(mask.shape, dtype=np.int32)
bin_mask = np.copy(labeled_mask)
num_labels = len(roi.contours)
for i in range(num_labels):
# Pixel intensity of (i+1) such that the first object has value
cv2.drawContours(labeled_mask, roi.contours[i], -1, (i+1), -1)
cv2.drawContours(bin_mask, roi.contours[i], -1, (255), -1)
cropped_mask = logical_and(mask_copy, bin_mask)
# Make a labeled mask from the cropped objects
label_mask_where = np.where(cropped_mask == 255, labeled_mask, 0)

# Print/plot debug image
colorful = label2rgb(label_mask_where)
colorful2 = (255*colorful).astype(np.uint8)
params.debug = debug
_debug(visual=colorful2, filename=os.path.join(params.debug_outdir, f"{params.device}_label_colored_mask.png"))

return cropped_mask.astype(np.uint8), label_mask_where, num_labels
18 changes: 17 additions & 1 deletion tests/plantcv/roi/test_quick_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import cv2
import numpy as np
from plantcv.plantcv import Objects
from plantcv.plantcv.roi import quick_filter
from plantcv.plantcv.roi.quick_filter import quick_filter, _quick_cutto


def test_quick_filter(test_data):
Expand All @@ -19,3 +19,19 @@ def test_quick_filter(test_data):
area = cv2.countNonZero(filtered_mask)
# Assert that the contours were filtered as expected
assert area == 221


def test_quick_cutto(test_data):
"""Test for PlantCV."""
# Read in test data
img = cv2.imread(test_data.small_rgb_img)
mask = np.zeros(np.shape(img)[:2], dtype=np.uint8)
cnt, cnt_str = test_data.load_contours(test_data.small_contours_file)
cv2.drawContours(mask, cnt, -1, (255), -1, lineType=8, hierarchy=cnt_str)
roi = [np.array([[[200, 200]], [[200, 190]], [[249, 190]], [[249, 200]]], dtype=np.int32)]
roi_str = np.array([[[-1, -1, -1, -1]]], dtype=np.int32)
roi_obj = Objects(contours=[roi], hierarchy=[roi_str])
filtered_mask, _, _ = _quick_cutto(mask=mask, roi=roi_obj)
area = cv2.countNonZero(filtered_mask)
# Assert that the contours were filtered as expected
assert area == 7

0 comments on commit 248f983

Please sign in to comment.