1. Objetivo
Nesse exercício iremos criar um efeito de quebra cabeças. Nesse exercício, aprenderemos o conceito de região de interesse, e o utilizaremos para separar a matriz em 4 submatrizes e trocá-las aleatoriamente
2. Região de Interesse e Aleatoriedade
O primeiro conceito que devemos trabalhar é o de Região de Interesse, ou ROI (do inglês: Region of Interest). A região de interesse, como o próprio nome nos diz, especifica uma subregião de uma região maior a qual vamos destinar nossas operações. Quando trabalhamos com imagens, nossa ROI é uma parte da imagem, e nela vamos realizar algumas operações específicas sem ter que realizá-la na imagem completa.
Para trocar as regiões iremos implementar um algoritmo de aleatoriedade. As linguagens C
e C++
possuem tal função, e ela será apresentada em seguida.
3. O Código
O código é mostrado abaixo.
#include <iostream>
#include <opencv2/opencv.hpp>
#include <time.h>
using namespace std;
using namespace cv;
int main(int argc,char* argv[]){
Mat im= imread(argv[1],CV_LOAD_IMAGE_COLOR);
if(!im.data){
cout<<"Nao abriu a imagem!\n";
return -1;
}
Mat result = im.clone();
Rect regions[4] = {Rect(0,0,im.cols/2,im.rows/2),Rect(im.cols/2,0,im.cols/2,im.rows/2),
Rect(0,im.rows/2,im.cols/2,im.rows/2),Rect(im.cols/2,im.rows/2,im.cols/2,im.rows/2)};
int vetOptions[24][4] = { {0, 1, 2, 3}, {0, 1, 3, 2}, {0, 2, 1, 3}, {0, 2, 3, 1}, {0, 3, 1, 2}, {0, 3, 2, 1},
{1, 0, 2, 3}, {1, 0, 3, 2}, {1, 2, 0, 3}, {1, 2, 3, 0}, {1, 3, 0, 2}, {1, 3, 2, 0},
{2, 1, 3, 0}, {2, 1, 0, 3}, {2, 0, 1, 3}, {2, 0, 3, 1}, {2, 3, 1, 0}, {2, 3, 0, 1},
{3, 2, 1, 0}, {3, 2, 0, 1}, {3, 1, 2, 0}, {3, 1, 0, 2}, {3, 0, 1, 2}, {3, 0, 2, 1}};
int option; namedWindow("janela");
do{
cout<<"Escolha uma opção:\n1. Trocar aleatoriamente\n2. Escolher ordem\n3. Mostrar\n4. Salvar\n-- Terminar (-1) \n";
cin>>option;
if(option==1){
srand(time(NULL));
int order = rand() % 24;
for(int i = 0; i < 4; i++ ){
switch (vetOptions[order][i]){
case 0:
im(regions[i]).copyTo(result(regions[0]));
break;
case 1:
im(regions[i]).copyTo(result(regions[1]));
break;
case 2:
im(regions[i]).copyTo(result(regions[2]));
break;
case 3:
im(regions[i]).copyTo(result(regions[3]));
break;
}
}
}
if(option==2){
int order[4];
cout<<"1 2\n3 4\n\nRegiao 1: ";
cin>>order[0];
cout<<"Regiao 2: ";
cin>>order[1];
cout<<"Regiao 3: ";
cin>>order[2];
cout<<"Regiao 4: ";
cin>>order[3];
for(int i = 0; i < 4; i++ ){
im(regions[order[i]-1]).copyTo(result(regions[i]));
}
}
if(option==3){
imshow("janela",result);
waitKey();
}
if(option==4){
string nome;
cout<<"Nome do Arquivo";
cin>>nome;
imwrite(nome,result);
}
}while(option!=-1);
}
3.1. Bibliotecas Utilizadas
Além das bibliotecas usuais <iostream>
e <opencv.hpp>
, utilizamos também a biblioteca <time.h>
. Essa biblioteca nos permite utilizar a função time()
com o parâmetro NULL
. Isso nos retorna o tempo em segundos desde 1° de Janeiro de 1970. Desse modo, o valor retornado é sempre diferente.
Com esse valor diferente, podemos aplica-lo na função srand()
, que nos permite passar como parâmetro a "semente" da função rand()
, ou seja, cada vez que passarmos um parâmetro para a função srand()
, a função rand()
nos retornará um valor pseudo-randômico. Variando-se o parâmetro da função srand()
garantimos que o valor retornado por rand()
seja sempre diferente.
3.2. Os Vetores de ROI e de Possibilidades
Primeiramente, devemos saber que a classe cv::Rect
é uma classe que define uma região de interesse retangular, tendo como parâmetros do construtor um ponto e sua altura e largura. Podemos utilizar essa classe dentro da classe cv::Mat
para selecionarmos apenas uma parte da Matriz.
Em seguida, como mostrado abaixo, criamos um vetor de Rect
chamado de regions[]
, como mostrado abaixo.
Rect regions[4] = {Rect(0,0,im.cols/2,im.rows/2),Rect(im.cols/2,0,im.cols/2,im.rows/2),
Rect(0,im.rows/2,im.cols/2,im.rows/2),Rect(im.cols/2,im.rows/2,im.cols/2,im.rows/2)};
Esse vetor define as regiões como mostrado na matriz abaixo, sendo a numeração da matriz igual à numeração do vetor+1. \$\begin{bmatrix} 1 &2 \\ 3 &4 \end{bmatrix}\$.
Um outro vetor que iremos utilizar é o vetor int vetOptions[24][4]
. Esse vetor tem 24 linhas pois cada uma delas representa uma possibilidade de troca (\$4!=24\$). Não podemos utilizar somente a função rand()
pois ela não garante que não haverá repetições. As colunas, para cada linha, representam a ordem da troca. Se, por exemplo, tivermos que vetOptions[19] = {3, 2, 0, 1}
, logo a região 1 passará a ser a região 4, a região 2 passará a ser a região 3, a 1 passará a ser a 2 e a 2 passará a ser a região 3.
A nomenclatura pela qual chamamos as regiões é diferente da utilizada no código, pois \$\text{nomenclatura_utilizada} = \text{nomeclatura_código} + 1\$. |
3.3. Trocando as Regiões
A etapa final é mostrada no trecho de código a seguir.
if(option==1){
srand(time(NULL));
int order = rand() % 24;
for(int i = 0; i < 4; i++ ){
switch (vetOptions[order][i]){
case 0:
im(regions[i]).copyTo(result(regions[0]));
break;
case 1:
im(regions[i]).copyTo(result(regions[1]));
break;
case 2:
im(regions[i]).copyTo(result(regions[2]));
break;
case 3:
im(regions[i]).copyTo(result(regions[3]));
break;
}
}
}
Nesse trecho do código, apenas implementamos as ideias discutidas anteriormente. Primeiro chamamos a função srand()
e depois geramos um número pseudo-radômico com a função rand()
. Quando realizamos a operação \$\text{order = rand()%24}\$, atribuímos à variável order
o valor do resto da divisão de rand
por 24, e obtemos um número entre 0 e 23, que servirá para acessarmos as linhas do vetor vetOptions[]
.
A matriz im representa a imagem original e a matriz result é a matriz com as regiões trocadas. Ela foi criada através da função clone() (em uma parte anterior do código) que cria uma cópia da matriz que chama a função (result = im.clone() ). O operador = aparentou criar uma cópia por referência retornando resultados inesperados no código
|
Em seguida, acessamos cada uma das posições do vetOptions[order]
, e utilizamos um switch
para cada uma das trocas possíveis. Caso o valor de i
seja 1, acessaremos o vetOptions[order][1]
; caso seu valor seja 2, inseriremos na região 2 da matriz result
o valor da região 1 da matriz im
.
3.4. Usuário Escolhendo as Regiões
O programa também possui um menu de operações possíveis ao usuário. O usuário pode trocar as regiões aleatoriamente ou selecionar ele mesmo as regiões que deseja trocar. Em vez de utilizarmos um vetor com todas as possibilidades, as regiões a serem trocadas já vão ser fornecidas pelo usuario, e assim basta armazená-la (utilizamos um vetor chamado order
) e em seguida executarmos um for
indo de 0 a 3, para cada uma das regiões. Depois o usuário pode ver a imagem modificada e salvá-la, se desejado.
if(option==2){
int order[4];
cout<<"1 2\n3 4\n\nRegiao 1: ";
cin>>order[0];
cout<<"Regiao 2: ";
cin>>order[1];
cout<<"Regiao 3: ";
cin>>order[2];
cout<<"Regiao 4: ";
cin>>order[3];
for(int i = 0; i < 4; i++ ){
im(regions[order[i]-1]).copyTo(result(regions[i]));
}
}
if(option==3){
imshow("janela",result);
waitKey();
}
if(option==4){
string nome;
cout<<"Nome do Arquivo";
cin>>nome;
imwrite(nome,result);
}
4. A Saída do Programa
Utilizamos a seguinte imagem do Darth Vader, vilão dos filmes de Star Wars, como entrada do nosso programa.

Obtivemos duas saídas diferentes, mostrando, assim, a aleatoriedade do programa

