Более быстрая альтернатива ColorConvertOp - PullRequest
10 голосов
/ 05 января 2012

У меня есть метод преобразования BufferedImages, тип которого TYPE_CUSTOM, в TYPE_INT_RGB.Я использую следующий код, однако я действительно хотел бы найти более быстрый способ сделать это.

BufferedImage newImg = new BufferedImage(
    src.getWidth(), 
    src.getHeight(), 
    BufferedImage.TYPE_INT_RGB);

ColorConvertOp op = new ColorConvertOp(null);
op.filter(src, newImg);

Он работает нормально, но довольно медленно, и мне интересно, есть ли более быстрый способ сделать этоэто преобразование.

ColorModel до преобразования:

ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace@1c92586f transparency = 1 has alpha = false isAlphaPre = false

ColorModel после преобразования:

DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0

Спасибо!


Обновление:

Оказалось, что работа с необработанными пиксельными данными была лучшим способом.Поскольку TYPE_CUSTOM на самом деле был RGB, преобразование вручную выполняется просто и примерно на 95% быстрее, чем ColorConvertOp.

public static BufferedImage makeCompatible(BufferedImage img) throws IOException {
    // Allocate the new image
    BufferedImage dstImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);

    // Check if the ColorSpace is RGB and the TransferType is BYTE. 
    // Otherwise this fast method does not work as expected
    ColorModel cm = img.getColorModel();
    if ( cm.getColorSpace().getType() == ColorSpace.TYPE_RGB && img.getRaster().getTransferType() == DataBuffer.TYPE_BYTE ) {
        //Allocate arrays
        int len = img.getWidth()*img.getHeight();
        byte[] src = new byte[len*3];
        int[] dst = new int[len];

        // Read the src image data into the array
        img.getRaster().getDataElements(0, 0, img.getWidth(), img.getHeight(), src);

        // Convert to INT_RGB
        int j = 0;
        for ( int i=0; i<len; i++ ) {
            dst[i] = (((int)src[j++] & 0xFF) << 16) | 
                     (((int)src[j++] & 0xFF) << 8) | 
                     (((int)src[j++] & 0xFF));
        }

        // Set the dst image data
        dstImage.getRaster().setDataElements(0, 0, img.getWidth(), img.getHeight(), dst);

        return dstImage;
    }

    ColorConvertOp op = new ColorConvertOp(null);
    op.filter(img, dstImage);

    return dstImage;
}

Ответы [ 6 ]

7 голосов
/ 15 января 2012

BufferedImages мучительно медленно. У меня есть решение, но я не уверен, что оно вам понравится. Самый быстрый способ обработки и преобразования буферизованных изображений - извлечь массив необработанных данных из BufferedImage. Вы делаете это, вызывая buffImg.getRaster () и преобразовывая его в определенный растр. Затем вызовите raster.getDataStorage (). Если у вас есть доступ к необработанным данным, вы можете написать код быстрой обработки изображений без всякой абстракции в BufferedImages, замедляющей его. Этот метод также требует глубокого понимания форматов изображений и некоторого обратного инжиниринга с вашей стороны. Это единственный способ заставить код обработки изображений работать достаточно быстро для моих приложений.

Пример:

ByteInterleavedRaster srcRaster = (ByteInterleavedRaster)src.getRaster();
byte srcData[] = srcRaster.getDataStorage();

IntegerInterleavedRaster dstRaster = (IntegerInterleavedRaster)dst.getRaster();
int dstData[] = dstRaster.getDataStorage();

dstData[0] = srcData[0] << 16 | srcData[1] << 8 | srcData[2];

или что-то в этом роде. Ожидайте ошибки компилятора, предупреждающие, что вы не будете иметь доступ к низкоуровневым растрам. Единственное место, где у меня были проблемы с этой техникой - это внутри апплетов, где будет происходить нарушение доступа.

3 голосов
/ 27 августа 2014

Я обнаружил, что рендеринг с использованием Graphics.drawImage () вместо ColorConvertOp в 50 раз быстрее. Я могу только предположить, что drawImage () ускоряется GPU.

то есть это действительно медленно, например, 50 мс для 100х200 прямоугольников

public void BufferdImage convert(BufferedImage input) {
   BufferedImage output= new BufferedImage(input.getWidht(), input.getHeight(), BufferedImage.TYPE_BYTE_BINARY, CUSTOM_PALETTE);

   ColorConvertOp op = new ColorConvertOp(input.getColorModel().getColorSpace(), 
                                          output.getColorModel().getColorSpace());

   op.filter(input, output);
   return output;
}

то есть, однако, это регистрирует <1 мс для тех же самых входов </p>

public void BufferdImage convert(BufferedImage input) {
   BufferedImage output= new BufferedImage(input.getWidht(), input.getHeight(), BufferedImage.TYPE_BYTE_BINARY, CUSTOM_PALETTE);

   Graphics graphics = output.getGraphics();
   graphics.drawImage(input, 0, 0, null);
   graphics.dispose();
   return output;
}
0 голосов
/ 15 января 2012

возможно попробуйте это:

Bitmap source = Bitmap.create(width, height, RGB_565);//don't remember exactly...
Canvas c = new Canvas(source);
// then 
c.draw(bitmap, 0, 0);

Тогда исходное изображение будет изменено.

Позже вы можете сделать:

onDraw(Canvas canvas){
canvas.draw(source, rectSrs,rectDestination, op);
}

если вам удастся справиться, всегда используйте растровое изображение, чтобы повысить производительность. Также вы можете использовать другие функции холста для рисования своего растрового изображения

0 голосов
/ 12 января 2012

Если у вас установлен JAI, вы можете попытаться удалить его, если можете, или иным образом найти способ отключить codecLib при загрузке JPEG.В прошлой жизни у меня были похожие проблемы (http://www.java.net/node/660804) и ColorConvertOp был самым быстрым в то время.

Насколько я помню, фундаментальная проблема заключается в том, что Java2D вообще не оптимизирован для образов TYPE_CUSTOM в целом. При установкеJAI поставляется с codecLib, у которого есть декодер, который возвращает TYPE_CUSTOM и используется вместо значения по умолчанию. Список JAI может предоставить дополнительную помощь, это уже несколько лет.

0 голосов
/ 09 января 2012

Я подозреваю, что проблема может заключаться в том, что ColorConvertOp () работает попиксельно (гарантированно "медленный").

Q: Возможно ли использовать gc.createCompatibleImage () ?

Q: Ваш исходный растр имеет истинный цвет или использует цветовую карту?

Q: Если все остальное не получится, вы согласитесь написать интерфейс JNI?Либо для вашего собственного пользовательского кода C, либо для внешней библиотеки, такой как ImageMagick ?

0 голосов
/ 05 января 2012

Вы пытались предоставить какие-либо RenderingHints ? Без гарантий, но с использованием

ColorConvertOp op = new ColorConvertOp(new RenderingHints(
    RenderingHints.KEY_COLOR_RENDERING, 
    RenderingHints.VALUE_COLOR_RENDER_SPEED));

вместо null в вашем фрагменте кода может несколько ускорить его.

...