簡體   English   中英

任何更快的從RGB轉換為CMYK的算法

[英]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配置文件轉換。

不確定使用基於floatRaster時它的工作速度有多快,但它可以完成這項工作。

就像是:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM