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.

regions.cpp
#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.

XxhTLgHZ
Figura 1. Darth Vader

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

darth
Figura 2. Saída 1
darth2
Figura 3. Saída 2