BufferedImage меняет местами красный и синий канал

Моя цель - поменять местами красный и синий каналы Java BufferedImage.

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

Любая помощь приветствуется.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
415
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Возможно, вы создадите новый экземпляр BufferedImage с тем же WritableRaster raster и теми же свойствами, но с ColorModel с поменяемыми местами цветов:

например:

ColorModel swappedColorModel = new DirectColorModel(24,
                                              0x000000ff,   // Red -> Blue
                                              0x0000ff00,   // Green
                                              0x00ff0000,   // Blue -> Red
                                              0x0           // Alpha
                                              );
BufferedImage swapedColorImage = new BufferedImage (swappedColorModel,
                                                    originalImage.getRaster(),
                                                    swappedColorModel.isAlphaPremultiplied(),
                                                    properties);

Я не пробовал этот код

Если вы не писали видео, можете ли вы дать ссылку на источник решения?

MadProgrammer 04.11.2018 03:05

У меня это не сработало. Он вылетает, за исключением того, что я не могу использовать цветовую модель с моим изображением. Изображение в формате 3BYTE_BGR. Может быть, проблема с несуществующим альфа-каналом?

David Bauer 04.11.2018 10:29

@DavidBauer Я думаю, что идея правильная (и быстрая!), Однако для TYPE_3BYTE_BGR вам нужен ComponentColorModel, а не DirectColorModel (который работает с TYPE_INT_*BufferedImages). Однако это не поменяет местами каналы данных, а только то, как они отображаются. Ваш вопрос неоднозначен относительно того, нормально это или нет.

Harald K 04.11.2018 13:27

Ничего страшного, если конечный результат будет таким же. Я просто хочу поменять местами интенсивность красного и синего в изображении. Как это достигается, не имеет большого значения. Собираюсь попробовать ComponentColorModel как можно скорее

David Bauer 04.11.2018 21:09

ComponentColorModel сделал свое дело. Мне также пришлось изменить растр, который использовался в BufferedImage. Мне тоже очень помог этот пост: stackoverflow.com/questions/24639986/…

David Bauer 04.11.2018 22:03

@haraldK, не могли бы вы опубликовать свой ответ здесь, чтобы я мог проголосовать за него

David Bauer 04.11.2018 22:05
Ответ принят как подходящий

Вот одно решение, очень быстрое, поскольку оно не изменяет данные, а только способ их отображения.

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

Если у вас уже есть BufferedImage, самый простой способ создать образец модели с переставленными каналами - это создать новый дочерний Raster с помощью метода Raster.createWritableChild(...) и указать порядок каналов (или «полос») в последнем параметре.

bgr.getRaster().createWritableChild(0, 0, bgr.getWidth(), bgr.getHeight(), 0, 0, 
                                    new int[]{2, 1, 0}); // default order is 0, 1, 2

В приведенном ниже примере данные изображения те же (в случае сомнений попробуйте переместить часть рисования после клонирования изображения и убедитесь, что результат такой же). Меняются только каналы:

public static void main(String[] args) {
    // Original
    final BufferedImage bgr = new BufferedImage(100, 100, BufferedImage.TYPE_3BYTE_BGR);

    // Paint something
    Graphics2D graphics = bgr.createGraphics();
    try {
        graphics.setColor(Color.BLUE);
        graphics.fillRect(0, 0, bgr.getWidth(), bgr.getHeight());
        graphics.setColor(Color.YELLOW);
        graphics.fillRect(0, 0, bgr.getWidth(), bgr.getHeight() / 3);
        graphics.setColor(Color.GREEN);
        graphics.fillRect(0, 0, bgr.getWidth() / 3, bgr.getHeight());
    }
    finally {
        graphics.dispose();
    }

    // Clone, and swap BGR -> RGB
    ColorModel colorModel = bgr.getColorModel();
    WritableRaster swapped = bgr.getRaster().createWritableChild(0, 0, bgr.getWidth(), bgr.getHeight(), 0, 0, 
                                                                 new int[]{2, 1, 0}); // default order is 0, 1, 2
    final BufferedImage rgb = new BufferedImage(colorModel, swapped, colorModel.isAlphaPremultiplied(), null);

    System.err.println("bgr: " + bgr); // TYPE_3BYTE_BGR (5)
    System.err.println("rgb: " + rgb); // TYPE_CUSTOM (0)

    // Display it all
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

            frame.add(new JLabel(new ImageIcon(bgr)), BorderLayout.WEST);
            frame.add(new JLabel(new ImageIcon(rgb)));

            frame.pack();
            frame.setLocationRelativeTo(null);

            frame.setVisible(true);
        }
    });
}

PS: Я знаю из комментариев, что OP в этом не нуждается, но если вам действительно нужно поменять местами каналы пиксельных данных по какой-то причине (например, требуется нативная библиотека или около того), самым быстрым, вероятно, было бы получить data, выполните цикл и поменяйте местами красный и синий (1-й и 3-й) компоненты:

byte[] data = ((DataBufferByte) bgr.getRaster().getDataBuffer()).getData();
for (int i = 0; i < data.length; i += 3) {
    // Swap 1st and 3rd component
    byte b = data[i];
    data[i] = data[i + 2];
    data[i + 2] = b;
 }

Другие вопросы по теме