[英]Any faster algorithm to transform from RGB to CMYK
這就是我使用更“正確”的方式從RGB轉換為CMYK的方式 - 即使用ICC顏色配置文件。
// Convert RGB to CMYK with level shift (minus 128)
private void RGB2CMYK(int[] rgb, float[][] C, float[][] M, float[][] Y, float[][] K, int imageWidth, int imageHeight) throws Exception {
ColorSpace instance = new ICC_ColorSpace(ICC_Profile.getInstance(JPEGWriter.class.getResourceAsStream(pathToCMYKProfile)));
float red, green, blue, cmyk[];
//
for(int i = 0, index = 0; i < imageHeight; i++) {
for(int j = 0; j < imageWidth; j++, index++) {
red = ((rgb[index] >> 16) & 0xff)/255.0f;
green = ((rgb[index] >> 8) & 0xff)/255.0f;
blue = (rgb[index] & 0xff)/255.0f;
cmyk = instance.fromRGB(new float[] {red, green, blue});
C[i][j] = cmyk[0]*255.0f - 128.0f;
M[i][j] = cmyk[1]*255.0f - 128.0f;
Y[i][j] = cmyk[2]*255.0f - 128.0f;
K[i][j] = cmyk[3]*255.0f - 128.0f;
}
}
}
我的問題是:給定大圖像時速度過慢。 在一種情況下,我花了大約104s而不是通常的2s來將數據寫成JPEG圖像。 事實證明,上述轉換是最耗時的部分。
我想知道是否有任何方法可以讓它更快。 注意:我不會使用可以從網上找到的廉價轉換算法。
更新 :根據haraldK的建議,以下是修訂版:
private void RGB2CMYK(int[] rgb, float[][] C, float[][] M, float[][] Y, float[][] K, int imageWidth, int imageHeight) throws Exception {
if(cmykColorSpace == null)
cmykColorSpace = new ICC_ColorSpace(ICC_Profile.getInstance(JPEGWriter.class.getResourceAsStream(pathToCMYKProfile)));
DataBuffer db = new DataBufferInt(rgb, rgb.length);
WritableRaster raster = Raster.createPackedRaster(db, imageWidth, imageHeight, imageWidth, new int[] {0x00ff0000, 0x0000ff00, 0x000000ff}, null);
ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorConvertOp cco = new ColorConvertOp(sRGB, cmykColorSpace, null);
WritableRaster cmykRaster = cco.filter(raster, null);
byte[] o = (byte[])cmykRaster.getDataElements(0, 0, imageWidth, imageHeight, null);
for(int i = 0, index = 0; i < imageHeight; i++) {
for(int j = 0; j < imageWidth; j++) {
C[i][j] = (o[index++]&0xff) - 128.0f;
M[i][j] = (o[index++]&0xff) - 128.0f;
Y[i][j] = (o[index++]&0xff) - 128.0f;
K[i][j] = (o[index++]&0xff) - 128.0f;
}
}
}
更新:我還發現在BufferedImage而不是Raster上進行過濾要快得多。 看到這篇文章: ARGB int數組到CMYKA字節數組轉換
你應該擺脫最內層循環中的內存分配。 new
是一個非常昂貴的操作。 此外,它可能會使垃圾收集器付諸行動,這進一步增加了惡意。
如果您可以使用內存消耗,則可以創建查找表:
private void RGB2CMYK(int[] rgb, float[][] C, float[][] M, float[][] Y, float[][] K, int imageWidth, int imageHeight) throws Exception {
ColorSpace cs = new ICC_ColorSpace(...);
int[] lookup = createRGB2CMYKLookup(cs);
for(int y = 0, index = 0; y < imageHeight; y++) {
for(int x = 0; x < imageWidth; x++, index++) {
int cmyk = lookup[rgb[index]];
C[y][x] = ((cmyk >> 24) & 255) - 128F;
M[y][x] = ((cmyk >> 16) & 255) - 128F;
Y[y][x] = ((cmyk >> 8) & 255) - 128F;
K[y][x] = ((cmyk ) & 255) - 128F;
}
}
}
static int[] createRGB2CMYKLookup(ColorSpace cs) {
int[] lookup = new int[16 << 20]; // eats 16m times 4 bytes = 64mb
float[] frgb = new float[3];
float fcmyk[];
for (int rgb=0; rgb<lookup.length; ++rgb) {
frgb[0] = ((rgb >> 16) & 255) / 255F;
frgb[1] = ((rgb >> 8) & 255) / 255F;
frgb[2] = ((rgb ) & 255) / 255F;
fcmyk = cs.fromRGB(frgb);
int c = (int) (fcmyk[0] * 255F);
int m = (int) (fcmyk[1] * 255F);
int y = (int) (fcmyk[2] * 255F);
int k = (int) (fcmyk[3] * 255F);
int icmyk = (c << 24) | (m << 16) | (y << 8) | k;
}
return lookup;
}
現在這實際上可能會使小圖像的性能惡化 。 只有在您可以為多個圖像重復使用查找表時,它才有用,但正如您的示例所示,您實際上反復使用相同的ICC配置文件。 因此,您可以緩存查找表並僅支付其初始化成本一次:
static int[] lookup;
static {
ColorSpace cs = new ICC_ColorSpace(...);
lookup = createRGB2CMYKLookup(cs);
}
// convert always using (the same) lookup table
private void RGB2CMYK(int[] rgb, float[][] C, float[][] M, float[][] Y, float[][] K, int imageWidth, int imageHeight) throws Exception {
for(int y = 0, index = 0; y < imageHeight; y++) {
for(int x = 0; x < imageWidth; x++, index++) {
int cmyk = lookup[rgb[index]];
C[y][x] = ((cmyk >> 24) & 255) - 128F;
M[y][x] = ((cmyk >> 16) & 255) - 128F;
Y[y][x] = ((cmyk >> 8) & 255) - 128F;
K[y][x] = ((cmyk ) & 255) - 128F;
}
}
}
您應該使用ColorConvertOp
。 它在大多數平台上使用優化的本機代碼,並支持ICC配置文件轉換。
不確定使用基於float
的Raster
時它的工作速度有多快,但它可以完成這項工作。
就像是:
ICC_Profile cmyk = ...;
ICC_Profile sRGB = ...;
ColorConvertOp cco = new ColorConvertOp(sRGB, cmyk);
Raster rgbRaster = ...;
WritableRaster cmykRaster = cco.filter(rgbRaster, null);
// Or alternatively, if you have a BufferedImage input
BufferedImage rgbImage = ...;
BufferedImage cmykImage = cco.filter(rgbImage, null);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.