segunda-feira, 1 de janeiro de 2018

Problema dos apontadores, parâmetros argc, argv e manipulação dos pixels de um arquivo .ppm


Compartilhe!

O objetivo do programa em C abaixo é implementar 3 filtros: escala de cinza, esticar contraste e normalizar, em uma imagem/arquivo do formato ppm.

A entrada:
A função main receberá em argv dois nomes de arquivos que serão copiados para as variáveis arqEntrada e arqSaida, respectivamente. Em arqEntrada estará o nome do arquivo contendo a imagem original, e em arqSaida o nome do arquivo de saída que será escrito pelo programa.

A imagem original terá intensidade máxima igual a 255 e tamanho n x m, onde n,m <= 128. O efeito a ser aplicado (cinza, esticar ou normalizar) é lido da entrada padrão e armazenado na váriavel efeito, conforme implementação da função main no arquivo.

A saída:
A imagem resultante deve ser escrita em PPM no arquivo de saída (arqSaida).

Exemplo de um arquivo de entrada e outro de saída (repare no seu formato, abrindo o arquivo com um bloco de notas):

https://drive.google.com/file/d/1BhFXRoW8afqRBqJuyNUUgkYYz4FL0722/view

https://drive.google.com/file/d/1a1NtW6gxkLSZSxN-esXlKVZmq4Ul93ux/view
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

void normalizar(char *arqE, char *arqS);
void esticarContraste(char *arqE, char *arqS);
void escalaDeCinza(char *arqE, char *arqS);

int main(int argc, char **argv){

 if (argc != 3){//Verifica se há os três argumentos.
 
  fprintf(stderr, "Argumentos invalidos. Use:\n");//Caso erro, exibe mensagem com instrução.
  fprintf(stderr, "./lab18 <arqEntrada> <arqSaida>\n");
  fprintf(stderr, "Usado:");
  
  for (int i=0; i<argc; i++){
  
   fprintf(stderr, " %s", argv[i]);
   
  }
  
  fprintf(stderr, "\n");
  return 1;
 }
 
 char efeito[20];
 char *arqEntrada = argv[1];
 char *arqSaida = argv[2];

 scanf("%s", efeito);//Leia efeito.

 if(strcmp("cinza", efeito)==0){//Verifica qual efeito aplicar e chama a respectiva função.
  escalaDeCinza(arqEntrada, arqSaida);
 }else if (strcmp("esticar", efeito)==0){
  esticarContraste(arqEntrada, arqSaida);
 }else if (strcmp("normalizar", efeito)==0){
  normalizar(arqEntrada, arqSaida);
 }

  return 0;
}

//Função normalizar recebe como parâmetros dois apontadores para os endereços dos arquivos entrada e saída. Ele executa alterações no arquivo saída, apenas.
void normalizar(char *arqE, char *arqS){

 FILE *arqEntrada = fopen(arqE, "r"), *arqSaida = fopen(arqS, "w");//Abre os arquivos.
 char marcador[2];
 int width, height, Imax, R, G, B, novaR, novaG, novaB, soma;

 if(arqEntrada == NULL) return;//Se não encontrou arquivo de entrada, encerra a função.

 fscanf(arqEntrada,"%s\n%d %d\n%d\n", marcador, &width, &height, &Imax);//Leia marcador, largura, altura e Imax do arquivo de entrada e escreve valores no arq. de saída.
 fprintf(arqSaida, "%s\n%d %d\n%d\n", marcador, width, height, Imax);

 for(int i = 0; i<height; i++){//Como uma matriz, percorre as linhas e as colunas.
 
  for(int j = 0; j<width; j++){
  
   fscanf(arqEntrada,"%d %d %d ", &R, &G, &B);//Leia três inteiros (que representam o R, G e o B).
   
   soma = R + G + B;
   if(soma!=0){//Evitar a divisão por zero.

    novaR = (R * 255) / soma;
    novaG = (G * 255) / soma;
    novaB = (B * 255) / soma;//Armazena novos valores para R, G e B.

   }else novaR = R, novaG = G, novaB = B;

   fprintf(arqSaida, "%d %d %d%c", novaR, novaG, novaB, (j != width-1? ' ':'\n'));//Escreve no arquivo o R G e o B seguido de um espaço ou \n (este se for último elemento da linha.
   
  }
  
  fscanf(arqEntrada,"\n");
  //fprintf(arqSaida, "\n");

 }

 fclose(arqEntrada);//Fecha os arquivos.
 fclose(arqSaida);
}


