Fotomontajes con OpenCV

Una de la operaciones que comúnmente realizamos con aplicaciones editoras de imágenes son los fotomontajes, tomar una porción de cierta imagen y luego pegarla en otra imagen, lógicamente y solo copiamos y pegamos el resultado podría no ser el mejor, con OpenCV disponemos de la función llamada seamlessClone la cual intenta fusionar dos imágenes de la mejor manera posible.

image

image


void cv::seamlessClone( 
	InputArray 	src,
	InputArray 	dst,
	InputArray 	mask,
	Point 	        p,
	OutputArray 	blend,
	int 		flags )

Para utilizar esta función debemos las imagen que deseamos clonar, una será la fuente y la otra el destino, requerimos además una mascara, una imagen binaria que representa el área dela imagen fuente que deseamos clonar,  indicamos el punto donde deseamos insertar la imagen fuente y finalmente indicamos el OutputArray donde se guardará el resultado, el parámetro flags nos permite establecer el método utilizado para combinar las imágenes.

#include <opencv2/photo.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int, char** argv)
{
    Mat source = imread("source.png", IMREAD_COLOR);
    Mat destination = imread("destination.png", IMREAD_COLOR);
    Mat mask = imread("mask.png", IMREAD_COLOR);

    imshow("source", source);
    imshow("mask", mask);
    imshow("destination", destination);
    
    Mat result;
    
    Point p;
    p.x = (float)2*destination.size().width/3;
    p.y = (float)destination.size().height/4;
    
    seamlessClone(source, destination, mask, p, result, NORMAL_CLONE);
    
    imshow("fotomontaje", result);

    waitKey();
    return 0;
}

Las imágenes utilizadas por este ejemplo las puedes encontrar en el repositorio git que contiene los datos de prueba para OpenCV, en la siguiente dirección:

https://github.com/Itseez/opencv_extra/tree/master/testdata/cv/cloning/Normal_Cloning

Desarrollo de Nuestra Aplicación

Aplicando lo aprendido en los tutoriales OpenCV anteriores vamos a crear una pequeña aplicación que permite agregar un tercer ojo a un rostro, para este software aplicaremos técnicas de detección de rostros y ojos, además del seamless clone que acabamos de ver.

1. Lo primero que haremos será utilizar los clasificadores en cascada para detectar un rostro presente en la imagen de entrada, si existe localizamos la región correspondiente, utilizaremos el clasificador haarcascade_frontal_face_default.xml entrenado par localizar rostros frontales.

String face_xml = R"(..\haarcascades\haarcascade_frontalface_default.xml)";
String eyel_xml = R"(..\haarcascades\haarcascade_eye.xml)";

CascadeClassifier face_detector, eye_detector;
if (!eye_detector.load(eyel_xml) || !face_detector.load(face_xml)) {
    cout << "Error: no se ecuentra el cascade xml" << endl;
}

Mat frame, gray_frame;

frame = imread(R"(..\opencv-3.2.0\samples\data\lena.jpg)");

cvtColor(frame, gray_frame, COLOR_BGR2GRAY);
equalizeHist(gray_frame, gray_frame);

std::vector<cv::Rect> faces;
face_detector.detectMultiScale(gray_frame, faces);

2. Dentro de la región que contiene el rosto intentamos localizar los ojos, para esta operación también utilizaremos los clasificadores en cascada, el clasificador XML usado para ello será haarcascade_eye.xml entrenado para localizar ojos. 

Mat face = frame(region);

int leftX = cvRound(face.cols * EYE_SX);
int topY = cvRound(face.rows * EYE_SY);
int widthX = cvRound(face.cols * EYE_SW);
int heightY = cvRound(face.rows * EYE_SH);
int rightX = cvRound(face.cols * (1.0 - EYE_SX - EYE_SW));

Mat topLeftOfFace = face(Rect(leftX, topY, widthX, heightY));
Mat topRightOfFace = face(Rect(rightX, topY, widthX, heightY));

std::vector<cv::Rect> left_eye, right_eye;

eye_detector.detectMultiScale(topLeftOfFace, left_eye);
eye_detector.detectMultiScale(topRightOfFace, right_eye);

3. Tomamos uno de los ojos encontrados y lo usaremos como imagen fuente para la función cv::seamlessClone, la imagen de entrada será el destino, para la mascara usaremos una elipse para indicar el área que deseamos clonar, la cual queremos que sea el ojo, finalmente necesitamos calcular la posición, trataremos que sea justo en medio de los ojos. 

Mat eye = topLeftOfFace(left_eye[0]);
Mat mask(eye.rows, eye.cols, CV_8U, Scalar::all(0));

Point pos_ellipse(mask.size() / 2);
Point pos_clone(face.size().width / 2, face.size().height / 5);
Size size(mask.size().width / 2, (mask.size().height / 2) - 2);

cv::ellipse(mask, pos_ellipse ,size , 0, 0, 360, Scalar::all(255), CV_FILLED, LINE_AA);
cv::seamlessClone(eye, face, mask, pos_clone, face, NORMAL_CLONE);

Al ejecutar tendremos el siguiente resultado, aun se puede mejorar mucho mas, pero es lo bastante simple como para comprender la idea de lo que deseamos hacer y como lo haremos con una cuantas líneas de código y aplicando lo aprendido en tutoriales anteriores.

fotomontaje con opencv

Proyecto en GitHub: Fotomontajes con OpenCV

Comentarios

Entradas populares de este blog

Conectar SQL Server con Java

Entrenar OpenCV en Detección de Objetos

Acceso a la webcam con OpenCV

JavaFx 8 Administrar ventanas

Conociendo la clase cv::Mat de OpenCV