Преобразование RGB в серый с моей собственной формулой в OpenCV

Я хотел бы преобразовать изображение RGB в серый цвет, как это делает эта функция:

Imgproc.cvtColor(mRgba, mGrey, Imgproc.COLOR_RGBA2GRAY);

Но openCV использует конкретную формулу (0,299 * R + 0,587 * G + 0,114 * B, я думаю?), и вместо этого я хотел бы использовать min (R, G, B).

Есть ли эффективный способ сделать это? (Это OpenCV для Android, поэтому с Java)

ИЗМЕНИТЬ:

Вот профилирование, которое я определенно должен был сделать раньше:

Базовое приложение для захвата камеры работает со скоростью 12 кадров в секунду.

  • с cvtColor() OpenCV: 12 FPS (разница в скорости незначительна)
  • с ответом Марка Миллера: 1,5 FPS
  • с ответом Марка Миллера и оптимизацией Мики (один раз получить указатель данных): 7 FPS

Это может показаться невероятным, но только эта маленькая функция стоит половину FPS. Устройство все еще может выполнять очень сложные вещи, такие как ORB, с приличной скоростью (3-4 FPS) и все подпрограммы O (n) C ++ без разницы в FPS. Также только «уловка» Мики делает функцию в 10 раз быстрее.


person Saiph    schedule 13.07.2015    source источник
comment
вам нужно преобразовать из RGB в оттенки серого ???   -  person Parth Bhayani    schedule 13.07.2015
comment
да, но я также хотел бы указать openCV формулу, которую он должен использовать при преобразовании   -  person Saiph    schedule 13.07.2015
comment
для этого вы импортировали эту библиотеку openCV в свой проект?   -  person Parth Bhayani    schedule 13.07.2015
comment
Есть ли эффективный способ сделать это? - Нет. из java этого не сделать, и даже с помощью jni это будет очень тяжелая работа.   -  person berak    schedule 13.07.2015
comment
@Parth Bhayani Нет, весь мой проект основан на openCV. (я добавил openCV в заголовок, извините)   -  person Saiph    schedule 13.07.2015
comment
Но если ваш проект основан на openCV, для этого вам нужно добавить эту библиотеку в свое рабочее пространство, а также добавить в свой проект.   -  person Parth Bhayani    schedule 13.07.2015
comment
Да, я сделал это, и это хорошо работает для моего проекта.   -  person Saiph    schedule 13.07.2015
comment
Правда, @berak? split и два вызова min не помогут? Я поверю вам на слово, так как я не использовал OpenCV в Java, только в C++.   -  person beaker    schedule 13.07.2015
comment
Ну, если вы говорите о производительности, вероятно, java не лучший выбор. Однако какое у вас эталонное изображение? сколько времени занимает CvtColor? И с наивной реализацией вашей модифицированной функции (которую вы должны опубликовать, кстати)? Без такой информации ваш вопрос бессмыслен.   -  person Miki    schedule 13.07.2015
comment
Я просто спрашиваю, как правильно что-то сделать (потому что мне показалось вероятным, что openCV разрешил это напрямую). Как это бессмысленно?   -  person Saiph    schedule 14.07.2015
comment
Я считаю, что Мики фокусируется на производительности. Без базового уровня того, что считается эффективным, на вопрос нельзя ответить, потому что существует спектр ответов на разных уровнях по шкале удобочитаемости и скорости. Я дал самый простой ответ, потому что в большинстве случаев выполняется правило №1 оптимизации.   -  person Mark Miller    schedule 14.07.2015
comment
Да, я имел в виду более эффективное, чем написание циклов на Java, которое, как я предполагал, будет очень медленным по сравнению с вызовом нативного метода (но на самом деле я могу ошибаться?). Спасибо за ответ и комментарии тоже   -  person Saiph    schedule 15.07.2015


Ответы (1)


Нет встроенного способа дать OpenCV произвольную функцию для преобразования в оттенки серого. Оставшийся вариант — использование самих каналов RGB.

for (int x = 0; x < mRgba.cols(); x++)
{
    for (int y = 0; y < mRgba.rows(); y++)
    {
        double[] rgb = mRgba.get(x, y);
        mGray.put(x, y, Math.min(rgb[0], Math.min(rgb[1], rgb[2])));
    }
}

Как вы упомянули, это может быть слишком медленным, особенно с большими изображениями и ветвлением, которое исходит от операторов if. Однако есть три правила оптимизации: (1) не делать этого, (2) пока не делать этого и (3) сначала профилировать. Проще говоря, сначала попробуйте версию с простым кодом, а затем, как только вы точно узнаете, что этот раздел является медленным [т. е. запустив его и синхронизировав его], измените код, чтобы он был более эффективным.

person Mark Miller    schedule 13.07.2015
comment
вы получили мой -1 за код, но +1 за соображения оптимизации. Так что 0 :D - person Miki; 13.07.2015
comment
@Miki Что плохого в коде? Это на Java, и это медленно? Правки и комментарии приветствуются. - person Mark Miller; 13.07.2015
comment
ну, по крайней мере, инвертируйте внутренний и внешний цикл (чтобы вы могли последовательно получать доступ к данным в оперативной памяти и оптимизировать кеш), я не очень хорошо разбираюсь в том, как работает Java, но если бы это было на С++, я бы взял указатель каждую строку и работать над указателями. Я не знаю, как это сделать на Java (поэтому я не опубликовал свой ответ). Я полностью согласен с вами по поводу оптимизации, как вы можете видеть из моего комментария к вопросу. - person Miki; 13.07.2015
comment
ах, это должно помочь получить указатель на данные Mat fv; ... ; int size = (int) fv.total() * fv.channels(); double[] buff = new double[size]; fv.get(0, 0, buff); - person Miki; 13.07.2015
comment
@Miki, этот трюк потрясающий, он делает функцию как минимум в 10 раз быстрее. Наконец, я не использовал min, потому что он все еще был слишком медленным, но применение этого трюка в других частях моего кода сделало их намного быстрее. - person Saiph; 13.09.2015
comment
@Saiph инвертирует также циклы for, вы должны получить отличное ускорение. - person Miki; 13.09.2015