def from_xyz100(self, xyz): # x, y, z = (xyz.T / self.whitepoint).T # In nit units, ranging from 0 to 10000? x, y, z = xyz x_ = self.b * x - (self.b - 1) * z y_ = self.g * y - (self.g - 1) * x lms = npx.dot(self.M1, [x_, y_, z]) lms_ = ((self.c1 + self.c2 * (lms / 10000)**self.n) / (1 + self.c3 * (lms / 10000)**self.n))**self.p iz, az, bz = npx.dot(self.M2, lms_) jz = (1 + self.d) * iz / (1 + self.d * iz) - self.d0 return np.array([jz, az, bz])
def to_xyz100(self, data, description): """Input: J or Q; C, M or s; H or h""" rgb_c = compute_to(data, description, self) # Step 6: Calculate R, G and B # rgb = (rgb_c.T / self.D_RGB).T # Step 7: Calculate X, Y and Z # xyz = self.solve_M16(rgb) return npx.dot(self.invM_, rgb_c)
def from_xyz100(self, xyz): # Step 1: Calculate 'cone' responses # rgb = dot(self.M16, xyz) # Step 2: Complete the color adaptation of the illuminant in # the corresponding cone response space # rgb_c = (rgb.T * self.D_RGB).T rgb_c = npx.dot(self.M_, xyz) return compute_from(rgb_c, self)
def from_xyz100(self, xyz): # Step 1: Calculate 'cone' responses rgb = npx.dot(self.M16, xyz) # Step 2: Complete the color adaptation of the illuminant in # the corresponding cone response space rgb_c = (rgb.T * self.D_RGB).T # Step 3: Calculate the post-adaptation cone response (resulting in # dynamic range compression) alpha = (self.F_L * abs(rgb_c) / 100)**0.42 rgb_a = np.sign(rgb_c) * 400 * alpha / (alpha + 27.13) + 0.1 # Step 4 a = npx.dot(np.array([1, -12 / 11, 1 / 11]), rgb_a) b = npx.dot(np.array([1 / 9, 1 / 9, -2 / 9]), rgb_a) # Make sure that h is in [0, 360] h = np.rad2deg(np.arctan2(b, a)) % 360 # Step 5: Calculate eccentricity (e_t) and hue composition (H), using # the unique hue data given in Table 2.4. h_ = (h - self.h[0]) % 360 + self.h[0] e_t = (np.cos(np.deg2rad(h_) + 2) + 3.8) / 4 i = np.searchsorted(self.h, h_) - 1 beta = (h_ - self.h[i]) * self.e[i + 1] H = self.H[i] + 100 * beta / (beta + self.e[i] * (self.h[i + 1] - h_)) # Step 6 A = (npx.dot(np.array([2, 1, 1 / 20]), rgb_a) - 0.305) * self.N_bb # Step 7: Calculate the correlate of lightness J = 100 * (A / self.A_w)**(self.c * self.z) # Step 8: Calculate the correlate of brightness sqrt_J_100 = np.sqrt(J / 100) Q = (4 / self.c) * sqrt_J_100 * (self.A_w + 4) * self.F_L**0.25 # Step 9: Calculate the correlates of chroma (C), colourfulness (M) # and saturation (s) # t = (50000 / 13 * e_t * self.N_c * self.N_cb * np.sqrt(a**2 + b**2) / npx.dot(np.array([1, 1, 21 / 20]), rgb_a)) C = t**0.9 * (1.64 - 0.29**self.n)**0.73 * sqrt_J_100 M = C * self.F_L**0.25 s = 100 * np.sqrt(M / Q) return np.array([J, C, H, h, M, s, Q])
def f_df(omega): omega = np.full_like(ap, omega) cbrt_RGB = np.array([omega, omega + ap, omega + bp]) # A = np.array([[-13.7, 17.7, -4], [1.7, 8, -9.7], [0.0, 1.0, 0.0]]) # Ainv = np.linalg.inv(A) # rhs = np.array( # [np.full(omega.shape, a), np.full(omega.shape, b), omega] # ) # cbrt_RGB = dot(Ainv, rhs) # # a = -13.7 * np.cbrt(R) + 17.7 * np.cbrt(G) - 4 * np.cbrt(B) # # b = 1.7 * np.cbrt(R) + 8 * np.cbrt(G) - 9.7 * np.cbrt(B) RGB = cbrt_RGB**3 xyz100 = npx.dot(self.Minv, RGB) X, Y, _ = xyz100 sum_xyz = np.sum(xyz100, axis=0) x = X / sum_xyz y = Y / sum_xyz K = (4.4934 * x**2 + 4.3034 * y**2 - 4.276 * x * y - 1.3744 * x - 2.5643 * y + 1.8103) f = Y * K - Y0 # df/domega # dcbrt_RGB = 1.0 # dRGB = 3 * cbrt_RGB ** 2 * dcbrt_RGB dRGB = 3 * cbrt_RGB**2 dxyz100 = npx.dot(self.Minv, dRGB) dX, dY, _ = dxyz100 dsum_xyz = np.sum(dxyz100, axis=0) dx = (dX * sum_xyz - X * dsum_xyz) / sum_xyz**2 dy = (dY * sum_xyz - Y * dsum_xyz) / sum_xyz**2 dK = (4.4934 * 2 * x * dx + 4.3034 * 2 * y * dy - 4.276 * (dx * y + x * dy) - 1.3744 * dx - 2.5643 * dy) df = dY * K + Y * dK return f, df, xyz100
def to_xyz100(self, srgb1_linear): # Note: The Y value is often used for grayscale conversion. # 0.2126 * R_linear + 0.7152 * G_linear + 0.0722 * B_linear return 100 * npx.dot(self.invM, srgb1_linear)
def to_xyz100(self, lab): return npx.dot(self.M1inv, npx.dot(self.M2inv, lab)**3) * 100
def from_xyz100(self, xyz100): xyz = np.asarray(xyz100) / 100 return npx.dot(self.M2, np.cbrt(npx.dot(self.M1, xyz)))
def to_xyz100(self, data, description): """Input: J or Q; C, M or s; H or h""" if description[0] == "J": J = data[0] # Q perhaps needed for C Q = (4 / self.c) * np.sqrt( J / 100) * (self.A_w + 4) * self.F_L**0.25 else: # Step 1–1: Compute J from Q (if start from Q) assert description[0] == "Q" Q = data[0] J = 6.25 * (self.c * Q / (self.A_w + 4) / self.F_L**0.25)**2 # Step 1–2: Calculate C from M or s if description[1] == "C": C = data[1] elif description[1] == "M": M = data[1] C = M / self.F_L**0.25 else: assert description[1] == "s" s = data[1] C = (s / 100)**2 * Q / self.F_L**0.25 if description[2] == "h": h = data[2] else: assert description[2] == "H" # Step 1–3: Calculate h from H (if start from H) H = data[2] i = np.searchsorted(self.H, H) - 1 Hi = self.H[i] hi, hi1 = self.h[i], self.h[i + 1] ei, ei1 = self.e[i], self.e[i + 1] h_ = ((H - Hi) * (ei1 * hi - ei * hi1) - 100 * hi * ei1) / ( (H - Hi) * (ei1 - ei) - 100 * ei1) h = np.mod(h_, 360) h = np.deg2rad(h) # Step 2: Calculate t, et , p1, p2 and p3 A = self.A_w * (J / 100)**(1 / self.c / self.z) # Step 3: Calculate a and b t = (C / np.sqrt(J / 100) / (1.64 - 0.29**self.n)**0.73)**(1 / 0.9) e_t = 0.25 * (np.cos(h + 2) + 3.8) one_over_t = 1 / t one_over_t = np.select([np.isnan(one_over_t), True], [np.inf, one_over_t]) p1 = (50000.0 / 13) * self.N_c * self.N_cb * e_t * one_over_t p2 = A / self.N_bb + 0.305 p3 = 21 / 20 sin_h = np.sin(h) cos_h = np.cos(h) num = p2 * (2 + p3) * (460.0 / 1403) denom_part2 = (2 + p3) * (220.0 / 1403) denom_part3 = (-27.0 / 1403) + p3 * (6300.0 / 1403) a = np.empty_like(h) b = np.empty_like(h) small_cos = np.abs(sin_h) >= np.abs(cos_h) b[small_cos] = num[small_cos] / ( p1[small_cos] / sin_h[small_cos] + (denom_part2 * cos_h[small_cos] / sin_h[small_cos]) + denom_part3) a[small_cos] = b[small_cos] * cos_h[small_cos] / sin_h[small_cos] a[~small_cos] = num[~small_cos] / ( p1[~small_cos] / cos_h[~small_cos] + denom_part2 + (denom_part3 * sin_h[~small_cos] / cos_h[~small_cos])) b[~small_cos] = a[~small_cos] * sin_h[~small_cos] / cos_h[~small_cos] # Step 4: Calculate RGB_a_ rgb_a_ = (npx.dot( np.array([[460, 451, 288], [460, -891, -261], [460, -220, -6300]]), np.array([p2, a, b]), ) / 1403) # Step 5: Calculate RGB_ rgb_c = (np.sign(rgb_a_ - 0.1) * 100 / self.F_L * ((27.13 * abs(rgb_a_ - 0.1)) / (400 - abs(rgb_a_ - 0.1)))**(1 / 0.42)) # Step 6: Calculate R, G and B rgb = (rgb_c.T / self.D_RGB).T # Step 7: Calculate X, Y and Z xyz = npx.dot(self.invM16, rgb) return xyz