Оптимизация производительности GrabCut в opencv-java
Недавно мне был предоставлен проект, в котором мне нужно извлечь лицо (лицо + волосы) из заданного изображения.
Я решаю эту проблему следующими способами.
- Я извлекаю места для лица из заданного изображения. [Я получаю прямоугольник]
- Я извлекаю этот прямоугольник и помещаю его в другое изображение того же размера, что и входное изображение. [face_image]
- Я применяю алгоритм grabCut на face_image шага 2.
Когда face_image содержит плавный фон, тогда алгоритм grabCut работает хорошо, но когда фон face_image является сложным, тогда алгоритм grabCut извлекает часть фона в обработанное изображение.
Вот моментальный снимок результатов, которые я получаю.
![results of each step]()
Вот мой код grabCut:
public void extractFace(Mat image, String fileNameWithCompletePath,
int xOne, int xTwo, int yOne, int yTwo) throws CvException {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Rect rectangle = new Rect(xOne, yOne, xTwo, yTwo);
Mat result = new Mat();
Mat bgdModel = new Mat();
Mat fgdModel = new Mat();
Mat source = new Mat(1, 1, CvType.CV_8U, new Scalar(3));
Imgproc.grabCut(image, result, rectangle, bgdModel, fgdModel, 8, Imgproc.GC_INIT_WITH_RECT);
Core.compare(result, source, result, Core.CMP_EQ);
Mat foreground = new Mat(image.size(), CvType.CV_8UC3, new Scalar(255, 255, 255));
image.copyTo(foreground, result);
Imgcodecs.imwrite(fileNameWithCompletePath, foreground);
}
Как повысить производительность алгоритма grabCut, чтобы он обнаруживал только лицо и волосы с данного изображения?
Ответы
Ответ 1
Вы должны быть в состоянии сделать это, помогая grabCut
немного узнать о переднем плане и фоне. Существует учебник python, который показывает, как это делается вручную, выбирая передний план и фон.
Чтобы сделать это автоматически, вам нужно будет найти программные способы обнаружения переднего плана и фона. Передний план состоит в основном из волос и кожи, поэтому вам нужно их обнаружить.
Кожа - есть несколько статей и блоги о том, как это сделать, Некоторые из них довольно простые и этот учебник OpenCV также может помочь. Я нашел чистый оттенок/насыщенность, чтобы доставить меня довольно далеко.
Волосы - это сложнее, но определенно все еще выполнимо. Вы можете быть в состоянии волос и просто использовать кожу и фон, если это окажется слишком много работы.
Фон. Вы можете использовать range()
, чтобы найти на изображении фиолетовые, зеленые и синие объекты. Вы точно знаете, что эти вещи не являются кожей или волосами и поэтому являются частью фона.
Используйте пороговое значение, чтобы создать маску областей, наиболее вероятных для кожи, волос и фона. Затем вы можете использовать их как bgdModel
и fgdModel
(или маски для кожи и волос) вместо Mat()
.
Извините, это настолько высокий уровень. Надеюсь, это поможет!
Ответ 2
Другой подход, поскольку вы уже обнаружили лицо, состоит в том, чтобы просто выбрать лучшую начальную маску для инициализации GrabCut - например, используя овал вместо прямоугольника.
- Обнаружение прямоугольника лица (как вы уже делаете)
-
Создайте маску:
a) Создайте новое черное изображение того же размера, что и ваше входное изображение
b) Нарисуйте эллипс с белым заполнением с одинаковыми по высоте, ширине, верхнему и левому положениям в качестве прямоугольника лица
-
Вызовите GrabCut с помощью GC_INIT_WITH_MASK
вместо GC_INIT_WITH_RECT
:
Imgproc.grabCut(image, mask, rectangle, bgdModel, fgdModel, 8, Imgproc.GC_INIT_WITH_MASK);
Это инициализирует передний план лучшей моделью, потому что грани более овальной формы, чем прямоугольные, поэтому для начала нужно включить меньше фона.
Ответ 3
Я предлагаю "играть" с координатами прямоугольника (int xOne, int xTwo, int yOne, int yTwo)
. Используя ваш код и эти координаты 1, 400, 30, 400, я смог избежать фона. (Я попытался опубликовать изображения, которые я успешно обрезал, но для этого мне нужна репутация не менее 10)
Ответ 4
Лучшая оптимизация, которая может быть выполнена для любой Java-процедуры, - это преобразование на родной язык.