Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warped perspective feature, splitting the segmentation into segmenter and extractor (Issue #60) #76

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions colour_checker_detection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,18 @@
SETTINGS_SEGMENTATION_COLORCHECKER_CLASSIC,
SETTINGS_SEGMENTATION_COLORCHECKER_NANO,
SETTINGS_SEGMENTATION_COLORCHECKER_SG,
Template,
detect_colour_checkers_inference,
detect_colour_checkers_segmentation,
extractor_default,
extractor_warped,
inferencer_default,
plot_colours,
plot_colours_warped,
plot_contours,
plot_swatches_and_clusters,
segmenter_default,
segmenter_warped,
)

__author__ = "Colour Developers"
Expand All @@ -48,6 +56,14 @@
"detect_colour_checkers_segmentation",
"inferencer_default",
"segmenter_default",
"segmenter_warped",
"extractor_default",
"extractor_warped",
"Template",
"plot_contours",
"plot_swatches_and_clusters",
"plot_colours",
"plot_colours_warped",
]

ROOT_RESOURCES: str = os.path.join(os.path.dirname(__file__), "resources")
Expand All @@ -58,6 +74,9 @@
ROOT_RESOURCES, "colour-checker-detection-tests-datasets"
)

ROOT_DETECTION: str = os.path.join(os.path.dirname(__file__), "detection")
ROOT_DETECTION_TEMPLATES: str = os.path.join(ROOT_DETECTION, "templates")

__application_name__ = "Colour - Checker Detection"

__major_version__ = "0"
Expand Down
52 changes: 52 additions & 0 deletions colour_checker_detection/detection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
scale_contour,
approximate_contour,
quadrilateralise_contours,
largest_convex_quadrilateral,
is_convex_quadrilateral,
remove_stacked_contours,
DataDetectionColourChecker,
sample_colour_checker,
Expand All @@ -31,10 +33,34 @@
SETTINGS_SEGMENTATION_COLORCHECKER_CLASSIC,
SETTINGS_SEGMENTATION_COLORCHECKER_SG,
SETTINGS_SEGMENTATION_COLORCHECKER_NANO,
filter_contours,
filter_contours_multifeature,
cluster_swatches,
filter_clusters,
filter_clusters_by_swatches,
group_swatches,
order_centroids,
determine_best_transformation,
extract_colours,
correct_flipped,
check_residuals,
plot_contours,
plot_swatches_and_clusters,
plot_colours,
plot_colours_warped,
segmenter_default,
segmenter_warped,
extractor_default,
extractor_warped,
detect_colour_checkers_segmentation,
)

from .templates import (
Template,
are_three_collinear,
generate_template,
)

__all__ = [
"DTYPE_INT_DEFAULT",
"DTYPE_FLOAT_DEFAULT",
Expand All @@ -53,6 +79,8 @@
"scale_contour",
"approximate_contour",
"quadrilateralise_contours",
"largest_convex_quadrilateral",
"is_convex_quadrilateral",
"remove_stacked_contours",
"DataDetectionColourChecker",
"sample_colour_checker",
Expand All @@ -61,7 +89,25 @@
"SETTINGS_SEGMENTATION_COLORCHECKER_CLASSIC",
"SETTINGS_SEGMENTATION_COLORCHECKER_SG",
"SETTINGS_SEGMENTATION_COLORCHECKER_NANO",
"filter_contours",
"filter_contours_multifeature",
"cluster_swatches",
"filter_clusters",
"filter_clusters_by_swatches",
"group_swatches",
"order_centroids",
"determine_best_transformation",
"extract_colours",
"correct_flipped",
"check_residuals",
"plot_contours",
"plot_swatches_and_clusters",
"plot_colours",
"plot_colours_warped",
"segmenter_default",
"segmenter_warped",
"extractor_default",
"extractor_warped",
"extract_colour_checkers_segmentation",
"detect_colour_checkers_segmentation",
]
Expand All @@ -71,3 +117,9 @@
"inferencer_default",
"detect_colour_checkers_inference",
]

__all__ += [
"Template",
"are_three_collinear",
"generate_template",
]
110 changes: 93 additions & 17 deletions colour_checker_detection/detection/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@
"scale_contour",
"approximate_contour",
"quadrilateralise_contours",
"largest_convex_quadrilateral",
"is_convex_quadrilateral",
"remove_stacked_contours",
"DataDetectionColourChecker",
"sample_colour_checker",
Expand All @@ -90,7 +92,6 @@
DTYPE_FLOAT_DEFAULT: Type[DTypeFloat] = np.float32
"""Default floating point number dtype."""


