This notebook showcases machine learning inference retated colour checker detection examples.
The inference is performed by Ultralytics YOLOv8
The model is published on HuggingFace, it was trained on a purposely constructed dataset.
dcraw -T -g 2.4 12.92 *.CR2
and then resized and converted to *.png with Image Magick as follows: mogrify -resize 50% -format png *.tiff
.
import glob
import numpy as np
import os
import colour
from colour_checker_detection import (
ROOT_RESOURCES_EXAMPLES,
detect_colour_checkers_inference)
colour.plotting.colour_style()
colour.utilities.describe_environment();
=============================================================================== * * * Interpreter : * * python : 3.11.6 (main, Oct 2 2023, 13:45:54) [Clang 15.0.0 * * (clang-1500.0.40.1)] * * * * colour-science.org : * * colour : 0.4.4 * * colour-checker-detection : v0.1.2-205-g0afde4d * * * * Runtime : * * imageio : 2.33.1 * * matplotlib : 3.8.2 * * networkx : 3.2.1 * * numpy : 1.26.3 * * pandas : 2.1.4 * * scipy : 1.11.4 * * tqdm : 4.66.1 * * opencv : 4.9.0 * * * ===============================================================================
COLOUR_CHECKER_IMAGE_PATHS = glob.glob(
os.path.join(ROOT_RESOURCES_EXAMPLES, 'detection', '*25*.png'))
COLOUR_CHECKER_IMAGES = [
colour.cctf_decoding(colour.io.read_image(path))
for path in COLOUR_CHECKER_IMAGE_PATHS
]
for image in COLOUR_CHECKER_IMAGES:
colour.plotting.plot_image(colour.cctf_encoding(image));
/Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028
SWATCHES = []
for image in COLOUR_CHECKER_IMAGES:
for colour_checker_data in detect_colour_checkers_inference(
image, additional_data=True):
swatch_colours, swatch_masks, colour_checker_image = (
colour_checker_data.values)
SWATCHES.append(swatch_colours)
# Using the additional data to plot the colour checker and masks.
masks_i = np.zeros(colour_checker_image.shape)
for i, mask in enumerate(swatch_masks):
masks_i[mask[0]:mask[1], mask[2]:mask[3], ...] = 1
colour.plotting.plot_image(
colour.cctf_encoding(
np.clip(colour_checker_image + masks_i * 0.25, 0, 1)));
/Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028 /Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028
0: 864x1280 1 ColorCheckerClassic24, 1003.7ms Speed: 24.5ms preprocess, 1003.7ms inference, 2.4ms postprocess per image at shape (1, 3, 864, 1280)
/Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028 /Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028
0: 864x1280 1 ColorCheckerClassic24, 1080.8ms Speed: 24.7ms preprocess, 1080.8ms inference, 2.5ms postprocess per image at shape (1, 3, 864, 1280)
D65 = colour.CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']
REFERENCE_COLOUR_CHECKER = colour.CCS_COLOURCHECKERS[
'ColorChecker24 - After November 2014']
colour_checker_rows = REFERENCE_COLOUR_CHECKER.rows
colour_checker_columns = REFERENCE_COLOUR_CHECKER.columns
# NOTE: The reference swatches values as produced by the "colour.XYZ_to_RGB"
# definition are linear by default.
# See https://github.com/colour-science/colour-checker-detection/discussions/59
# for more information.
REFERENCE_SWATCHES = colour.XYZ_to_RGB(
colour.xyY_to_XYZ(list(REFERENCE_COLOUR_CHECKER.data.values())),
'sRGB', REFERENCE_COLOUR_CHECKER.illuminant)
for i, swatches in enumerate(SWATCHES):
swatches_xyY = colour.XYZ_to_xyY(colour.RGB_to_XYZ(
swatches, 'sRGB', D65))
colour_checker = colour.characterisation.ColourChecker(
os.path.basename(COLOUR_CHECKER_IMAGE_PATHS[i]),
dict(zip(REFERENCE_COLOUR_CHECKER.data.keys(), swatches_xyY)),
D65, colour_checker_rows, colour_checker_columns)
colour.plotting.plot_multi_colour_checkers(
[REFERENCE_COLOUR_CHECKER, colour_checker])
swatches_f = colour.colour_correction(swatches, swatches, REFERENCE_SWATCHES)
swatches_f_xyY = colour.XYZ_to_xyY(colour.RGB_to_XYZ(
swatches_f, 'sRGB', D65))
colour_checker = colour.characterisation.ColourChecker(
'{0} - CC'.format(os.path.basename(COLOUR_CHECKER_IMAGE_PATHS[i])),
dict(zip(REFERENCE_COLOUR_CHECKER.data.keys(), swatches_f_xyY)),
D65, colour_checker_rows, colour_checker_columns)
colour.plotting.plot_multi_colour_checkers(
[REFERENCE_COLOUR_CHECKER, colour_checker])
colour.plotting.plot_image(colour.cctf_encoding(
colour.colour_correction(
COLOUR_CHECKER_IMAGES[i], swatches, REFERENCE_SWATCHES)));
for image in COLOUR_CHECKER_IMAGES:
for colour_checker_data in detect_colour_checkers_inference(
image, show=True):
pass
/Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028 /Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028
0: 864x1280 1 ColorCheckerClassic24, 1053.7ms Speed: 24.4ms preprocess, 1053.7ms inference, 2.2ms postprocess per image at shape (1, 3, 864, 1280)
/Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028 /Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028
0: 864x1280 1 ColorCheckerClassic24, 1032.2ms Speed: 25.3ms preprocess, 1032.2ms inference, 2.3ms postprocess per image at shape (1, 3, 864, 1280)
The current inference process is prone to fail if the image is not that of a ColorChecker Classic 24, especially with the ColorChecker Passport:
COLOUR_CHECKER_IMAGE_PATH = next(
iter(glob.glob(os.path.join(ROOT_RESOURCES_EXAMPLES, "detection", "*197*.png")))
)
for colour_checker_data in detect_colour_checkers_inference(
COLOUR_CHECKER_IMAGE_PATH, apply_cctf_decoding=True, show=True
):
pass
/Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028
0: 864x1280 3 ColorCheckerClassic24s, 1042.6ms Speed: 25.0ms preprocess, 1042.6ms inference, 4.4ms postprocess per image at shape (1, 3, 864, 1280)
/Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028
In this specific case, it is possible to transform the image post-detection and horizontally compress it to have better swatch masks placement:
for colour_checker_data in detect_colour_checkers_inference(
COLOUR_CHECKER_IMAGE_PATH,
apply_cctf_decoding=True,
inferred_confidence=0.875,
transform={"scale": (1.15, 1)},
show=True,
):
pass
/Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028
0: 864x1280 3 ColorCheckerClassic24s, 1128.9ms Speed: 25.6ms preprocess, 1128.9ms inference, 4.6ms postprocess per image at shape (1, 3, 864, 1280)
/Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028
import time
from ultralytics import YOLO
def inferencer_agpl(image, **kwargs):
model = YOLO(
os.path.join(
os.path.expanduser("~"),
".colour-science",
"colour-checker-detection",
"colour-checker-detection-l-seg.pt",
),
)
data = []
# NOTE: YOLOv8 expects "BGR" arrays.
if isinstance(image, np.ndarray):
image = image[..., ::-1]
image = image.astype(np.float32)
# `device=0` for CUDA GPU
for result in model(image, device="mps"):
if result.boxes is None:
continue
if result.masks is None:
continue
data_boxes = result.boxes.data
data_masks = result.masks.data
for i in range(data_boxes.shape[0]):
data.append(
(
data_boxes[i, 4].cpu().numpy(),
data_boxes[i, 5].cpu().numpy(),
data_masks[i].data.cpu().numpy(),
)
)
return data
print("Default Inferencer (Script via Sub-Process)")
for image in COLOUR_CHECKER_IMAGES:
start = time.perf_counter()
for colour_checker_data in detect_colour_checkers_inference(image):
pass
print(f"Total Inference Time: {time.perf_counter() - start:.3f} seconds")
print("\n")
print("Custom Inferencer")
for image in COLOUR_CHECKER_IMAGES:
start = time.perf_counter()
for colour_checker_data in detect_colour_checkers_inference(
image, inferencer=inferencer_agpl
):
pass
print(f"Total Inference Time: {time.perf_counter() - start:.3f} seconds")
Default Inferencer (Script via Sub-Process)
/Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028 /Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028
0: 864x1280 1 ColorCheckerClassic24, 1126.4ms Speed: 26.3ms preprocess, 1126.4ms inference, 2.6ms postprocess per image at shape (1, 3, 864, 1280) Total Inference Time: 7.238 seconds
/Users/kelsolaar/Library/Caches/pypoetry/virtualenvs/colour-checker-detection-4JNYxXhS-py3.11/lib/python3.11/site-packages/colour/utilities/verbose.py:265: ColourUsageWarning: "OpenImageIO" related API features are not available, switching to "Imageio"! warn(*args, **kwargs) # noqa: B028
0: 864x1280 1 ColorCheckerClassic24, 1001.5ms Speed: 24.3ms preprocess, 1001.5ms inference, 2.4ms postprocess per image at shape (1, 3, 864, 1280) Total Inference Time: 7.604 seconds Custom Inferencer 0: 864x1280 (no detections), 342.9ms Speed: 16.5ms preprocess, 342.9ms inference, 34.6ms postprocess per image at shape (1, 3, 864, 1280) Total Inference Time: 1.680 seconds 0: 864x1280 (no detections), 112.1ms Speed: 12.3ms preprocess, 112.1ms inference, 1.7ms postprocess per image at shape (1, 3, 864, 1280) Total Inference Time: 1.037 seconds