Как повысить производительность отрисовки BufferedImage, созданного ComponentColorModel на языке Java?

Я разрабатываю приложение Java, которое захватывает видео с устройства захвата кадров dvi. Я хочу визуализировать кадры, которые я получаю от этого фреймграббера, в реальном времени. Но столкнулся с проблемой производительности - рендеринг был очень медленным, мой компьютер успевал рендерить всего 5-6 кадров в секунду. Хотя устройство могло снимать до 40 кадров в секунду. Я профилировал свой код и обнаружил, что метод drawImage работает относительно медленно. Если я вызывал метод drawImage с BMP-изображением, загружаемым через ImageIO.readImage, то для его отрисовки с помощью drawImage требовалось 20 мс. С изображениями, которые я получил из фреймграббера, их отрисовка занимает более 100 мс. Я изучил код библиотеки фреймграббера и обнаружил, что BufferedImage создается следующим образом —

    ColorModel cm;
    if (format == PixelFormat.RGB24) {
       cm = new ComponentColorModel(
            ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {8,8,8},
            false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
    } else {
        throw new UnsupportedOperationException();
    }
    SampleModel sm = cm.createCompatibleSampleModel(width, height);
    DataBuffer db = new DataBufferByte(pixels, length);
    WritableRaster raster = Raster.createWritableRaster(sm, db, null);
    return new BufferedImage(cm, raster, false, null);

пиксели — это массив байтов, предоставляемый фреймграббером. Есть ли способ создать BufferedImage по-другому, чтобы ускорить вызов метода drawImage. Я знаю о ColorSpaces, мне не нужно сохранять правильное цветовое пространство. Скорость важнее.

заранее спасибо


person Rob Yart    schedule 28.07.2012    source источник


Ответы (1)


У меня была проблема с реализацией jai tiff для API ImageIO, которая звучит похоже (я не знаю, поможет это или нет).

По сути, он преобразует цветовую модель в «образцовую модель, упакованную в один пиксель»: P

Это не мой код, я не беру на себя ответственность за него, я нашел его в сети некоторое время назад, и, боюсь, я не помню, где (я пытался найти его, но не нашел подходящей ссылки.

/*******************************************************************************
 *
 * It seems that SinglePixelPackedSampleModel is the only fast mode when a
 * color profile is converted. This is probably a bug (that has nothing to do
 * with bugs 4886071 and 4705399).
 * Note that grayscale images (TYPE_GRAY) are not converted.
 *
 ******************************************************************************/
public static BufferedImage convertToSinglePixelPackedSampleModel(BufferedImage image) {

    long time = System.currentTimeMillis();

    WritableRaster sourceRaster = image.getRaster();
    ColorModel colorModel = image.getColorModel();
    ICC_ColorSpace colorSpace = (ICC_ColorSpace) colorModel.getColorSpace();
    final SampleModel ssmd = sourceRaster.getSampleModel();

    if (colorSpace.getType() == ColorSpace.TYPE_GRAY) {

        logger.info(">>>> TYPE_GRAY, not converting");

    } else if (!(ssmd instanceof PixelInterleavedSampleModel)) {

        logger.info(">>>> sourceSampleModel is " + ssmd.getClass() + ", not converting");

    } else {

        PixelInterleavedSampleModel sourceSampleModel = (PixelInterleavedSampleModel) ssmd;
        int[] bitMasks = new int[]{0x00ff0000, 0x0000ff00, 0x000000ff};

        SinglePixelPackedSampleModel sampleModel = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, image.getWidth(),
                image.getHeight(), bitMasks);

        WritableRaster destRaster = Raster.createWritableRaster(sampleModel, null);
        DataBufferInt destDataBuffer = (DataBufferInt) destRaster.getDataBuffer();
        int[] destBuffer = destDataBuffer.getData();
        int[] bandOffsets = sourceSampleModel.getBandOffsets();

        for (int i = 0; i < bandOffsets.length; i++) {
            bandOffsets[i] += ((-sourceRaster.getSampleModelTranslateX() * sourceSampleModel.getPixelStride())
                    - (sourceRaster.getSampleModelTranslateY() * sourceSampleModel.getScanlineStride()));
        }

        DataBuffer sourceDataBuffer = sourceRaster.getDataBuffer();

        if (sourceDataBuffer instanceof DataBufferUShort) {

            convertUShortDataBuffer(image, (DataBufferUShort) sourceDataBuffer, sourceSampleModel, bandOffsets, destBuffer);

        } else if (sourceDataBuffer instanceof DataBufferByte) {

            convertByteDataBuffer(image, (DataBufferByte) sourceDataBuffer, sourceSampleModel, bandOffsets, destBuffer);

        } else {

            throw new IllegalArgumentException("Cannot deal with " + sourceDataBuffer.getClass());

        }

        String sourceProfileName = getICCProfileName(colorSpace.getProfile());

        if (sourceProfileName.equals("Nikon sRGB 4.0.0.3001")) {
            logger.warn(">>>> Workaround #1094403: using sRGB instead of " + sourceProfileName);
            colorSpace = new ICC_ColorSpace(ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB));
        }

        colorModel = new DirectColorModel(colorSpace, 24, bitMasks[0], bitMasks[1], bitMasks[2], 0, false, DataBuffer.TYPE_INT);
        image = new BufferedImage(colorModel, destRaster, false, null);
    }

    time = System.currentTimeMillis() - time;
    logger.info(">>>> convertToSinglePixelPackedSampleModel() completed ok in " + time + " msec");

    return image;
}

