sRGBからCIELABへの変換
普通に計算するとすっげー遅いよね、と言ってみるテスト。
import java.util.Random; public class CIELAB { private static final int[] _gammaTable; private static final int[] _labTable; static { _gammaTable = createGammaTable(); _labTable = createLabTable(); } private static int[] createGammaTable() { int[] table = new int[256]; for (int i = 0; i < 256; ++i) { double d = i / 255.0; table[i] = (int) (((d > 0.04045) ? Math.pow((d+0.055)/1.055,2.4) : d/12.92) * 100000); } return table; } private static int[] createLabTable() { int[] table = new int[100001]; for (int i = 0; i < table.length; ++i) { double d = i / 100001.0; table[i] = (int) (((d > 0.008856) ? Math.pow(d,1/3.0) : (7.787*d)+(16/116.0)) * 1000000); } return table; } public static void sRGBToLab_fast(int srgb, int[] lab) { int r = _gammaTable[(srgb & 0x00FF0000) >> 16]; int g = _gammaTable[(srgb & 0x0000FF00) >> 8 ]; int b = _gammaTable[ srgb & 0x000000FF ]; // r,g,b 各値は 0 から 100000 の範囲 int x = 4124 * r + 3576 * g + 1805 * b; int y = 2126 * r + 7152 * g + 722 * b; int z = 193 * r + 1192 * g + 9505 * b; // x: 0-950500000 // y: 0-1000000000 // z: 0-1089000000 // 0-1000000000 の範囲へ揃えるのと _labTable のインデックス範囲 0-100000 へのマッピングを同時に行う。 x = x / 9505; y = y / 10000; z = z / 10890; // x,y,z 各値は 0 から 100000 の範囲 x = _labTable[x]; y = _labTable[y]; z = _labTable[z]; // x,y,z 各値は 0 から 1000000 の範囲 lab[0] = 116 * y - 16000000; // L* lab[1] = 500 * (x - y); // a* lab[2] = 200 * (y - z); // b* } public static void sRGBToLab_orig(int srgb, double[] lab) { double r = ((srgb & 0x00FF0000) >> 16) / 255.0; double g = ((srgb & 0x0000FF00) >> 8 ) / 255.0; double b = ((srgb & 0x000000FF) ) / 255.0; r = ((r > 0.04045) ? Math.pow((r+0.055)/1.055,2.4) : r/12.92) * 100; g = ((g > 0.04045) ? Math.pow((g+0.055)/1.055,2.4) : g/12.92) * 100; b = ((b > 0.04045) ? Math.pow((b+0.055)/1.055,2.4) : b/12.92) * 100; // Observer. = 2°, Illuminant = D65 double x = 0.4124 * r + 0.3576 * g + 0.1805 * b; double y = 0.2126 * r + 0.7152 * g + 0.0722 * b; double z = 0.0193 * r + 0.1192 * g + 0.9505 * b; x = x/ 95.047; //ref_X = 95.047 Observer= 2°, Illuminant= D65 y = y/100.000; //ref_Y = 100.000 z = z/108.883; //ref_Z = 108.883 x = (x > 0.008856) ? Math.pow(x,1/3.0) : (7.787*x)+(16/116.0); y = (y > 0.008856) ? Math.pow(y,1/3.0) : (7.787*y)+(16/116.0); z = (z > 0.008856) ? Math.pow(z,1/3.0) : (7.787*z)+(16/116.0); lab[0] = 116 * y - 16; lab[1] = 500 * (x - y); lab[2] = 200 * (y - z); } public static void main(String[] args) { if (args.length == 3) { int srgb = ((Integer.parseInt(args[0]) & 0xff) << 16) | ((Integer.parseInt(args[1]) & 0xff) << 8) | (Integer.parseInt(args[2]) & 0xff); int[] lab_i = new int[3]; sRGBToLab_fast(srgb, lab_i); System.out.printf("fast: L=%f, a=%f, b=%f%n", lab_i[0]/1000000.0, lab_i[1]/1000000.0, lab_i[2]/1000000.0); double[] lab_d = new double[3]; sRGBToLab_orig(srgb, lab_d); System.out.printf("orig: L=%f, a=%f, b=%f%n", lab_d[0], lab_d[1], lab_d[2]); } else { int n = 100000; if (args.length > 0) { try { n = Integer.parseInt(args[0]); } catch (NumberFormatException e) { } } benchmark(n); } } private static void benchmark(int n) { int[] srgb = new int[n]; int[] lab_i = new int[3]; double[] lab_d = new double[3]; Random random = new Random(); for (int i = 0; i < srgb.length; ++i) { srgb[i] = random.nextInt(0x1000000); } long start; start = System.currentTimeMillis(); for (int i = 0; i < srgb.length; ++i) { sRGBToLab_fast(srgb[i], lab_i); lab_d[0] = lab_i[0]/1000000.0; lab_d[1] = lab_i[1]/1000000.0; lab_d[2] = lab_i[2]/1000000.0; } System.out.printf("fast: %d ms%n", System.currentTimeMillis() - start); start = System.currentTimeMillis(); for (int i = 0; i < srgb.length; ++i) { sRGBToLab_orig(srgb[i], lab_d); } System.out.printf("orig: %d ms%n", System.currentTimeMillis() - start); } }
1280x720, 10フレーム分(=9216000ピクセル)で計ってみる。
>java CIELAB 9216000 fast: 437 ms orig: 13438 ms