def test_to_husl_triplet(): assert type(nphusl.to_husl([10, 30, 40])) == np.ndarray _diff(nphusl.to_husl([0x80, 0x80, 0x80]), nphusl.to_husl([0.5, 0.5, 0.5]), diff=0.3) assert nphusl.to_husl([30, 30, 30])[1] < 0.01 # low saturation gray value assert nphusl.to_husl(np.asarray([255, 0, 0]))[0] < 13 # red hue
def test_to_husl_1d_grayscale(): hsl_from_gray = nphusl.to_husl([0.1, 0.1]) # interpreted as 2 gray pixels hsl_from_rgb = nphusl.to_husl([[0.1, 0.1, 0.1], [0.1, 0.1, 0.1]]) expected = [[2.88467242e+02, 1.02865661e-05, 9.30666400], [2.88467242e+02, 1.02865661e-05, 9.30666400]] assert np.all(hsl_from_gray == hsl_from_rgb) _diff_husl(hsl_from_gray, expected) _diff_husl(hsl_from_rgb, expected)
def test_to_husl_rgba(): rgb = transform.ensure_rgb_float(_img()) rgba = np.zeros(shape=rgb.shape[:-1] + (4, ), dtype=rgb.dtype) rgba[..., :3] = rgb rgba[..., 3] = 0.5 # 50% for a float RGBA array new_rgb = rgb * 0.5 hsl_from_rgba = nphusl.to_husl(rgba) hsl_from_rgb = nphusl.to_husl(new_rgb) _diff_husl(hsl_from_rgba, hsl_from_rgb)
def test_to_husl_rgba(): rgb = _img() rgba = np.zeros(shape=rgb.shape[:-1] + (4,), dtype=rgb.dtype) rgba[..., :3] = rgb alpha = 0x80 # 50% rgba[..., 3] = alpha ratio = alpha / 255.0 new_rgb = np.round(rgb * ratio).astype(dtype=np.uint8) hsl_from_rgba = nphusl.to_husl(rgba) hsl_from_rgb = nphusl.to_husl(new_rgb) assert _diff(hsl_from_rgba, hsl_from_rgb)
def test_to_husl_2d(): img = _img()[0] rgb_arr = img * 255 husl_new = nphusl.to_husl(rgb_arr) for row in range(rgb_arr.shape[0]): husl_old = husl.rgb_to_husl(*img[row]) assert _diff(husl_new[row], husl_old)
def melonize(img, n_frames): hsl = nphusl.to_husl(img) hue, sat, lit = (hsl[..., n] for n in range(3)) #sat[:] = 99 pink = 360 # very end of the H spectrum green = 130 def gen_chunksizes(): yield from range(1, 100) yield from range(100, 1, -1) for chunksize in gen_chunksizes(): hsl_out = hsl.copy() hue_out, sat_out, lit_out = (hsl_out[..., i] for i in range(3)) for low, high in nphusl.chunk(100, chunksize): # chunks of the hue range select = np.logical_and(lit > low, lit < high) is_odd = low % (chunksize * 2) color = pink if is_odd else green hue_out[select] = color select = np.logical_and(lit > (low - 1), lit < low) select = np.logical_and(select, lit > 60) ave = (low + high) / 2 select = np.logical_and(lit > (ave - 2), lit < (ave + 2)) sat_out[select] = 100 yield nphusl.to_rgb(hsl_out)
def test_to_husl_2d(): img = np.ascontiguousarray(_img()[:, 4]) float_img = transform.ensure_rgb_float(img) husl_new = nphusl.to_husl(img) for row in range(img.shape[0]): husl_old = husl.rgb_to_husl(*float_img[row]) _diff_husl(husl_new[row], husl_old)
def clump_hues(img): hsl = nphusl.to_husl(img) H, L = hsl[..., 0], hsl[..., 2] light = mask(img, L > 1) travel = (1,) effects = (clump_vert(s, travel) for s in _select_ranges(H, 50, light)) return Group(img, *effects()).zip()
def img(request): if CachedImg.rgb is None: path = request.config.getoption("--img") CachedImg.path = path CachedImg.rgb = imageio.imread(path) CachedImg.hsl = nphusl.to_husl(CachedImg.rgb) return CachedImg
def test_to_husl_3d(): img = _img() rgb_arr = img * 255 husl_new = nphusl.to_husl(rgb_arr) for row in range(rgb_arr.shape[0]): for col in range(rgb_arr.shape[1]): husl_old = husl.rgb_to_husl(*img[row, col]) assert _diff_hue(husl_new[row, col], husl_old)
def test_to_husl_3d(): img = _img() float_img = transform.ensure_rgb_float(img) husl_new = nphusl.to_husl(img) for row in range(img.shape[0]): for col in range(img.shape[1]): husl_old = husl.rgb_to_husl(*float_img[row, col]) _diff_husl(husl_new[row, col], husl_old)
def clump_dark(img): hsl = nphusl.to_husl(img) H, L = hsl[..., 0], hsl[..., 2] dark = mask(img, L < 5) travel = (5,) vert = clump_vert(dark, travel) horz = clump_horz(dark, travel) return Group(img, vert, horz).zip(effects_per_frame=5)
def disperse_light(img): hsl = nphusl.to_husl(img) H, L = hsl[..., 0], hsl[..., 2] light = mask(img, L > 80) travel = (1,) vert = disperse_vert(img, light, travel) horz = disperse_horz(img, light, travel) return Group(img, horz, vert).zip()
def test_husl_to_lch(): img = _img() float_img = transform.ensure_rgb_float(img) lch = _nphusl._rgb_to_lch(float_img) husl = nphusl.to_husl(img) lch_2 = _nphusl._husl_to_lch(husl) img_2 = _nphusl._lch_to_rgb(lch_2) img_2 = transform.ensure_rgb_int(img_2) _diff(img_2, img, diff=1)
def reveal_blue(img): """Easy mode! Selecting bluish pixels with the HUSL color space.""" # convert an integer RGB image to HUSL array of floats hsl = nphusl.to_husl(img) hue = hsl[..., 0] # separate out the hue channel # create a mask for pixels with hues between 250 and 290 (blue) bluish = np.logical_and(hue > 250, hue < 290) hsl[..., 2][~bluish] *= 0.5 # halve lightness of non-bluish areas return nphusl.to_rgb(hsl), "blue"
def test_to_husl_gray(): img = transform.ensure_rgb_float(_img()) img[..., 1] = img[..., 0] img[..., 2] = img[..., 0] gray_arr = img[..., 0] # single channel husl_new = nphusl.to_husl(gray_arr) for row in range(gray_arr.shape[0]): for col in range(gray_arr.shape[1]): husl_old = husl.rgb_to_husl(*img[row, col]) _diff_husl(husl_new[row, col], husl_old)
def test_to_husl_gray(): img = _img() img[..., 1] = img[..., 0] img[..., 2] = img[..., 0] rgb_arr = img[..., 0] * 255 # single channel husl_new = nphusl.to_husl(rgb_arr) for row in range(rgb_arr.shape[0]): for col in range(rgb_arr.shape[1]): husl_old = husl.rgb_to_husl(*img[row, col]) assert _diff_hue(husl_new[row, col], husl_old)
def slide_colors(img): hsl = nphusl.to_husl(img) H, L = hsl[..., 0], hsl[..., 2] light = mask(img, L > 4) blue = mask(img, light, H > 240, H < 290) red = mask(img, light, _or(H < 40, H > 320)) travel = (4,) blue_up = slide_vert(blue, travel) red_right = slide_horz(red, travel) light_right = slide_horz(light, travel) return Group(img, light_right).zip()
def hue_watermelon(img): hsl = nphusl.to_husl(img) hue, saturation, lightness = (hsl[..., n] for n in range(3)) hue_out = hue.copy() pink = 360 # very end of the H spectrum green = 130 chunksize = 45 for low, high in nphusl.chunk(360, chunksize): # chunks of the hue range select = np.logical_and(hue > low, hue < high) is_odd = low % (chunksize * 2) color = pink if is_odd else green hue_out[select] = color hue[:] = hue_out return nphusl.to_rgb(hsl), "watermelon"
def microwave(img): hsl = nphusl.to_husl(img) hue = hsl[..., 0] rows, cols = hue.shape yield nphusl.to_rgb(hsl) while True: for chunk, ((rs, re), (cs, ce)) in nphusl.chunk_img(hue, chunksize=8): hue_left = hue[rs, cs-1] hue_up = hue[rs-1, cs] this_hue = chunk[0, 0] new_hue = (-random.randrange(30, 50) * (hue_up / 360) -10*random.randrange(1, 10) * (hue_left / 360)) new_hue = (15*this_hue + 2*new_hue) / 17 chunk[:] = new_hue np.mod(hue, 360, out=hue) yield nphusl.to_rgb(hsl)
def microwave(img): hsl = nphusl.to_husl(img) hue = hsl[..., 0] rows, cols = hue.shape yield nphusl.to_rgb(hsl) while True: for chunk, ((rs, re), (cs, ce)) in nphusl.chunk_img(hue, chunksize=8): hue_left = hue[rs, cs - 1] hue_up = hue[rs - 1, cs] this_hue = chunk[0, 0] new_hue = (-random.randrange(30, 50) * (hue_up / 360) - 10 * random.randrange(1, 10) * (hue_left / 360)) new_hue = (15 * this_hue + 2 * new_hue) / 17 chunk[:] = new_hue np.mod(hue, 360, out=hue) yield nphusl.to_rgb(hsl)
def test_to_husl_gray_3d(): img = _img() img[..., 1] = img[..., 0] # makes things gray img[..., 2] = img[..., 0] # makes things gray img_float = transform.ensure_rgb_float(img) husl_new = nphusl.to_husl(img) was_wrong = False for row in range(img.shape[0]): for col in range(img.shape[1]): husl_old = husl.rgb_to_husl(*img_float[row, col]) a = husl.husl_to_rgb(*husl_old) b = husl.husl_to_rgb(*husl_new[row, col]) a = np.asarray(a) b = np.asarray(b) i = row * img.shape[1] * 3 + col * 3 _diff_husl(husl_new[row, col], husl_old)
def clump_gradient(img): hsl = nphusl.to_husl(img) H, L = hsl[..., 0], hsl[..., 2] dark = mask(img, L < 5) travel = (5,) ranges = list(range(1, 5)) def effects(ranges): min_L = ranges[0] for max_L in ranges[1:]: m = mask(img, L < max_L, L > min_L) yield clump_vert(m, travel) yield clump_horz(m, travel) min_L = max_L effs = effects(ranges) return Group(img, *effs).zip(effects_per_frame=5)
def test_to_hue(): img = _img()[0] # 2D as_husl = nphusl.to_husl(img) just_hue = nphusl.to_hue(img) assert _diff(as_husl[..., 0], just_hue)
def test_to_rgb_2d(): img = np.ascontiguousarray(_img()[:, 17]) husl = nphusl.to_husl(img) rgb = nphusl.to_rgb(husl) _diff(rgb, img, diff=1)
def highlight_saturation(img): hsl = nphusl.to_husl(img) hsl[..., 2][hsl[..., 1] < 80] = 0 return nphusl.to_rgb(hsl), "saturation"
def reveal_light(img): hsl = nphusl.to_husl(img) lightness = hsl[..., 2] # just the lightness channel dark = lightness < 62 hsl[..., 2][dark] = 0 # darkish areas to completely dark return nphusl.to_rgb(hsl), "light"
def test_transform_rgb(): img = _img() as_husl = nphusl.to_husl(img) chunk_husl = transform.in_chunks(img, nphusl.to_husl, 10) _diff(as_husl, chunk_husl)
def test_to_hue_2d(): img = _img()[:, 14] # 2D RGB as_husl = nphusl.to_husl(img) just_hue = nphusl.to_hue(img) _diff(as_husl[..., 0], just_hue)
def test_to_husl_rgba_quadruplet(): hsl = nphusl.to_husl([0.5, 0.2, 0, 0.5]) _diff_husl(hsl, [28.42153418, 100.0, 14.39285828])
def test_accuracy(img): with nphusl.simd_enabled(): hsl = nphusl.to_husl(img.rgb) with nphusl.numpy_enabled(): rgb = nphusl.to_rgb(hsl) hsl_ref = nphusl.to_husl(img.rgb) size = hsl.shape[0] * hsl.shape[1] hsl_flat = hsl.reshape((size, 3)) rgb_flat = rgb.reshape((size, 3)) hsl_ref_flat = hsl_ref.reshape((size, 3)) rgb_ref_flat = img.rgb.reshape((size, 3)) rgb_diff = np.abs(rgb_flat.astype(int) - rgb_ref_flat) hsl_diff = np.abs(hsl_flat - hsl_ref_flat) h_err, s_err, l_err = (hsl_diff[..., n] for n in range(3)) h, s, l = (hsl_flat[..., n] for n in range(3)) percentiles = [0, 25, 50, 90, 95, 96, 97, 98, 99.5, 99.6, 99.7, 99.8, 99.9, 100] def print_err_tables(): fields = "Percentile", "Red error", "Green error", "Blue error", " " print(BOLD + "\nIMG->HUSL->RGB roundtrip error" + END) print(_error_table(rgb_diff, percentiles, fields)) fields = "Percentile", "Hue error", "Sat error", "Light error", " " print(BOLD + "\nIMG->HUSL error vs. reference impl." + END) print(_error_table(hsl_diff, percentiles, fields)) for i, name in enumerate("hue saturation lightness".split()): c = hsl_flat[..., i] c_err = hsl_diff[..., i] err_99 = np.percentile(c_err, 99) print(BOLD + "\nTypical RGB for {} error above 99th " "percentile: ".format(name) + END) print(img.rgb[c_err.reshape(rgb.shape[:-1]) > err_99]) print(BOLD + "\nTypical HUSL for {} error above 99th " "percentile: ".format(name) + END) print(hsl[c_err.reshape(rgb.shape[:-1]) > err_99]) print(BOLD + "\nAll RGB & HUSL errors") print( "=====================" + END) print_err_tables() h_err[s < 0.1] = 0 # hue errors for low saturation have no meaning rgb_diff[s < 0.1] = 0 s_err[l > 99.5] = 0 # saturation errors when very bright not meaningful rgb_diff[l > 99.5] = 0 s_err[l < 1] = 0 # saturation errors when very dim not meaningful rgb_diff[l < 1] = 0 print(BOLD + "\nPerceptible RGB & HUSL errors") print( "=============================" + END) print_err_tables() max_err = max(np.percentile(rgb_diff, 100, axis=0)) mask = np.any(rgb_diff == max_err, axis=1) print(BOLD + "\nMost challenging pixel") print( "======================" + END) print("RGB input vs. output: {} -> {}".format( rgb_ref_flat[mask].squeeze(), rgb_flat[mask].squeeze())) print("HUSL ouput vs. reference impl.: {} vs. {}".format( hsl_flat[mask].squeeze(), hsl_ref_flat[mask].squeeze())) src = "_accuracy_test_source.png" rec = "_accuracy_test_recreated.png" print("\nWriting PNGs: {}, {}".format(src, rec)) imageio.imwrite(src, img.rgb) imageio.imwrite(rec, rgb)