/**
 * @param image
 * @param sourceDataBuffer
 * @param sourceSampleModel
 * @param bandOffsets
 * @param destBuffer
 */
protected static void convertByteDataBuffer(BufferedImage image, DataBufferByte sourceDataBuffer,
        PixelInterleavedSampleModel sourceSampleModel, int[] bandOffsets, int[] destBuffer) {
    int base = 0;
    int i = 0;
    byte[] sourceBuffer = sourceDataBuffer.getData();
    int pixelStride = sourceSampleModel.getPixelStride();

    for (int y = 0; y < image.getHeight(); y++) {
        int j = base;

        for (int x = 0; x < image.getWidth(); x++) {
            int r = (sourceBuffer[j + bandOffsets[0]] & 0xff);
            int g = (sourceBuffer[j + bandOffsets[1]] & 0xff);
            int b = (sourceBuffer[j + bandOffsets[2]] & 0xff);

            destBuffer[i++] = (r << 16) | (g << 8) | b;
            j += pixelStride;
        }

        base += sourceSampleModel.getScanlineStride();
    }
}

protected static void convertUShortDataBuffer(BufferedImage image, DataBufferUShort sourceDataBuffer,
        PixelInterleavedSampleModel sourceSampleModel, int[] bandOffsets, int[] destBuffer) {
    int base = 0;
    int i = 0;
    short[] sourceBuffer = sourceDataBuffer.getData();

    for (int y = 0; y < image.getHeight(); y++) {
        int j = base;

        for (int x = 0; x < image.getWidth(); x++) {
            int r = (sourceBuffer[j + bandOffsets[0]] & 0xffff) >> 8;
            int g = (sourceBuffer[j + bandOffsets[1]] & 0xffff) >> 8;
            int b = (sourceBuffer[j + bandOffsets[2]] & 0xffff) >> 8;

            destBuffer[i++] = (r << 16) | (g << 8) | b;
            j += 3;
        }

        base += sourceSampleModel.getScanlineStride();
    }
}

//    public static ICC_Profile getICCProfile(RenderedImage image) {
//
//        ColorSpace colorSpace = image.getColorModel().getColorSpace();
//
//        if (colorSpace instanceof ICC_ColorSpace) {
//
//            ICC_ColorSpace iccColorSpace = (ICC_ColorSpace) colorSpace;
//
//            return iccColorSpace.getProfile();
//
//        }
//
//        return null;
//
//    }

public static String getICCProfileName(ICC_Profile profile) {

    if (profile == null) {
        return null;
    }

    byte[] xx = profile.getData(ICC_Profile.icSigProfileDescriptionTag);
    int offset = 12;
    int count;

    for (count = 1; xx[offset + count] != 0; count++) {
        ;
    }

    return new String(xx, 0, offset, count);

}

По сути, просто позвоните .convertToSinglePixelPackedSampleModel(image).

Я сократил время рендеринга (относительно большого изображения TIFF) с минут до менее чем нескольких секунд: P

ps - я думаю, что здесь я нашел исходный код http://www.koders.com/java/fidFE1D69AFE6930A514D5E189310AB10A3DFD43F78.aspx

person MadProgrammer    schedule 29.07.2012
comment
У меня была аналогичная проблема. Буферизированное изображение рисовалось очень медленно. Это решило это. - person Codey McCodeface; 13.01.2014