41 mask: np.ndarray[tuple[int, int], np.bool],
42 xyz_whitepoint: tuple[float, float],
44 dilation_iterations: int = 3,
46 """Heal out-of-gamut regions in a Lab image using diffusion-based inpainting.
50 lab_image : `LABImage`
51 A NxMx3 array in the Lab colorspace. Modified in-place by healing out-of-gamut regions.
52 For each region, a copy is made before modification, then written back.
53 mask : `numpy.ndarray` of `bool`
54 A boolean mask indicating which pixels are out of gamut.
55 xyz_whitepoint : `tuple` of `float`, `float`
56 Sets the white point of the xyz colorspace in xy coordinates.
57 max_size : `int`, optional
58 Maximum size of regions to heal. Larger regions are skipped. Default is 500.
59 dilation_iterations : `int`, optional
60 Number of iterations for mask dilation. Default is 3.
65 The healed image converted to RGB colorspace.
70 Raised if the shapes of lab_image and mask are incompatible.
74 The healing algorithm works by:
76 1. Labeling connected regions in the mask
77 2. For each region smaller than max_size:
79 - Dilating the mask to create an annulus around the region
80 - Computing average a,b color values from the annulus
81 - Using rgb.inpaint_mask to interpolate the L, a, and b channels
82 - Filling the masked region with the interpolated values
84 3. Regions larger than max_size are skipped.
87 labels = label(binary_dilation(mask, iterations=3))[0]
88 places = find_objects(labels)
92 size = int(3 * np.min([sl.stop - sl.start
for sl
in place]))
93 new_y = slice(np.max((0, place[0].start - size)), np.min((mask.shape[0], place[0].stop + size)),
None)
94 new_x = slice(np.max((0, place[1].start - size)), np.min((mask.shape[1], place[1].stop + size)),
None)
95 new_places.append((new_y, new_x))
101 for (place_y, place_x), (old_y, old_x)
in zip(new_places, places):
102 sub_labels = labels[old_y, old_x]
103 if np.sum(sub_labels > 0) >= max_size:
105 label_number = np.max(sub_labels)
107 sub_mask = labels[place_y, place_x] == label_number
108 sub_lab = np.copy(lab_image[place_y, place_x])
109 new_lum = rgb.inpaint_mask(
110 np.ascontiguousarray(sub_lab[..., 0]),
116 new_a = rgb.inpaint_mask(
117 np.ascontiguousarray(sub_lab[..., 1]),
123 new_b = rgb.inpaint_mask(
124 np.ascontiguousarray(sub_lab[..., 2]),
130 sub_lab[..., 0] = new_lum
131 sub_lab[..., 1] = new_a
132 sub_lab[..., 2] = new_b
134 lab_image[place_y, place_x] = sub_lab
136 return rgb.Oklab_to_RGB(np.ascontiguousarray(lab_image), xyz_whitepoint)
RGBImage heal_gamut(LABImage lab_image, np.ndarray[tuple[int, int], np.bool] mask, tuple[float, float] xyz_whitepoint, int max_size=500, int dilation_iterations=3)