def ipt_jch(start, amount, saturation, luminosity): # Generate colors k = 360/amount colors = [(luminosity, saturation, start + i*k) for i in range(amount)] # From lch to IPT ans = ((l/100, c/100*cos(radians(h)), c/100*sin(radians(h))) for l, c, h in colors) # From IPT to XYZ1 ans = (convert_color(IPTColor(i, p, t), XYZColor, target_illuminant="d65") for i, p, t in ans) ipt_colors = (color.get_value_tuple() for color in ans) # From JCh to XYZ1 ans = (cspace_convert(color, "JCh", "XYZ1") for color in colors) jch_colors = (color.tolist() for color in ans) # Compute average ans = (((x1 + x2)/2, (y1 + y2)/2 , (z1 + z2)/2) for (x1, y1, z1), (x2, y2, z2) in zip(ipt_colors, jch_colors)) # From XYZ1 to sRGB1 ans = (cspace_convert(color, "XYZ1", "sRGB1") for color in ans) ans = ((color.tolist() for color in ans)) ans = (sRGBColor(*color) for color in ans) return to_hex_rgb(ans)
def get_sRGB1_color_points(self, norm = 1.0, use_deuteranomaly = False ): cp = self.get_color_points(norm) sRGB1_color_points = [] for i in range(3): sRGB1_color_points.append(cspace_convert(cp[i], self.color_space, "sRGB1")) if use_deuteranomaly: for i in range(3): sRGB1_color_points[i] = np.clip(cspace_convert(sRGB1_color_points[i], cvd_space, "sRGB1"),0,1) return sRGB1_color_points
def desaturate(hex_color, n): if n == 1: return hex_color cspace = 'CIELab' dim = 0 jch_color = cspace_convert([hex2color(hex_color)], 'sRGB1', cspace) desats = np.repeat(jch_color, n, axis=0) orig_sat = jch_color[0, dim] desats[:, dim] = orig_sat * np.linspace(0.25, 1, n) return np.clip(cspace_convert(desats, cspace, 'sRGB1'), 0, 1)
def mix(color1, color2): rgb1, rgb2 = hex_to_rgb(color1), hex_to_rgb(color2) x1, y1, z1 = cspace_convert(rgb1, "sRGB255", cie_string).tolist() x2, y2, z2 = cspace_convert(rgb2, "sRGB255", cie_string).tolist() ans = ((x1 + x2)/2, (y1 + y2)/2 , (z1 + z2)/2) ans = cspace_convert(ans, cie_string, "sRGB1").tolist() ans = sRGBColor(*ans) ans = (ans.clamped_rgb_r, ans.clamped_rgb_g, ans.clamped_rgb_b) ans = tuple(round(i*255) for i in ans) return "#%02x%02x%02x" % ans
def transform(ctab, src='sRGB1', dst='CAM02-UCS', inverse=False): """Transform a colortable between color spaces""" if missing: raise ImportError(missing) out = ctab.copy() if not inverse: out[:, :3] = cspace_convert(out[:, :3], src, dst) else: out[:, :3] = cspace_convert(out[:, :3], dst, src) return out
def convert(self, srgb_image): """Slightly more complicated convert""" srgb_image = self._validate_input_image(srgb_image) grayscale = cspace_convert(srgb_image, 'sRGB1', 'JCh') if self.severity == 100: grayscale[..., 1] = 0 else: grayscale[..., 1] *= self.severity / 100 converted = cspace_convert(grayscale, 'JCh', self.dest_space_name) return np.clip(converted, 0, 1)
def set_convertor(name, ill='D65'): """ Binds the conversion functions LCH2RGB() and RGB2LCH() to the choosen colour package """ global LCH2RGB, RGB2LCH, convertor, illuminant if name not in ['custom', 'colorspacious', 'colourscience']: print("Unknown conversion module") return convertor = name illuminant = ill if name == 'custom': LCH2RGB = lambda L, C, H: XYZ2RGB(Lab2XYZ(LCH2Lab((L, C, H)))) RGB2LCH = lambda R, G, B: Lab2LCH(XYZ2Lab(RGB2XYZ((R, G, B)))) if name == 'colorspacious': from colorspacious import cspace_convert func_LCH2RGB = lambda L, C, H: cspace_convert([L, C, H], { "name": "CIELCh", "XYZ100_w": ill }, "sRGB1") func_RGB2LCH = lambda R, G, B: cspace_convert([R, G, B], "sRGB1", { "name": "CIELCh", "XYZ100_w": ill }) if name == 'colourscience': import colour as cs cs_ill = cs.ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ill] func_LCH2RGB = lambda L, C, H: cs.XYZ_to_sRGB( cs.Lab_to_XYZ(cs.LCHab_to_Lab([L, C, H]), illuminant=cs_ill)) func_RGB2LCH = lambda R, G, B: cs.Lab_to_LCHab( cs.XYZ_to_Lab(cs.sRGB_to_XYZ([R, G, B]), illuminant=cs_ill)) if name == 'colorspacious' or name == 'colourscience': def LCH2RGB(L, C, H): if hasattr(L, '__iter__'): RGB = np.array(list(map(func_LCH2RGB, L, C, H))) R = RGB[:, 0] G = RGB[:, 1] B = RGB[:, 2] else: R, G, B = func_LCH2RGB(L, C, H) return R, G, B def RGB2LCH(R, G, B): if hasattr(R, '__iter__'): LCH = np.array(list(map(func_RGB2LCH, R, G, B))) L = LCH[:, 0] C = LCH[:, 1] H = LCH[:, 2] else: L, C, H = func_RGB2LCH(R, G, B) return L, C, H print("convertor = '%s' (illuminant = '%s')" % (name, illuminant))
def colordiff_1d(image, colorspace, channel=0, input_space='sRGB1', uniform_space='CAM02-UCS'): """ Only compute distance along first axis of uniform space """ uniform1 = cspace_convert(image, input_space, uniform_space) uniform2 = cspace_convert(colorspace.convert(image), input_space, uniform_space) #return np.min(np.abs(uniform1 - uniform2), axis=-1) return np.abs(uniform1[..., channel] - uniform2[..., channel])
def decomposeImage(original_image, image_CIELab, lightness, lambdas, alphas, r, display=True): Us = [] Us_RGB = [] (m, n) = lightness.shape for lambda_, alpha in zip(lambdas, alphas): print("WLS with parameters : alpha = {}, lambda = {}".format( alpha, lambda_)) print("Building system...") A = buildA(original_image, lambda_, r, alpha, alpha) print("Solving system...") U = (sp.linalg.spsolve(A, lightness.flatten())).reshape((m, n)) Us.append(U) if (display): U_RGB = cs.cspace_convert( rescaleLightness(changeLightness(image_CIELab, U)), "CIELab", "sRGB1") Us_RGB.append(U_RGB) if (display): print("Multi-scale edge-preserving smoothing results :") comparePlotList(original_image, Us_RGB, ["lambda = " + str(l) for l in lambdas]) details = [] details_RGB = [] prev = lightness for U in Us: D = prev - U details.append(D) prev = U if (display): D_RGB = cs.cspace_convert( rescaleLightness(changeLightness(image_CIELab, D)), "CIELab", "sRGB1") details_RGB.append(D_RGB) base = Us[len(Us) - 1] if (display): print("Multi-scale detail extraction results :") comparePlotList(original_image, details_RGB, ["lambda = " + str(l) for l in lambdas]) return base, details
def eval_cost(points): norm_ucs = colorspacious.cspace_convert(points, norm_space, 'CAM02-UCS') deut_ucs = colorspacious.cspace_convert(points, deut_space, 'CAM02-UCS') prot_ucs = colorspacious.cspace_convert(points, prot_space, 'CAM02-UCS') trit_ucs = colorspacious.cspace_convert(points, trit_space, 'CAM02-UCS') dist = np.concatenate([ spd.pdist(norm_ucs) - norm_min_dist, spd.pdist(deut_ucs) - deut_min_dist, spd.pdist(prot_ucs) - prot_min_dist, spd.pdist(trit_ucs) - trit_min_dist, ]) cost_collision = np.minimum(0, dist)**2 cost_contrast = -np.log(1 + np.maximum(0, dist)) return np.sum(cost_collision + cost_contrast)
def get_avg_color(image): """Takes an image, converts it to CIECAM02 UCS color space, grabs the center pixels, averages the color. We move to UCS because it's a perceptually uniform space, in which we can average. Then we move to CIECAM02 JCh space to return the hue angle. Inputs: image: non-normalized (i.e. RGB 0-1 floats) numpy array of shape(224,224,3)""" perceptually_uniform = cspace_convert(image, "sRGB1", "CAM02-UCS") avg_center = np.mean(np.mean(perceptually_uniform, axis=0), axis=0) avg_center_JCh = cspace_convert(avg_center, "CAM02-UCS", "JCh") return avg_center_JCh
def generate_color_table(): """ Generate a lookup table with all possible RGB colors, encoded in perceptually uniform CAM02-UCS color space. Table rows correspond to individual RGB colors, columns correspond to J', a', and b' components. The table is stored as a NumPy array. """ widgets = ["Generating color table: ", Percentage(), " ", Bar(), " ", ETA()] pbar = ProgressBar(widgets=widgets, maxval=(MAX * MAX)).start() i = 0 colors = np.empty(shape=(NUM_COLORS, 3), dtype=float) for r in range(MAX): for g in range(MAX): d = i * MAX for b in range(MAX): colors[d + b, :] = (r, g, b) colors[d:d + MAX] = cspace_convert( colors[d:d + MAX], "sRGB255", "CAM02-UCS" ) pbar.update(i) i += 1 pbar.finish() return colors
def generate_palette(colors, size, base=None, no_black=False): # Initialize palette with given base or white color if base: palette = [colors[i, :] for i in base] else: palette = [colors[-1, :]] # white # Exclude colors that are close to black if no_black: MIN_DISTANCE_TO_BLACK = 35 d = np.linalg.norm((colors - colors[0, :]), axis=1) colors = colors[d > MIN_DISTANCE_TO_BLACK, :] # Initialize distances array num_colors = colors.shape[0] distances = np.ones(shape=(num_colors, 1)) * 1000 # A function to recompute minimum distances from palette to all colors def update_distances(colors, color): d = np.linalg.norm((colors - color), axis=1) np.minimum(distances, d.reshape(distances.shape), distances) # Build progress bar widgets = ['Generating palette: ', Percentage(), ' ', Bar(), ' ', ETA()] pbar = ProgressBar(widgets=widgets, maxval=size).start() # Update distances for the colors that are already in the palette for i in range(len(palette) - 1): update_distances(colors, palette[i]) pbar.update(i) # Iteratively build palette while len(palette) < size: update_distances(colors, palette[-1]) palette.append(colors[np.argmax(distances), :]) pbar.update(len(palette)) pbar.finish() return cspace_convert(palette, 'CAM02-UCS', 'sRGB1')
def printRgb2Cam02UCS(rgb): jab = cspace_convert(rgb, "sRGB255", "CAM02-UCS") jabStr = ','.join([str(np.round(c, decimals=2)) for c in jab]) rgbStr = 'RGB: [' + ','.join([str(c) for c in rgb]) + ']' while len(rgbStr) < 18: rgbStr = rgbStr + ' ' print rgbStr, '|:::| Jab (UCS): ['+jabStr+']'
def printRgb2XYZ100(rgb): xyz = cspace_convert(rgb, "sRGB255", "XYZ100") xyzStr = ','.join([str(np.round(c, decimals=2)) for c in xyz]) rgbStr = 'RGB: [' + ','.join([str(c) for c in rgb]) + ']' while len(rgbStr) < 18: rgbStr = rgbStr + ' ' print rgbStr, '|:::| XYZ100: ['+xyzStr+']'
def printRgb2Cam02(rgb): jch = cspace_convert(rgb, "sRGB255", "CIECAM02") jchStr = ','.join([str(np.round(c, decimals=2)) for c in jch]) rgbStr = 'RGB: [' + ','.join([str(c) for c in rgb]) + ']' while len(rgbStr) < 18: rgbStr = rgbStr + ' ' print rgbStr, '|:::| JCh: ['+jchStr+']'
def convert(data, from_space, to_space): if from_space == CSPACE1: data = np.clip(data, 0, 1) new = cspace_convert(data.T, from_space, to_space).T if to_space == CSPACE1: new = np.clip(new, 0, 1) return new
def plot_analytic_vortex_test_data(ax, my_map, npts=16, xmax=1.5, ymax=1.75, with_quiver=True, with_deuteranomaly=False): X, Y = np.meshgrid(linspace(0, xmax, xmax * npts), linspace(0, ymax, ymax * npts)) U = np.sin(2 * pi * Y) V = np.cos(2 * pi * X) angle = arctan2(V, U) magnitude = sqrt(V**2 + U**2) magnitude_norm = magnitude / magnitude.max() angle_norm = angle image = my_map(angle_norm, magnitude_norm) if with_deuteranomaly: cvd_space = { "name": "sRGB1+CVD", "cvd_type": "deuteranomaly", "severity": 50 } image = clip(cspace_convert(image, cvd_space, "sRGB1"), 0, 1) ax.imshow(image, origin='lower', extent=(0, xmax, 0, ymax), interpolation='None') if with_quiver: ax.quiver(X, Y, U, V, units='width')
def generate_color_table(): """ Generate a lookup table with all possible RGB colors, encoded in perceptually uniform CAM02-UCS color space. Table rows correspond to individual RGB colors, columns correspond to J', a', and b' components. The table is stored as a NumPy array. """ widgets = [ 'Generating color table: ', Percentage(), ' ', Bar(), ' ', ETA() ] pbar = ProgressBar(widgets=widgets, maxval=(MAX * MAX)).start() i = 0 colors = np.empty(shape=(NUM_COLORS, 3), dtype=float) for r in range(MAX): for g in range(MAX): d = i * MAX for b in range(MAX): colors[d + b, :] = (r, g, b) colors[d:d + MAX] = cspace_convert(colors[d:d + MAX], 'sRGB255', 'CAM02-UCS') pbar.update(i) i += 1 pbar.finish() return colors
def colorblind(colors, type): space = { 'name': 'sRGB1+CVD', 'cvd_type': type, 'severity': 100, } return np.clip(colorspacious.cspace_convert(colors, space, 'sRGB1'), 0, 1)
def color_palette2(n_colors=6, chroma=0.5, hue_offset=0.1, v_scale="linear", max_lum=1.0): h = 360 * hue_offset + np.linspace(0, 360, n_colors, endpoint=False)[::-1] C = [chroma * 100] * n_colors if v_scale == "linear": J = np.linspace(0, max_lum, n_colors) elif v_scale == "log": J = np.log10(np.linspace(1, 10**max_lum, n_colors)) elif v_scale == "sqrt": J = np.sqrt(np.linspace(0, max_lum, n_colors)) else: raise NotImplementedError() J *= 100 J = np.maximum(1e-1, J) # convert to RGB pal = cspace_convert(list(zip(J, C, h)), "JCh", "sRGB1") # clip RGB values to [0, 1] pal = np.maximum(0, pal) pal = np.minimum(1, pal) return pal
def get_colors(n_colors=1, lightness=50, saturation=50, shift=0): """Get colors on the LCh ring Parameters ---------- n_colors : param lightness: (Default value = 1) lightness : (Default value = 50) saturation : (Default value = 50) shift : (Default value = 0) Returns ------- """ hues = np.linspace(0, 360, n_colors + 1)[:-1] + shift return (np.clip( colorspacious.cspace_convert( np.stack( [ np.ones_like(hues) * lightness, np.ones_like(hues) * saturation, hues, ], 1, ), "CIELCh", "sRGB1", ), 0, 1, ) * 255)
def maximize_triangle_radius(self, cut_off = 0.01, radius_min = 0.0, radius_max = 256.0, verbose=False): angles = self.angle_0 + np.array([0,120,240]) a_unit_vec = np.cos(np.pi/180*angles) b_unit_vec = np.sin(np.pi/180*angles) lab_sequence = np.zeros((3,3)) lab_sequence[:,0] = self.L_plane while (radius_max - radius_min) > cut_off: radius = 0.5*(radius_max + radius_min) lab_sequence[:,1] = radius*a_unit_vec + self.center[0] lab_sequence[:,2] = radius*b_unit_vec + self.center[1] sRGB1_sequence = cspace_convert(lab_sequence, self.color_space, "sRGB1") if np.any(sRGB1_sequence>1.0) or np.any(sRGB1_sequence<0.0): radius_is_safe = False if verbose:print (radius_min, radius, radius_max, radius_is_safe) radius_max = radius else: radius_is_safe = True if verbose:print (radius_min, radius, radius_max, radius_is_safe) radius_min = radius if radius_is_safe==False: radius = radius_min self.radius = radius return radius
def hex_colors(start, amount, saturation, luminosity): k = 360/amount ans = ((luminosity, saturation, start + i*k) for i in range(amount)) ans = (cspace_convert(color, cie_string, "sRGB1") for color in ans) ans = (color.tolist() for color in ans) ans = (sRGBColor(*color) for color in ans) return to_hex_rgb(ans)
def convert(data, from_space, to_space): ''' Takes a single color value or matrix of values and converts to the desired colorspace Parameters ----------- data: 3 x COL array Colormap name OR array with complete color data. Invalid colormap names throw a ValueError. Refer to _check_cmap for more information. from_space: str Colorspace the current color value(s) reside(s) in to_space: str Colorspace to convert the color value(s) to Returns ----------- n : 3 x COL ndarray RGB values for each converted color value ''' if from_space == CSPACE1: data = np.clip(data, 0, 1) new = cspace_convert(data.T, from_space, to_space).T if to_space == CSPACE1: new = np.clip(new, 0, 1) return new
def get_orientation_map(image, filts): """Convolves 4 quadrature filters over an image and returns the image orientation. Converts to greyscale first. Inputs: image - (224,224,3) filts - Outputs: angle (between 0 and 1) - remember to rescale later intensity = (between 0 and 1) - 0 represents no angle energy """ perceptually_uniform = cspace_convert(image, "sRGB1", "JCh") lightness_image = perceptually_uniform[:, :, 0] magnitudes = [] for filt in filts: sin_conv = convolve2d(lightness_image, filt[1], mode='same') cos_conv = convolve2d(lightness_image, filt[0], mode='same') magnitudes.append(np.sqrt(sin_conv ** 2 + cos_conv ** 2)) orientation_vec = [magnitudes[0] - magnitudes[2], magnitudes[1] - magnitudes[3]] angle = np.arctan2(magnitudes[0] - magnitudes[2], magnitudes[1] - magnitudes[3]) rescaled_angle = angle / (np.pi * 2) + 0.5 intensity = np.sqrt(orientation_vec[0] ** 2 + orientation_vec[1] ** 2) / \ np.max(np.sqrt(orientation_vec[0] ** 2 + orientation_vec[1] ** 2)) return rescaled_angle, intensity
def generate_palette(self, size): """ Return palette in sRGB1 format. If the palette isn't long enough, new entries are generated. """ if size <= len(self.palette): return cspace_convert(self.palette[0:size], "CAM02-UCS", "sRGB1") # Initialize distances array num_colors = self.colors.shape[0] distances = np.ones(shape=(num_colors, 1)) * 1000 # A function to recompute minimum distances from palette to all colors def update_distances(colors, color): d = np.linalg.norm((colors - color), axis=1) np.minimum(distances, d.reshape(distances.shape), distances) # Build progress bar widgets = [ "Generating palette: ", Percentage(), " ", Bar(), " ", ETA() ] pbar = ProgressBar(widgets=widgets, maxval=size).start() # Update distances for the colors that are already in the palette for i in range(len(self.palette) - 1): update_distances(self.colors, self.palette[i]) pbar.update(i) # Iteratively build palette while len(self.palette) < size: update_distances(self.colors, self.palette[-1]) self.palette.append(self.colors[np.argmax(distances), :]) pbar.update(len(self.palette)) pbar.finish() assert self.check_validity_internal_palette( ), "Internal error during extend_palette: self.palette is poorly formatted." if self.overwrite_base_palette: self.save_palette(palette=self.palette, path=self.base_palette, format="byte", overwrite=True) return cspace_convert(self.palette[0:size], "CAM02-UCS", "sRGB1")
def max_chroma_colormap(z, nancolor='gray'): """ Map complex value to color, with maximum chroma at each lightness Magnitude is represented by lightness and angle is represented by hue. The interval [0, ∞] is mapped to lightness [0, 100]. Parameters ---------- z : array_like Complex numbers to be mapped. nancolor Color used to represent NaNs. Can be any valid matplotlib color, such as ``'k'``, ``'deeppink'``, ``'0.5'`` [gray], ``(1.0, 0.5, 0.0)`` [orange], etc. Returns ------- rgb : ndarray Array of colors, with values varying from 0 to 1. Shape is same as input, but with an extra dimension for R, G, and B. Examples -------- A point with infinite magnitude will map to white, and magnitude 0 will map to black. A point with magnitude 10 and phase of π/2 will map to a saturated yellow. NaNs will map to gray by default: >>> max_chroma_colormap([[np.inf, 0, 10j, np.nan]]) array([[[ 1. , 1. , 1. ], [ 0.051, 0. , 0.001], [ 0.863, 0.694, 0. ], [ 0.502, 0.502, 0.502]]]) """ # Map magnitude in [0, ∞] to J in [0, 100] J = (1.0 - (1 / (1.0 + np.abs(z)**0.3))) * 100 # Map angle in [0, 2π) to hue h in [0, 360) h = np.angle(z, deg=True) # TODO: Don't interpolate NaNs and get warnings # 2D interpolation of C lookup table C = max_interpolator(J, h, grid=False) # So if z is (vertical, horizontal), then # imshow expects shape of (vertical, horizontal, 3) JCh = np.stack((J, C, h), axis=-1) # TODO: Don't convert NaNs and get warnings rgb = cspace_convert(JCh, new_space, "sRGB1") # White for infinity (colorspacious doesn't quite reach it for J = 100) rgb[np.isinf(z)] = (1.0, 1.0, 1.0) # Color NaNs rgb[np.isnan(z)] = to_rgb(nancolor) return rgb.clip(0, 1)
def jmh_mixer_2(*colors): n = len(colors) ans = (hex_to_rgb(color) for color in colors) ans = (cspace_convert(color, "sRGB255", "JMh") for color in ans) ans = (color.tolist() for color in ans) ans = zip(*ans) ans = (sum(items)/n for items in ans) return list(ans)
def colorblind(pal, typ): input_space = { 'name': 'sRGB1+CVD', 'cvd_type': typ, 'severity': 100, } return np.clip(colorspacious.cspace_convert(pal, input_space, 'sRGB1'), 0, 1)
def sRGB_gamut_Jp_slice(Jp, uniform_space, ap_lim=(-50, 50), bp_lim=(-50, 50), resolution=200): bp_grid, ap_grid = np.mgrid[bp_lim[0] : bp_lim[1] : resolution * 1j, ap_lim[0] : ap_lim[1] : resolution * 1j] Jp_grid = Jp * np.ones((resolution, resolution)) Jpapbp = np.concatenate((Jp_grid[:, :, np.newaxis], ap_grid[:, :, np.newaxis], bp_grid[:, :, np.newaxis]), axis=2) sRGB = cspace_convert(Jpapbp, uniform_space, "sRGB1") sRGBA = np.concatenate((sRGB, np.ones(sRGB.shape[:2] + (1,))), axis=2) sRGBA[np.any((sRGB < 0) | (sRGB > 1), axis=-1)] = [0, 0, 0, 0] return sRGBA
def convert_to_jab(df, from_cm='sRGB255', from_cols=['R', 'G', 'B']): """Converts a dataframe inplace from one color map to JCAM02-UCS Will delete originating columns """ arr_tmp = cspace_convert(df[from_cols], from_cm, 'CAM02-UCS') df[['J', 'a', 'b']] = pd.DataFrame(arr_tmp, index=df.index) df.drop(columns=from_cols, inplace=True) return df
def get_rainbow(Cp, Jp, count): # Adapted from the demo in https://youtu.be/xAoljeRJ3lU?t=14m40s # Nathaniel Smith and Stéfan van der Walt # “A Better Default Colormap for Matplotlib” # SciPy 2015 Conference hp = np.linspace(0.0, 360.0, count) JpCphp = np.column_stack([np.full(hp.shape, Jp), np.full(hp.shape, Cp), hp]) with np.errstate(divide="ignore", invalid="ignore"): rgb = colorspacious.cspace_convert(JpCphp, "JCh", "sRGB255") clipped_rgb = np.clip(rgb, 0.0, 255.0) return np.ma.masked_array(clipped_rgb, clipped_rgb - rgb)
def get_rainbow(Cp, Jp, count): # Adapted from the demo in https://youtu.be/xAoljeRJ3lU?t=14m40s # Nathaniel Smith and Stéfan van der Walt # “A Better Default Colormap for Matplotlib” # SciPy 2015 Conference t = (np.linspace(0.0, 1.0, count)) * 2.0 * np.pi ap = Cp * np.cos(t) bp = Cp * np.sin(t) Jpapbp = np.column_stack([np.full(ap.shape, Jp), ap, bp]) with np.errstate(divide="ignore", invalid="ignore"): rgb = colorspacious.cspace_convert(Jpapbp, "CAM02-UCS", "sRGB255") clipped_rgb = np.clip(rgb, 0.0, 255.0) return np.ma.masked_array(clipped_rgb, clipped_rgb - rgb)
def perceptual(image): """ Convert color from RGB (sRGB1) to a perceptually uniform colorspace. This is a convenience function wrapping ``colorspacious.csapce_convert``. To configure the specific perceptual colorspace used, change ``photomosaic.options['colorspace']``. Parameters ---------- image : array """ return colorspacious.cspace_convert(image, options["rgb"], options["perceptual"])
def sRGB_gamut_patch(uniform_space, resolution=20): step = 1.0 / resolution sRGB_quads = [] sRGB_values = [] # each entry in 'quads' is a 4x3 array where each row contains the # coordinates of a corner point for fixed in 0, 1: for i in range(resolution): for j in range(resolution): # R quad sRGB_quads.append( [ [fixed, i * step, j * step], [fixed, (i + 1) * step, j * step], [fixed, (i + 1) * step, (j + 1) * step], [fixed, i * step, (j + 1) * step], ] ) sRGB_values.append((fixed, (i + 0.5) * step, (j + 0.5) * step, 1)) # G quad sRGB_quads.append( [ [i * step, fixed, j * step], [(i + 1) * step, fixed, j * step], [(i + 1) * step, fixed, (j + 1) * step], [i * step, fixed, (j + 1) * step], ] ) sRGB_values.append(((i + 0.5) * step, fixed, (j + 0.5) * step, 1)) # B quad sRGB_quads.append( [ [i * step, j * step, fixed], [(i + 1) * step, j * step, fixed], [(i + 1) * step, (j + 1) * step, fixed], [i * step, (j + 1) * step, fixed], ] ) sRGB_values.append(((i + 0.5) * step, (j + 0.5) * step, fixed, 1)) sRGB_quads = np.asarray(sRGB_quads) # work around colorspace transform bugginess in handling high-dim # arrays sRGB_quads_2d = sRGB_quads.reshape((-1, 3)) Jpapbp_quads_2d = cspace_convert(sRGB_quads_2d, "sRGB1", uniform_space) Jpapbp_quads = Jpapbp_quads_2d.reshape((-1, 4, 3)) gamut_patch = mpl_toolkits.mplot3d.art3d.Poly3DCollection(Jpapbp_quads[:, :, [1, 2, 0]]) gamut_patch.set_facecolor(sRGB_values) gamut_patch.set_edgecolor(sRGB_values) return gamut_patch
def rgb(image, clip=True): """ Convert color from a perceptually uniform colorspace to RGB. This is a convenience function wrapping ``colorspacious.csapce_convert``. To configure the specific perceptual colorspace used, change ``photomosaic.options['perceptual']`` and ``photomosaic.options['rgb']``. Parameters ---------- image : array clip : bool, option Clip values out of the gamut [0, 1]. True by default. """ result = colorspacious.cspace_convert(image, options["perceptual"], options["rgb"]) if clip: result = np.clip(result, 0, 1) return result
def analyze(filename): try: raw_image = imread(filename, **options["imread"]) except Exception as err: if skip_read_failures: warnings.warn("Skipping {}; raised exception:\n {}" "".format(filename, err)) return raise image = standardize_image(raw_image) # Subsample before doing expensive color space conversion. if sample_size is not None: sample = sample_pixels(image, sample_size) else: sample = image.reshape(-1, 3) # list of pixels # Convert color to perceptually-uniform color space. converted_sample = colorspacious.cspace_convert(sample, options["rgb"], options["perceptual"]) vector = analyzer(converted_sample) return vector
def fun_simulate_cvd_Machado(img_rgb, type_cvd = "p", severity_factor = 100): """The function use the code from the module colorspacious where the Method from Machado had been implemented """ if type_cvd == "p": cvd_space = {"name": "sRGB1+CVD", "cvd_type": "protanomaly", "severity": severity_factor} if type_cvd == "d": cvd_space = {"name": "sRGB1+CVD", "cvd_type": "deuteranomaly", "severity": severity_factor} if type_cvd == "t": cvd_space = {"name": "sRGB1+CVD", "cvd_type": "tritanomaly", "severity": severity_factor} img_cvd_sRGB = cs.cspace_convert(img_rgb, cvd_space, "sRGB1") img_cvd_sRGB = np.clip(img_cvd_sRGB,0, 255) img_cvd_sRGB = img_cvd_sRGB.astype("uint8") return img_cvd_sRGB
def generate_palette( colors, size, base=None, no_black=False, lightness_range=None, chroma_range=None, hue_range=None, ): # Initialize palette with given base or white color if base: palette = [colors[i, :] for i in base] else: palette = [colors[-1, :]] # white # Exclude greys (values with low Chroma in JCh) and set lightness range, if lightness_range is not None: jch = cspace_convert(colors, "CAM02-UCS", "JCh") colors = colors[ (jch[:, 0] >= lightness_range[0]) & (jch[:, 0] <= lightness_range[1]), : ] if chroma_range is not None: jch = cspace_convert(colors, "CAM02-UCS", "JCh") colors = colors[ (jch[:, 1] >= chroma_range[0]) & (jch[:, 1] <= chroma_range[1]), : ] if hue_range is not None: jch = cspace_convert(colors, "CAM02-UCS", "JCh") if hue_range[0] > hue_range[1]: colors = colors[ (jch[:, 2] >= hue_range[0]) | (jch[:, 2] <= hue_range[1]), : ] else: colors = colors[ (jch[:, 2] >= hue_range[0]) & (jch[:, 2] <= hue_range[1]), : ] # Exclude colors that are close to black if no_black: MIN_DISTANCE_TO_BLACK = 35 d = np.linalg.norm((colors - colors[0, :]), axis=1) colors = colors[d > MIN_DISTANCE_TO_BLACK, :] # Initialize distances array num_colors = colors.shape[0] distances = np.ones(shape=(num_colors, 1)) * 1000 # A function to recompute minimum distances from palette to all colors def update_distances(colors, color): d = np.linalg.norm((colors - color), axis=1) np.minimum(distances, d.reshape(distances.shape), distances) # Build progress bar widgets = ["Generating palette: ", Percentage(), " ", Bar(), " ", ETA()] pbar = ProgressBar(widgets=widgets, maxval=size).start() # Update distances for the colors that are already in the palette for i in range(len(palette) - 1): update_distances(colors, palette[i]) pbar.update(i) # Iteratively build palette while len(palette) < size: update_distances(colors, palette[-1]) palette.append(colors[np.argmax(distances), :]) pbar.update(len(palette)) pbar.finish() return cspace_convert(palette, "CAM02-UCS", "sRGB1")
# This creates a circular, perceptually uniform colormap # The code was stolen from the matplotlib/numpy/scipy # presentation (matplotlib 2.0 presentation) # Number of points N = 256 # Constant lightness Jp = 75*np.ones(N) # Constant saturation, varying hue radius = 30 theta = np.linspace(0, 2*np.pi, N) ap = radius*np.sin(theta) bp = radius*np.cos(theta) Jpapbp = np.column_stack((Jp, ap, bp)) from colorspacious import cspace_convert rgb = cspace_convert(Jpapbp, "CAM02-UCS", "sRGB1") # Matplotlib defaults to rgba, not rgb. # Reset the transparency values now. col_list[:,3] = 1. circular_map = mplc.ListedColormap(col_list, 'circular_map', 256) plt.register_cmap(cmap=circular_map) ## ------