Почему каждый 2 ряд пикселей в bitmap белого цвета при конвертировании BGR -> RGBA?
UPD. Решение все ещё не найдено, а в комментариях, можно сказать, одна вода, которая не даёт чёткого исправления проблемы. В комментах пишут, мол из-за целочисленного деления я получаю такой результат. Если я уберу деление на 4, то почему тогда я получаю аналогичное изображение с перезаписанными пикселями? Что интересно, в реализации java по ссылке, которой я прикрепил - там нет деления на 4 и результат отображается корректный. Я пока не понял, почему деление на 4 в c++ дало мне масштаб картинки 1 к 1, а в java без него тот же масштаб. Если в плюсах не писать деление на 4 - тогда я получаю только четверть картинки, и тоже с белыми линиями.
UPD2. Решение найдено. Но я просто хочу понять - почему в плюсах мы делим rowBytes на 4, а в java в том примере по ссылке, которой я прикрепил - там нет деления, там только +4. При этом результаты одинаковы. В чем подвох, или где я что упустил?
Мне нужно преобразовать bgr-массив в rgb-массив. Я бы рад воспользоваться реализацией из существующих библиотек, но они слишком медленные для моего проекта.
У меня есть собственная jni-функция, написанная на c++, которая конвертирует массив, а быть точнее Frame из FFmpegFrameGrabber, который будет записан после конвертации в Bitmap. Я из этого пакета только забираю оттуда:
(ByteBuffer) frame.image[0]; // здесь кадр в bgr-формате
Я знаю, что в opencv и javacv существуют разные конвертеры, но для моего проекта очень важна производительность, так как кадр должен будет попасть на мою нейронку. Поэтому конвертирование в матрицу, а потом конвертирование через Utils.matToBitmap или AndroidFrameConverter, у которого задержка составялет ~ 50 ms меня не устраивает. Хотя нативный метод matToBitmap очень быстрый, но он не подходит, потому что конвертирование javacv Mat в opencvMat занимает ~ 100 ms, что очень много. cvtColor не подходит по аналогичной проблеме, потому что принимает на вход матрицу.
Собственный конвертер на c++ я писал исходя AndroidFrameConverter из метода:
ByteBuffer bgr2rgba(ByteBuffer in, int width, int height, int stride, int rowBytes{...}
Писал конвертер только для ARGB_8888, то есть 4 байта на пиксель. Сейчас мой код затрачивает на конвертацию ~8-15 ms. Но я не понимаю, где я упустил часть кода, что каждый 2 ряд пикселей у Bitmap не отображаются. Так как я не силен в плюсах, поэтому собрал вот такой код:
#include <jni.h>
#include <endian.h>
extern "C"
JNIEXPORT jbyteArray JNICALL Java_com_example_testcpp_MainActivity_frameToBitmap(JNIEnv *env, jobject thiz, jobject in,
jint width, jint height, jint imageStride,
jint rowBytes) {
auto* buf = (uint8_t*) env->GetDirectBufferAddress(in);
int* new_buff = new int [4*width*height];
int _width = width - 1;
int _height = height - 1;
for (int y = 0; y < height; y++) {
int ro = y * imageStride;
for (int x = 0; x < width; x++) {
int rgb;
if (x < _width || y < _height) {
rgb = *(int*)(buf + y * imageStride + 3 * x);
} else {
int b = buf[ro + 3 * x ] & 0xff;
int g = buf[ro + 3 * x + 1] & 0xff;
int r = buf[ro + 3 * x + 2] & 0xff;
rgb = (r << 16) | (g << 8) | b;
}
new_buff[y / 4 * rowBytes + x] = htobe32((rgb << 8) | 0xff);
}
}
jbyteArray jByteArray = env->NewByteArray((jsize)4*width*height);
env->SetByteArrayRegion(jByteArray, 0, 4*width*height, reinterpret_cast<const jbyte*>(new_buff));
delete[] new_buff;
return jByteArray;
}
Вот какое изображение на выходе:

Где я допустил ошибку в c++ коде??
java вызов функции:
Frame frame = grabber.grabImage();
ByteBuffer in = (ByteBuffer)frame.image[0];
Bitmap bmp = Bitmap.createBitmap(frame.imageWidth, frame.imageHeight, Bitmap.Config.ARGB_8888);
byte [] data = frameToBitmap(in, frame.imageWidth, frame.imageHeight, frame.imageStride, bmp.getRowBytes());
bmp.copyPixelsFromBuffer(ByteBuffer.wrap(data).position(0));
Может кто-то знает быстрое преобразование javacv Mat в opencv Mat и затем конвертирование через быстрый нативный метод Utils.matToBitmap, чтобы не создавать свою jni-функцию? Было бы неплохо услышать еще, как можно оптимизировать и ускорить данный код.
Ответы (1 шт):
В коде по ссылке вычисляется адрес в буфере (в байтах), по которому записывается int, здесь у вас интовый массив, индекс в котором в 4 раза меньше.
При использовании целочисленного деления, как написали в комментариях, результат получается не тот, что вы ожидали, т.к. 0/4 = 1/4 = 2/4 = 3/4 = 0. Так работает целочисленное деление и в С, и в Java.
Для получения правильного результата в данном случае достаточно сначала выполнить умножение, потом уже делить:
new_buff[y * rowBytes / 4 + x] =