//Função esticarContraste recebe como parâmetros dois apontadores para os endereços dos arquivos entrada e saída. Ele executa alterações no arquivo saída, apenas.
void esticarContraste(char *arqE, char *arqS){
 /*Mesmo procedimento que na função normalizar*/
 FILE *arqEntrada = fopen(arqE, "r"), *arqSaida = fopen(arqS, "w");
 char marcador[2];
 int width, height, Imax, R, G, B, Rmin = 255, Rmax = 0, Gmin = 255, Gmax = 0, Bmin = 255, Bmax = 0, novaR, novaG, novaB;

 if(arqEntrada == NULL) return;

 fscanf(arqEntrada,"%s\n%d %d\n%d\n", marcador, &width, &height, &Imax);
 fprintf(arqSaida, "%s\n%d %d\n%d\n", marcador, width, height, Imax);

 for(int i = 0; i<height; i++){//A diferença da função normalizar está aqui. Pretende-se encontrar o menor e o maior valor para cada uma das intensidades (R, G e B).
 
  fscanf(arqEntrada,"\n");
  for(int j = 0; j<width; j++){
  
   fscanf(arqEntrada,"%d %d %d ", &R, &G, &B);
   
   if(R<Rmin) Rmin = R;
   else if(R>Rmax) Rmax = R;
   
   if(G<Gmin) Gmin = G;
   else if(G>Gmax) Gmax = G;
   
   if(B<Bmin) Bmin = B;
   else if(B>Bmax) Bmax = B;
  }
  
 }

 rewind(arqEntrada);//Retorna ao início do arquivo e lê apenas o seguinte para posicionar novamente o ponteiro do aquivo para onde começa a sequência dos pixels.
 fscanf(arqEntrada,"%s\n%d %d\n%d\n", marcador, &width, &height, &Imax);
 
 for(int i = 0; i<height; i++){
 
  for(int j = 0; j<width; j++){
   
   fscanf(arqEntrada,"%d %d %d ", &R, &G, &B);//Leia três inteiros (que representam o R, G e o B).
   
   novaR = ((R - Rmin) * 255)/(Rmax - Rmin);
   novaG = ((G - Gmin) * 255)/(Gmax - Gmin);
   novaB = ((B - Bmin) * 255)/(Bmax - Bmin);//Armazena os novos valores para R, G e B.
   
   fprintf(arqSaida, "%d %d %d%c", novaR, novaG, novaB, (j != width-1? ' ':'\n'));
   
  }
  fscanf(arqEntrada,"\n");
  //fprintf(arqSaida, "\n");
  
 }
 
 fclose(arqEntrada);
 fclose(arqSaida);
}


//Função escalaDeCinza recebe como parâmetros dois apontadores para os endereços dos arquivos entrada e saída. Ele executa alterações no arquivo saída, apenas.
void escalaDeCinza(char *arqE, char *arqS){
 /*Mesmo procedimento de normalizar, muda-se apenas a operação para cada novo pixel.*/
 FILE *arqEntrada = fopen(arqE, "r"), *arqSaida = fopen(arqS, "w");
 char marcador[2];
 int width, height, Imax, piso, R, G, B;

 if(arqEntrada == NULL) return;

 fscanf(arqEntrada,"%s\n%d %d\n%d\n", marcador, &width, &height, &Imax);
 fprintf(arqSaida, "%s\n%d %d\n%d\n", marcador, width, height, Imax);

 for(int i = 0; i<height; i++){
 
  for(int j = 0; j<width; j++){
  
   fscanf(arqEntrada,"%d %d %d ", &R, &G, &B);
   piso = floor((R + G + B) / 3.0);
   fprintf(arqSaida, "%d %d %d%c", piso, piso, piso, (j != width-1? ' ':'\n'));
   
  }
  
  fscanf(arqEntrada,"\n");
  //fprintf(arqSaida, "\n");
 }

 fclose(arqEntrada);
 fclose(arqSaida);
}

Se voce é professor, pode ser uma boa cobrar de seus alunos a solução desta tarefa usando matrizes. Aqui preferi ler e escrever diretamente nos arquivos de entrada e saída, porém existe essa outra possibilidade.