Com a biblioteca OpenCv possível fazer bastante coisa, o foco desse post é a detecção facial. Para quer tiver interessado em conhecer um pouco mais: http://opencv.org/
Vamos utilizar Java que é umas das linguagens suportadas. Como vamos brincar com essa biblioteca iremos construir um aplicativo para desfocar apenas os rotos das pessoas em fotos:
Atenção, Creuzebek!
Faça do download da biblioteca no site do OpenCv: http://opencv.org/. Crie um projeto Java no Eclipse.
Clique com o potão direito sobre o seu projeto vá em: Build Path -> Configure Build Path,
espanda o item referente a JRE clique em edit e selecione o diretório
onde estão as bibliotecas nativas do OpenCv que por padrão estão em C:/opencv/opencv/build/java/x86,
mas pode mudar dependendo da sua instalação.
Vamos utilizar Java que é umas das linguagens suportadas. Como vamos brincar com essa biblioteca iremos construir um aplicativo para desfocar apenas os rotos das pessoas em fotos:
"Chaves de menor":
Faça do download da biblioteca no site do OpenCv: http://opencv.org/. Crie um projeto Java no Eclipse.
Clique com o potão direito sobre o seu projeto vá em: Build Path -> Configure Build Path,
espanda o item referente a JRE clique em edit e selecione o diretório
onde estão as bibliotecas nativas do OpenCv que por padrão estão em C:/opencv/opencv/build/java/x86,
mas pode mudar dependendo da sua instalação.
Nesse projeto eu utilizei os conceitos do SOLID, que bem resumidamente diz que cada classe deve servir a um único propósito. Dessa forma essa ficou sendo a minha estrutura de pacotes:
Para começar coloque a linha a seguir no main do seu código antes de qualquer outro trecho:
public static void main(String[] args) {System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Essa linha é responsável por "invocar" as bibliotecas nativas que inserimos anteriormente, que na realidade é quem faz todo o trabalho. Logo após você precisará carregar um arquivo xml que contém as parametrizações necessárias para fazer o reconhecimento dos rostos:
CascadeClassifier cascadeClassifier = new CascadeClassifier(System.getProperty("user.dir") + "/haarcascade_frontalface_alt_tree.xml");
Bom aí você me pergunta onde encontro esse arquivo? dependendo da sua instalação ele fica arqui: C:\opencv\opencv\sources\data\haarcascades . Sugiro que você copie esse arquivo para raiz do seu projeto e carregue ele como está acima.
Após isso precisamos carregar a imagem, você deve fazer dessa forma:
Mat mat = Highgui.imread(System.getProperty("user.dir") +"/chaves.jpg");
No método imread você passa o caminho até o arquivo.
Para facilitar o entendimento eu crie um diagrama de atividades que ilustra os passos necessários para a brincadeira acontecer:
Passo 1 - Detectar faces
Para essa atividade ou passo está na classe ServiceDeteccaoFacesImagem e temo o método que faz a detecção das faces:
public MatOfRect detectarFaces(CascadeClassifier cascadeClassifier, Mat mat){ MatOfRect matOfRect = new MatOfRect(); cascadeClassifier.detectMultiScale(mat, matOfRect); return matOfRect;
}
Esse método recebe os dois objeto criados anteriormente, que são respectivamente: a classe que "carrega" o xml e a classe que "carrega" a imagem. o objeto CascadeClassifier contém o método detectMultScale que localiza o que está sendo procurado na imagem que em nosso caso são rostos.
Passo 2 - Obter dados das faces
Os dados que serão obtidos das faces serão sua posição (x,y) na imagem e a largura e altura da face. O método a seguir contém a lógica de extração desses dados novamente a classe utilizada é ServiceDeteccaoFacesImagem:
public ListobterDadosFaces(MatOfRect matOfRect){ List dados = new ArrayList (); for (Rect rect : matOfRect.toArray()) { PropriedadesFace prop = new PropriedadesFace(); prop.setX(rect.x); prop.setY(rect.y); prop.setHeight(rect.height); prop.setWidth(rect.width); dados.add(prop); } return dados; }
Notem que o retorno é uma lista do objeto PropriedadesFace abaixo o modelo da classe desse objeto, ocultado os geters e seteres:
public class PropriedadesFace { private int x; private int y; private int width; private int height; private BufferedImage imageCortada;
Passo 3 - Desfocar toda a imagem
Vamos desfocar a imagem antes de "recortar" as faces, essa atividade está na classe ServiceDesfoqueImagem:
public BufferedImage DesfocarImagem(Mat mat){ mat = Desfocar(mat); return Util.converterParaImage(mat); } private Mat Desfocar(Mat image){ Mat destination = new Mat(image.rows(),image.cols(),image.type()); Imgproc.GaussianBlur(image, destination,new Size(45,45), 0); return destination; }
Temos aqui esses 2 métodos um recebe o objeto Mat e o outro faz o desfoque da imagem. O desfoque é feito utilizado o método GaussianBlur da classe Imgproc, que pode ser invocado diretamente porque é um método estático. Por fim convertemos o objeto Mat para o BufferedImage, esse método se encontra na classe Util:
public static BufferedImage converterParaImage(Mat image){ MatOfByte bytemat = new MatOfByte(); Highgui.imencode(".jpg", image, bytemat); byte[] bytes = bytemat.toArray(); InputStream in = new ByteArrayInputStream(bytes); BufferedImage img=null; try { img = ImageIO.read(in); } catch (IOException e) { e.printStackTrace(); } return img; }
Passo 4 - Recortar as faces da imagem
Para cortar as faces da imagem utilizamos o método CortarImagem da classe ServiceCorteImagem:
public ListCortarImagem(List dados, BufferedImage imagem){ for(PropriedadesFace dado : dados){ dado.setImageCortada(imagem.getSubimage(dado.getX(), dado.getY(), dado.getWidth(), dado.getHeight())); } return dados; }
Utilizamos o método getSubImage para "cortar" uma determinada parte da imagem e a imagem "cortada" é incluída no objeto PropriedadesFace, o mesmo objeto que contém seus dados.
Passo 5 - Obter imagem sem o desfoque
Para obter essa imagem utilizamos o método converterParaImagem na classe Util:
public static BufferedImage converterParaImage(Mat image){ MatOfByte bytemat = new MatOfByte(); Highgui.imencode(".jpg", image, bytemat); byte[] bytes = bytemat.toArray(); InputStream in = new ByteArrayInputStream(bytes); BufferedImage img=null; try { img = ImageIO.read(in); } catch (IOException e) { e.printStackTrace(); } return img; }
Essa imagem vai servir como fundo para "colarmos" as imagens dos rostos desfocados sobre ela.
Passo 6 - Juntar imagem com as faces recortadas
Utilizamos esse dois métodos para sobrepor as imagens:
public BufferedImage juntarImagens(Listdados, BufferedImage imagemPrincipal){ for(PropriedadesFace dado: dados){ imagemPrincipal = juntarUmaImage(imagemPrincipal, dado.getImageCortada(),dado.getX(),dado.getY()); } return imagemPrincipal; } public static BufferedImage juntarUmaImage(BufferedImage imagemPrincipal, BufferedImage imagemCortada, int x, int y) { Graphics2D g = imagemPrincipal.createGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.drawImage(imagemPrincipal, 0,0, null); g.drawImage(imagemCortada, x, y, null); g.dispose(); return imagemPrincipal; }
O primeiro percorre todas as imagens que estão juntas com os dados das suas posições, já o segundo faz a sobreposição em si. Os dois métodos estão na classe ServiceSobreposicaoImagem.
Para terminar
Esse é o código do método main responsável por sequenciar as chamadas dos métodos:
System.loadLibrary(Core.NATIVE_LIBRARY_NAME); CascadeClassifier cascadeClassifier = new CascadeClassifier(System.getProperty("user.dir") + "/haarcascade_frontalface_alt_tree.xml"); Mat mat = Highgui.imread(System.getProperty("user.dir") +"/chaves.jpg"); ServiceDeteccaoFacesImagem serviceExtractFaces = new ServiceDeteccaoFacesImagem(); MatOfRect matOfRect = serviceExtractFaces.detectarFaces(cascadeClassifier, mat); ListpropsFaces = serviceExtractFaces.obterDadosFaces(matOfRect); ServiceDesfoqueImagem serviceBlur = new ServiceDesfoqueImagem(); BufferedImage imagemCorteDesfoque = serviceBlur.DesfocarImagem(mat); ServiceCorteImagem serviceCrop = new ServiceCorteImagem(); propsFaces = serviceCrop.CortarImagem(propsFaces, imagemCorteDesfoque); ServiceSobreposicaoImagem serviceOverlay = new ServiceSobreposicaoImagem(); BufferedImage imagemSemEfeitos = Util.converterParaImage(mat); imagemCorteDesfoque = serviceOverlay.juntarImagens(propsFaces, imagemSemEfeitos); File outputfile = new File("chaves menor.jpg"); try { ImageIO.write(imagemCorteDesfoque, "jpg", outputfile); } catch (IOException e) { e.printStackTrace(); }
E é isso galera quem quiser o código ele pode ser acessado aqui: https://github.com/rafaelguinho/HidingHerFace