Более быстрая альтернатива ColorConvertOp
У меня есть метод преобразования 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 Before Conversion:
ColorModel: #pixelBits = 24 numComponents = 3 color space = [email protected] transparency = 1 has alpha = false isAlphaPre = false
ColorModel After Conversion:
DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0
Спасибо!
Update:
Оказалось, что работа с необработанными пиксельными данными была наилучшим способом. Поскольку 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;
}
Ответы
Ответ 1
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];
или что-то в этом роде. Ожидайте ошибки компилятора, предупреждающие вас не обращаться к низкоуровневым растрам. Единственное место, где у меня были проблемы с этой техникой, - это внутри апплетов, где произойдет нарушение прав доступа.
Ответ 2
Я нашел рендеринг с использованием Graphics.drawImage() вместо ColorConvertOp в 50 раз быстрее. Я могу только предположить, что drawImage() ускорен GPU.
i.e это очень медленно, например 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;
}
i.e однако это регистрирует < 1 мс для тех же входов
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;
}
Ответ 3
Попробовали ли вы предоставить RenderingHints? Нет гарантий, но используя
ColorConvertOp op = new ColorConvertOp(new RenderingHints(
RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.VALUE_COLOR_RENDER_SPEED));
а не null
в фрагменте кода может немного ускорить его.
Ответ 4
Я подозреваю, что проблема может заключаться в том, что ColorConvertOp() работает по пикселям (гарантируется "медленный" ).
Q: Возможно ли использовать gc.createCompatibleImage()?
В: Является ли ваш исходный растровый истинный цвет, или он использует цветную карту?
В: В противном случае вы согласитесь написать интерфейс JNI? Либо ваш собственный, пользовательский код C, либо внешнюю библиотеку, такую как ImageMagick?
Ответ 5
Если у вас установлен JAI, вы можете попробовать удалить его, если хотите, или каким-либо другим способом отключить codecLib при загрузке JPEG. В прошлой жизни у меня были подобные проблемы (http://www.java.net/ node/660804), а ColorConvertOp был самым быстрым в то время.
Насколько я помню, основная проблема заключается в том, что Java2D вообще не оптимизирован для изображений TYPE_CUSTOM. Когда вы устанавливаете JAI, он поставляется с кодеком, который имеет декодер, который возвращает TYPE_CUSTOM и используется вместо значения по умолчанию. Список JAI может предоставить дополнительную помощь, это было несколько лет.
Ответ 6
возможно, попробуйте следующее:
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);
}
если вы можете управлять всегда повторно использовать растровое изображение, чтобы вы могли получить лучшую производительность. Кроме того, вы можете использовать другие функции холста для рисования растрового изображения