Lookup Op производительность
Я пытаюсь увеличить производительность моего конвейера рендеринга. (Безусловно) самая медленная часть выполняет java.awt.imaging.LookupOp на большом изображении.
Размер изображения составляет около 2048x2048.
Я понял, что выполнение фильтра вместе с операцией рисования намного быстрее, чем вызов метода фильтра. Однако это все еще оставляет нас с операциями поиска, которые занимают приблизительно 250 мс (или 4 кадра в секунду). У кого-нибудь есть какие-нибудь советы по рендерингу?
Вот по сути то, что мы делаем:
public void paint(Graphics g)
{
if(recalcLUT)
{
Graphics2D g2d = (Graphics2D) displayImage.getGraphics();
g2d.drawImage(srcImage, lut, 0, 0);
}
Graphics2D g2d = (Graphics2D) g;
g2d.clearRect(0, 0, this.getWidth(), this.getHeight());
AffineTransform at = new AffineTransform();
at.setToIdentity();
at.scale(scale, scale);
g2d.drawImage(displayImage, at, null);
}
переменная lut - это LookupOp, обычно ShortLookupOp, изображение - 16-битное изображение в градациях серого.
Спасибо
Я знаю, что есть некоторые другие очевидные оптимизации производительности, которые можно сделать здесь. Но главная проблема - просто выполнить операцию LookupOp, поэтому я ищу совет относительно этого.
Операции Lookup - это, по сути, место, где вы создаете массив, и вместо того, чтобы отображать каждый пиксель изображения в качестве его цвета, вы используете цвет в качестве индекса в массиве и визуализируете цвет в качестве значения в индексе. В этом конкретном примере я использую его для выполнения некоторых простых операций яркости / контраста. Вы также можете реализовать это, используя rescaleOp, который по сути является способом применения линейной функции к значению всех пикселей. Но это оказывается медленнее.
2 ответа
Я не использовал java около восьми лет, поэтому некоторые из синтаксиса могут быть неактуальными.
Ключом к любой производительности в отношении цикла является выталкивание как можно большего количества вещей из цикла. Если вы не можете тогда выполнять вычисления, только когда они меняются. Часто лучше подождать до последней минуты, чтобы пересчитать, чтобы можно было кэшировать несколько изменений.
Переместите всю конструкцию объекта за пределы вашего цикла рендеринга. Если вы заранее знаете, сколько предметов вам нужно, передайте их; если вы не используете пул объектов и фабрика создает объекты за пределами renderloop. Это сэкономит вам время строительства / разрушения.
Рассчитайте свой AffineTransform только при изменении масштаба. Выдвиньте это за пределы цикла рисования и передайте его (как константную ссылку... они вообще существуют в Java? Я был в C++ слишком долго)
Вам может не понадобиться вызывать at.setToIdentity(), так как ваш AffineTransform должен по умолчанию использовать матрицу Identity (отметьте это)
Нужно ли вызывать recalcLUT каждый кадр? Имеет ли смысл устанавливать recalcLUT в false после выполнения оператора if?
Я не уверен, какова цель LookupOp. Если вы дадите мне немного больше информации о том, что он делает, я могу предложить больше.
Надеюсь, это поможет.
У вас есть возможность перенести эту обработку на графический процессор? Несколько аппаратных конвейеров значительно ускорят обработку.
Если вы не можете этого сделать, вам следует распараллелить LookupOp на процессоре. Из того, что я понимаю, каждый поиск может быть сделан отдельно. Поэтому вы можете раскручивать несколько потоков (сколько зависит от вашего CPU Arch и всего, что происходит одновременно), и каждый поток просматривает часть изображения. Вам понадобится семафор при каждом запуске обновления, чтобы дождаться завершения всех потоков.
В любом случае вы должны сделать следующее, чтобы компенсировать некоторое потребление памяти для каждого кадра, в котором нет изменений. Если ваша частота кадров не ограничена, то чем больше у вас кадров, тем больше шансов, что ничего не изменилось. Это избавит вас от ненужной работы.
//Has filter or source image changed?
// yes, recalculate filter and cache
// no, render cached image
Наконец, есть ли у вас представление о реализации LookupOp? Что он делает, есть ли какие-либо улучшения производительности, которые можно найти, написав реализацию самостоятельно (это должно быть последним средством).