Mostrando postagens com marcador OpenCV. Mostrar todas as postagens
Mostrando postagens com marcador OpenCV. Mostrar todas as postagens
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:


"Chaves de menor":

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.



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 List obterDadosFaces(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 List CortarImagem(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(List dados, 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);
  
  List propsFaces = 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