_COLOURCHECKER = CCS_COLOURCHECKERS["ColorChecker24 - After November 2014"]
_COLOURCHECKER_VALUES = XYZ_to_RGB(
xyY_to_XYZ(list(_COLOURCHECKER.data.values())),
Expand Down Expand Up @@ -880,9 +881,81 @@ def quadrilateralise_contours(contours: ArrayLike) -> Tuple[NDArrayInt, ...]:
)


def largest_convex_quadrilateral(contour: np.ndarray) -> Tuple[NDArrayInt, bool]:
"""
Return the largest convex quadrilateral contained in the given contour.

Parameters
----------
contour
Contour to process.

Returns
-------
:class:`tuple`
(contour of the largest convex quadrilateral, convexity)

Example:
>>> contour = np.array(
... [[0, 0], [0, 1], [1, 1], [1, 0], [0.5, 0.5]], dtype=np.float32
... )
>>> largest_convex_quadrilateral(contour)
(array([[ 0., 0.],
[ 0., 1.],
[ 1., 1.],
[ 1., 0.]], dtype=float32), True)
"""
while len(contour) > 4:
areas = {
i: cv2.contourArea(np.delete(contour, i, axis=0))
for i in range(len(contour))
}
areas = dict(sorted(areas.items(), key=lambda item: item[1]))

# delete pt, which, if excluded leaves the largest area
contour = np.delete(contour, list(areas.keys())[-1], axis=0)

return contour, cv2.isContourConvex(contour)


def is_convex_quadrilateral(contour: np.ndarray, tolerance: float = 0.1) -> bool:
"""
Return True if the given contour is a convex quadrilateral.

Parameters
----------
contour
Contour to process.
tolerance
Tolerance for the ratio of the areas between the trimmed contour
and the original contour.

Returns
-------
:class:`bool`
True if the given contour is a convex quadrilateral.

Example:
>>> contour = np.array(
... [[0, 0], [0, 1], [1, 1], [1, 0], [0.5, 0.5]], dtype=np.float32
... )
>>> is_convex_quadrilateral(contour)
False
"""
if len(contour) >= 4:
original_area = cv2.contourArea(contour)
convex_contour, convexity = largest_convex_quadrilateral(contour)
if convexity:
convex_area = cv2.contourArea(convex_contour)
ratio = convex_area / original_area
return np.abs(ratio - 1) < tolerance

return False


def remove_stacked_contours(
contours: ArrayLike, keep_smallest: bool = True
) -> Tuple[NDArrayInt, ...]:
) -> NDArrayInt:
"""
Remove amd filter out the stacked contours from given contours keeping
either the smallest or the largest ones.
Expand Down Expand Up @@ -912,16 +985,16 @@ def remove_stacked_contours(
... [[0, 0], [10, 0], [10, 10], [0, 10]],
... ]
... )
>>> remove_stacked_contours(contours) # doctest: +ELLIPSIS
(array([[0, 0],
[7, 0],
[7, 7],
[0, 7]]...)
>>> remove_stacked_contours(contours, False) # doctest: +ELLIPSIS
(array([[ 0, 0],
[10, 0],
[10, 10],
[ 0, 10]]...)
>>> remove_stacked_contours(contours)
array([[[0, 0],
[7, 0],
[7, 7],
[0, 7]]], dtype=int32)
>>> remove_stacked_contours(contours, False)
array([[[ 0, 0],
[10, 0],
[10, 10],
[ 0, 10]]], dtype=int32)
"""

contours = as_int32_array(contours)
Expand Down Expand Up @@ -966,8 +1039,8 @@ def remove_stacked_contours(

filtered_contours[index] = contour

return tuple(
as_int32_array(filtered_contour) for filtered_contour in filtered_contours
return as_int32_array(
[as_int32_array(filtered_contour) for filtered_contour in filtered_contours]
)


Expand All @@ -991,8 +1064,8 @@ class DataDetectionColourChecker(MixinDataclassIterable):

swatch_colours: NDArrayFloat
swatch_masks: NDArrayInt
colour_checker: NDArrayFloat
quadrilateral: NDArrayFloat
colour_checker: NDArrayInt
quadrilateral: NDArrayInt


def sample_colour_checker(
Expand Down Expand Up @@ -1158,5 +1231,8 @@ def sample_colour_checker(
colour_checker = cast(NDArrayFloat, colour_checker)

return DataDetectionColourChecker(
sampled_colours, masks, colour_checker, quadrilateral
sampled_colours,
masks,
as_int32_array(colour_checker),
as_int32_array(quadrilateral),
)
Loading
Loading