Além do FOX Toolkit, eu utilizo a biblioteca OpenCV (Open Source Computer Vision Library), pois trabalho com Processamento de Imagens. Então surgiu a necessidade de juntar os dois.
O OpenCV até fornece alguns recursos de interface gráfica, mas são muito limitados: apenas janelas, trackbars e tratamento de eventos de mouse e teclado. A próxima figura mostra uma janela com uma trackbar.
Quebrando, então, a seqüência de tutoriais básicos, vou ensinar como exibir uma imagem do OpenCV (IplImage) em um aplicativo FOX.
Para isso, criarei um aplicativo que abre uma imagem pelo OpenCV, exibe e aplica o filtro da média. Não vou falar da criação da interface e do tratamento de eventos, apenas mostrar como se faz a "conversão" de IplImage para FXImage.
Na declaração da classe, eu apenas declaro os ponteiros para as imagens:
No construtor, eu apenas atribuo nulo a ambas, já que o usuário é quem vai escolher a imagem.
A "conversão" é feita ao carregar a imagem:
Aqui, eu crio uma imagem temporária só para carregar a imagem do arquivo. Neste caso, ele abre uma imagem colorida com três canais de 8 bits.
A representação interna do IplImage é um vetor de caracteres e os pixels são armazenados em BGR.
A representação interna do FXImage é um vetor de FXColor (equivalente a um inteiro sem sinal). Em cada inteiro são armazenados quatro canais (um byte para cada): RGBA.
Bom, aí já temos uma semelhança: ambos armazenam em um vetor. Isso que dizer que eu posso aproveitar o vetor de pixels do IplImage e jogar dentro do FXImage. Basta converter a representação interna.
Então, cria-se uma IplImage com quatro canais e profundidade de 8 bits sem sinal. Esta é a que foi declarada em nossa classe.
Basta então converter nossa imagem temporária de BGR para RGBA:
Assim, já temos uma IplImage com representação interna parecida com a de FXImage. É preciso ainda converter de char* para FXColor*. Essa não é uma conversão padrão, portanto é preciso usar um operador especial para isso.
Eu acho até dolorido usá-lo, mas não vi outra alternativa.
Com um vetor de pixels no formato interno do FXImage, é só criar nossa imagem:
Como a imagem foi criada depois do aplicativo, é preciso chamar create() manualmente.
Manipulando a imagem
Agora, temos um FXImage com o vetor de pixels compartilhado com o IplImage. Isso significa que qualquer operação executada sobre a imagem Ipl refletirá na imagem FX.
Isso é demonstrado aqui, onde eu aplico o filtro da média para suavizar a imagem:
Os únicos detalhes é que é preciso chamar render() de FXImage e atualizar o imageView.
O render() é necessário porque há duas representações do FXImage: uma do lado do cliente, que neste caso é o vetor compartilhado entre as duas imagens, e o lado do servidor gráfico. render() atualiza o lado do servidor gráfico, de forma que seja exibida a imagem modificada.
E é preciso atualizar o imageView porque ele não detecta automaticamente alterações na imagem, então é preciso informar manualmente que ele deve se redesenhar.
Trabalhando com vídeos
O OpenCV, além de imagens estáticas, também trabalha com vídeos, que podem ser capturados de câmeras ou lidos de arquivos.
Cada quadro do vídeo é recuperado em um IplImage. Isso significa que dá para usar a técnica descrita acima. O problema é que a imagem que representa cada quadro não pode ser deletada nem modificada. Isso significa que para cada quadro do vídeo, é preciso fazer a conversão para uma outra imagem.
Seria mais simples se desse para modificar a imagem retornada, assim dava para compartilhar os pixels sem a necessidade de uma imagem extra.
Infelizmente, não tenho nenhum exemplo a mostrar com vídeos.
Resultado
Créditos
Imagem retirada do Eu Podia Tá Matando
---
Código-fonte deste tutorial.
Baixar vídeo.
O OpenCV até fornece alguns recursos de interface gráfica, mas são muito limitados: apenas janelas, trackbars e tratamento de eventos de mouse e teclado. A próxima figura mostra uma janela com uma trackbar.
Quebrando, então, a seqüência de tutoriais básicos, vou ensinar como exibir uma imagem do OpenCV (IplImage) em um aplicativo FOX.
Para isso, criarei um aplicativo que abre uma imagem pelo OpenCV, exibe e aplica o filtro da média. Não vou falar da criação da interface e do tratamento de eventos, apenas mostrar como se faz a "conversão" de IplImage para FXImage.
Na declaração da classe, eu apenas declaro os ponteiros para as imagens:
19 FXImageView *imageView;
20
21 FXImage *fxImage;
22 IplImage *iplImage;
No construtor, eu apenas atribuo nulo a ambas, já que o usuário é quem vai escolher a imagem.
32 fxImage = 0;
33 iplImage = 0;
A "conversão" é feita ao carregar a imagem:
52 long MainWindow::onCmdOpen(FXObject*, FXSelector, void*) {
53 FXFileDialog dialog(this, "Abrir imagem");
54
55 if (!dialog.execute(PLACEMENT_OWNER))
56 return 1;
57
58 if (fxImage)
59 delete fxImage;
60
61 if (iplImage)
62 cvReleaseImage(&iplImage);
63
64 FXString filename = dialog.getFilename();
65
66 IplImage *temp = cvLoadImage(filename.text());
67 iplImage = cvCreateImage(cvGetSize(temp), IPL_DEPTH_8U, 4);
68
69 cvCvtColor(temp, iplImage, CV_BGR2RGBA);
70
71 cvReleaseImage(&temp);
72
73 FXColor *colorData = reinterpret_cast<FXColor*> (iplImage->imageData);
74
75 fxImage = new FXImage(getApp(), colorData,
76 IMAGE_SHMP|IMAGE_SHMI|IMAGE_KEEP,
77 iplImage->width, iplImage->height);
78
79 fxImage->create();
80
81 imageView->setImage(fxImage);
82
83 return 1;
84 }
Aqui, eu crio uma imagem temporária só para carregar a imagem do arquivo. Neste caso, ele abre uma imagem colorida com três canais de 8 bits.
A representação interna do IplImage é um vetor de caracteres e os pixels são armazenados em BGR.
A representação interna do FXImage é um vetor de FXColor (equivalente a um inteiro sem sinal). Em cada inteiro são armazenados quatro canais (um byte para cada): RGBA.
Bom, aí já temos uma semelhança: ambos armazenam em um vetor. Isso que dizer que eu posso aproveitar o vetor de pixels do IplImage e jogar dentro do FXImage. Basta converter a representação interna.
Então, cria-se uma IplImage com quatro canais e profundidade de 8 bits sem sinal. Esta é a que foi declarada em nossa classe.
67 iplImage = cvCreateImage(cvGetSize(temp), IPL_DEPTH_8U, 4);
Basta então converter nossa imagem temporária de BGR para RGBA:
69 cvCvtColor(temp, iplImage, CV_BGR2RGBA);
Assim, já temos uma IplImage com representação interna parecida com a de FXImage. É preciso ainda converter de char* para FXColor*. Essa não é uma conversão padrão, portanto é preciso usar um operador especial para isso.
Eu acho até dolorido usá-lo, mas não vi outra alternativa.
73 FXColor *colorData = reinterpret_cast<FXColor*> (iplImage->imageData);
Com um vetor de pixels no formato interno do FXImage, é só criar nossa imagem:
75 fxImage = new FXImage(getApp(), colorData,
76 IMAGE_SHMP|IMAGE_SHMI|IMAGE_KEEP,
77 iplImage->width, iplImage->height);
78
79 fxImage->create();
Como a imagem foi criada depois do aplicativo, é preciso chamar create() manualmente.
Manipulando a imagem
Agora, temos um FXImage com o vetor de pixels compartilhado com o IplImage. Isso significa que qualquer operação executada sobre a imagem Ipl refletirá na imagem FX.
Isso é demonstrado aqui, onde eu aplico o filtro da média para suavizar a imagem:
88 long MainWindow::onCmdSmooth(FXObject*, FXSelector, void*) {
89 cvSmooth(iplImage, iplImage, CV_BLUR, 3, 3);
90
91 fxImage->render();
92
93 imageView->update();
94
95 return 1;
96 }
Os únicos detalhes é que é preciso chamar render() de FXImage e atualizar o imageView.
O render() é necessário porque há duas representações do FXImage: uma do lado do cliente, que neste caso é o vetor compartilhado entre as duas imagens, e o lado do servidor gráfico. render() atualiza o lado do servidor gráfico, de forma que seja exibida a imagem modificada.
E é preciso atualizar o imageView porque ele não detecta automaticamente alterações na imagem, então é preciso informar manualmente que ele deve se redesenhar.
Trabalhando com vídeos
O OpenCV, além de imagens estáticas, também trabalha com vídeos, que podem ser capturados de câmeras ou lidos de arquivos.
Cada quadro do vídeo é recuperado em um IplImage. Isso significa que dá para usar a técnica descrita acima. O problema é que a imagem que representa cada quadro não pode ser deletada nem modificada. Isso significa que para cada quadro do vídeo, é preciso fazer a conversão para uma outra imagem.
Seria mais simples se desse para modificar a imagem retornada, assim dava para compartilhar os pixels sem a necessidade de uma imagem extra.
Infelizmente, não tenho nenhum exemplo a mostrar com vídeos.
Resultado
Créditos
Imagem retirada do Eu Podia Tá Matando
---
Código-fonte deste tutorial.
Baixar vídeo.
Nenhum comentário:
Postar um comentário