Processamento Digital de Imagens

Operações entre Imagens Digitais



    Nesta aula abordaremos os principais conceitos relacionados com algumas operaçoes lógicas e aritméticas que podem ser aplicadas às imagens digitais.


Importante: O resumo abaixo deve ser complementado, pelo aluno, com a leitura dos textos sugeridos na bibliografia do curso.


Tópicos


Introdução - Conceitos iniciais






Figura: Ilustração das operações entre duas imagens de entrada, X e Y, gerando uma imagem de saída Z.

Up

Operações Aritméticas

                São aplicados geralmente para se eliminar ruídos sistemáticos que aparecem igualmente na bandas de uma multiespectral.
X = X´+R ;  Y = Y´+R  e  Z = X-Y = X´-Y´

X = X´*R ;  Y = Y´*R  e  Z = X/Y = X´/Y´

Operação
Efeito sobre a Imagem
Aplicações
Adição
Z é o resultado da soma dos valores de intensidade de X e Y. Se Y for um escalar positivo, Z será uma versão mais clara de X; o acréscimo de intensidade será o próprio valor de Y.
Normalização de brilho de imagens (adequar a faixa total de níveis de cinza a um intervalo prédefinido)
Remoção de ruídos (ver técnica da filtragem pela média de múltiplas imagens na subseção 4.2.4 do livro texto)
Subtração
Z é o resultado da diferença dos valores de intensidade de X e Y. Se Y for um escalar positivo, Z será uma versão mais escura de X; o decréscimo de intensidade será o próprio valor de Y. Detecção de diferenças entre duas imagens (eventualmente adquiridas de forma consecutiva) da mesma cena
Multiplicação
Z é o produto dos valores de intensidade de X e Y. Se Y for um escalar positivo, os valores de intensidade de Z serão diretamente proporcionais a X por um fator Y. Calibração de brilho (adequação a diferentes valores de iluminância sobre uma mesma cena, por exemplo)
Divisão
Z é a razão dos valores de intensidade de X pelos valores correspondentes em Y. Se Y for um escalar positivo, os valores de intensidade de Z serão inversamente proporcionais a X por um fator Y Normalização do brilho


Tabela: Efeitos e aplicações das operações aritméticas sobre imagens (retirado do livro Processamento Digital de Imagens de  Oge Marques Filho e Hugo Vieira  Neto, pagina 41)

IDV=banda4/banda3.
 IDVN=(banda4-banda3)/(banda4+banda3).



(a)

(b)



(c)


Figura: Banda IDVN de índice de vegetação de diferença normalizada (a) obtida a partir das bandas 4 (b) e 3 (c) do sensor Thematic Mapper (TM) do satélite LandSat.

                                     
132
120
138


-
152
92
107

=
-20
28
31
84
110
200
80 124 210 4
-14
-10
255
92
177
230
100 164 25
-8
13
X -
Y =
Z
           
           
                            Zmin = -20  e Zmax = 31
                             a = (28-1)/(Zmax - Zmin) = 255/51 = 5
                             b = -a*Zmin = -5*(-20) = 100
    • Lembrete: Numa transformação linear, o valor de a, multiplicativo, é também chamado ganho e o valor de b, aditivo, é chamado de offset.

    •  linear Aplicando-se os valores de a e b, calculados acima, da relação S = a*Z + b = 5*Z + 100, em cada elemento da imagem Z, obtemos a imagem S, abaixo, que foi realçada com valores mínimo e máximo iguais a 0 e 255, respectivamente.

0
240
255
120
30
50
225
60
165
S


    • Importante: A imagem (c) da figura acima está realçada por um realce por histograma pois seus níveis de cinza originais estão na faixa entre -1 e 1.
    • Sugestão: Fazer exercícios de operações aritméticas no SPRING usando as bandas 3 e 4 do sensor TM LandSat (veja exercício 1 abaixo)

Up

Operações Lógicas



Figura: Exemplos de operações lógicas entre sobre imagens (retirado do livro Processamento Digital de Imagens de  Oge Marques Filho e Hugo Vieira  Neto, pagina 45)


Entradas
Operador Lógico
A
B
AND
OR
XOR
NOT A
0
0
0
0
0
1
0
1
0
1
1
1
1
0
0
1
1
0
1
1
1
1
0
0

                                     
X



AND
Y



=
Z
132
120
138
152
92
107 128
88
10
84
110
200
80 124 210 80
108
192
255
92
177
230
100 164 25
68
160
                  


X



AND
Y



=
Z
10000100
01111000
10001010
10011000
01011100 01101011 10000000
01011000
00001010
01010100
01101110
11001000
01010000 01111100 11010010 01010000
01101100
11000000
11111111
01011100
10110001
11100110
01100100 10100100 11100110 01000100
10100000
           
           



Up

Exercícios

Laboratório 5 - Aplicação de operações aritméticas entre duas imagens digitais ou entre duas bandas de uma imagem digital

.      
Imagens TM_3.jpg (esquerda) e TM_4.jpg  (direita)
       Observação: O relatório deve ser digital e deve ser enviado para o e-mail do professor ( carlos@dpi.inpe.br ) contendo:
Identificação do aluno ( nome, nro de matrícula, outros...) e data.
Nome do experimento do laboratório.
Uma introdução explicitando o objetivo do trabalho do laboratório.
Uma descrição da  metodologia utilizada para resolver o problema.
Uma descrição dos resultados obtidos com análises pertinentes.
Uma seção de conclusão com opiniões próprias de cada aluno e sugestões gerais relacionadas com o experimento.

   
Exercícios gerais

1. Faça operações utilizando o SPRING (Ative um Plano de Informação da categoria Imagem. Ative a função de operações aritméticas no menu Imagem do SPRING).  Utilize essa função  para executar operações aritméticas sobre duas bandas presentes no seu banco de dados. Aplique operações de  realce por contraste na banda de saída, quando necessário.
 
2. Suponha que você tenha as imagens X e Y abaixo codificadas com 3 bits. Calcule a imagem de saída Z aplicando sobre X e Y as operações: (a) soma; (b) subtração; (c) divisão; (d) OU e (e) AND.  Calcule os parâmetros da equação linear que realce a imagem de saída supondo que ela esteja codificada com 5 bits.

                   1 5 3 6              6 3 2 4
              X = 3 7 0 2      Y =  4 5 7 0 
                    6 5 5 1              7 1 3 2

3. Explique como você faria para aplicar operações matemáticas em imagens coloridas, cada qual com a s suas 3 componentes R, G e B.

4. A imagem resultante de operação booleana AND entre duas imagens de entrada deverá conter média menor ou igual à menor média das imagens de entrada. Voce concorda com essa afirmativa? Explique.

5. A imagem resultante de operação booleana OR entre duas imagens de entrada deverá conter média menor ou igual à menor média das imagens de entrada. Voce concorda com essa afirmativa? Explique.




Up

Apêndice1: Trabalhando com a classe  BufferedImage

// importação de pacotes do AWT para trabalhar com classe BufferedImage

            import java.awt.image.BufferedImage;
            import java.awt.image.Raster;
            import java.awt.image.WritableRaster;

// Instanciação de um objeto da classe BufferedImage

            BufferedImage ima_in, ima_out;

// Carregando de uma imagem através da classe BufferedImage

            BufferedImage ima_in = null;

String image_name = "nemo500-270.jpg";

            File file = new File(image_name);

            try {
                    ima_in= ImageIO.read(file);
            } catch(Exception e) {
                        System.out.println("Imagem '" + image_name + "' nao existe.");
                        System.exit(0);
            }

// Lendo informações do cabeçalho de uma imagem aberta

            int ncol = ima_in.getWidth();
            int nlin =  ima_in.getHeight();
            int type = ima_in.getType();

// Criando uma imagem de saída através da classe BufferedImage

            BufferedImage ima_out  = new BufferedImage(ncol, nlin,
                                BufferedImage.TYPE_BYTE_GRAY);   // 10 - TYPE_BYTE_GRAY

// Obtendo acesso aos dados (matriz) de uma BufferedImage

           Raster raster = ima_in.getRaster(); // declara e instancia objeto raster só para leitura

           WritableRaster wraster = ima_out.getRaster(); // declara e instancia objeto raster para escrita

// Lendo dados das bandas de uma imagem aberta

           int r = raster.getSample(x,y,0);  // le dado da banda 0 da imagem de entrada
           int g = raster.getSample(x,y,1);  // le dado da banda 1 da imagem de entrada
           int b = raster.getSample(x,y,2);  // le dado da banda 2 da imagem de entrada

//  Escrevendo dados de uma banda de uma imagem criada

           int valor_saida = (r+g+b)/3;

wraster.setSample(x,y,0,valor_saida);




Up

Apêndice2: Programa Java ProcessaeMostraImagem.java

O programa abaixo deve servir de base para o aluno desenvolver um programa que implemente a operação de cálculo do IVDN utilizando duas imagens digitais de entrada.


// Este programa em Java possibilita realizar vários processamentos em uma imagem
// de entrada (nivel de cinza ou colorida) armazenada no formato jpeg (.jpg)
// O resultado do processamento é uma imagem em níveis de cinza codificada com 8 bits.
// O Programa permite ler a imagem de entrada, processa-la e mostrar a imagem processada em um frame.

// pacote de classes de Entrada e Saida
import java.io.File;

// pacotes do AWT
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;


// pacote Swing para definir interface gráfica
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.ImageIcon;

// leitura em modo imediato - para J2SE 1.4+
import javax.imageio.ImageIO;

public class ProcessaeMostraImagem extends JFrame {

  public static void main(String args[]) {

    // Mostra JFrame decorado pelo Swing
        JFrame.setDefaultLookAndFeelDecorated(true);

    // Carrega e Visualiza imagem original

        String ima_name_in = "nemo500-270.jpg";
        String ima_name_out = "nemo500-270ncpond.jpg";
        BufferedImage ima_in, ima_out;

        ima_in = CarregaImagem(ima_name_in);

        ProcessaeMostraImagem frame1 = new ProcessaeMostraImagem();

        frame1.MostraImagem(ima_in, ima_name_in);


    // Carrega e processa imagem original e mostra imagem processada

         ima_out = ProcessaImagem(ima_in);

         ProcessaeMostraImagem frame2 = new ProcessaeMostraImagem();

        frame2.MostraImagem(ima_out, ima_name_out);

         SalvaImagem(ima_out, ima_name_out);
      }

//  public ProcessaeMostraImagem (){  };

  public static BufferedImage CarregaImagem(String image_name)   {
    // Associa objeto BufferedImage com <arquivo_imagem>
        BufferedImage ima_in = null;

    // Carrega imagem

        File file = new File(image_name);
        try {
          ima_in= ImageIO.read(file);
        } catch(Exception e) {
              System.out.println("Imagem '" + image_name + "' nao existe.");
              System.exit(0);
        }

        System.out.println("Nome da Imagem: "+image_name+" Tipo da Imagem: "+ima_in.getType());
        System.out.println("Tamanho da Imagem: Colunas "+ima_in.getWidth()+" Linhas "+ima_in.getHeight());

        return ima_in;

    }

    public static void SalvaImagem(BufferedImage dest, String image_name) {

        try {

            ImageIO.write(dest, "jpg", new File(image_name));

        } catch (Exception e) {

            System.out.println("Problema gravando arquivo. ");
            System.exit(0);
        }
    }

    public static BufferedImage ProcessaImagem (BufferedImage ima_in) {

    // Associa objeto BufferedImage com <arquivo_imagem> níveis de cinza (10)

        BufferedImage ima_out  = new BufferedImage(ima_in.getWidth(),ima_in.getHeight(),BufferedImage.TYPE_BYTE_GRAY); // 10 - TYPE_BYTE_GRAY

     // Recupera matriz das imagens de entrada e saida

        Raster raster = ima_in.getRaster(); // declara e instancia objeto raster soh para leitura
        WritableRaster wraster = ima_out.getRaster(); // declara e instancia objeto raster para escrita

    // Processa valores da imagem de entrada e armazena na imagem de saida

        int r, g, b, valorn;

        for(int y=0; y<ima_in.getHeight(); y++)
            for(int x=0; x<ima_in.getWidth(); x++){

                r = raster.getSample(x,y,0);  // le dado da banda 0 da imagem de entrada
                g = raster.getSample(x,y,1);  // le dado da banda 1 da imagem de entrada
                b = raster.getSample(x,y,2);  // le dado da banda 2 da imagem de entrada

//                valorn = r;  // Transfere Banda 0 (r) da imagem
//                valorn = g;  // Transfere Banda 1 (g) da imagem
//                valorn = b;  // Transfere Banda 2 (b) da imagem

//                Criacao de Imagens níveis de Cinza a partir das bandas r, g e b
//                valorn = (r+g+b)/3;    // Pela média das 3 bandas
//                valorn = (Maximo(r,g,b)+Minimo(r,g,b))/2; // Pela Média entre Maximo e Minimo
                valorn = (int)(.299*(double)r + .587*(double)g + .114*(double)b); // Pela Média Ponderada das 3 bandas

//                valorn = 255-valorn; // Inverte Imagem

//                if(valorn<127)valorn = 0; else valorn=255; // Binariza imagem pelo nivel 127

                wraster.setSample(x,y,0,valorn);

                if(y<=3 && x<=3)
                    System.out.println(y+" "+x+"  Valores "+r+"  "+g+"  "+b+" "+valorn);

            }

        return ima_out;
    };

    public void MostraImagem(BufferedImage ima, String image_name) {

    // Define GUI com objetos do Swing
           JLabel lsrc2 = new JLabel(new ImageIcon(ima));
        getContentPane().add(new JScrollPane(lsrc2));

    // Atribui nome e tamanho ao frame
           setTitle("Java2DImageDisplay: =>" + image_name);
          setSize(ima.getWidth()+40, ima.getHeight()+40);

        setVisible(true);

    // Encerra a aplicação clicando no "close"
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


  }


  public static int Minimo(int a, int b, int c){
      int min=a;
      if(b<min)min=b; if(c<min)min=c;
      return min;
  }

   public static int Maximo(int a, int b, int c){
      int max=a;
      if(b>max)max=b; if(c>max)max=c;
      return max;
  }


}


Apêndice3: Estrutura básica de um Programa em C para implementar operações matemáticas entre imagens digitais.


#include <stdio.h>
#include <malloc.h>

main()
{

FILE *fpiX, *fpiY, *fpo;
unsigned char *bufferX, *bufferY;
int nlin,ncol,l,c,cor,Xmin=10000, Xmax=-1000;
float ganho, offset, result;

// Abre arquivo de entrada com imagem X
   fpiX = fopen("tm3_or_393x433.raw","rb");
   if(fpiX==NULL){
                 printf("Arquivo da imagem Xnao existe");
                 getchar(); return 1;
                 }
                
// Abre arquivo de entrada com imagem Y


// COMPLETAR



// Cria arquivo de saida
     fpo = fopen("IDVN.raw","wb");
     if(fpo==NULL){
                 printf("Arquivo nemosaida.raw nao existe");
                 getchar(); return 1;
                 }                
// Le nro de linhas e colunas do usuario
       printf("\nEntre com o nro de linhas da imagem: ");
       scanf("%d",&nlin);
      
       printf("\nEntre com o nro de colunas da imagem: ");
       scanf("%d",&ncol);

// Le valores de ganho e offset
       printf("\nEntre com o valor do ganho: ");
       scanf("%f",&ganho);
      
       printf("\nEntre com o valor do offset: ");
       scanf("%f",&offset);
     
// Aloca dinamicamente o buffer de cada linha da imagem X
       bufferX = malloc(ncol*sizeof(char));
       if( bufferX == NULL)
           {
                 printf("Nao alocou memoria para o buffer");
                 getchar(); return 1;
           }                

//  Aloca dinamicamente o buffer de cada linha da imagem Y


//   COMPLETAR

     
//  Aplica a operação de IDVN sobre as imagens de entrada e escreve resultado na imagem de saida

      for(l=0; l<nlin; l++)
      {
//          Le linha l do arquivo de entrada da imagem X  em bufferX
             fread(bufferX, sizeof(char), ncol, fpo);

//          Le linha l do arquivo de entrada da imagem Y em bufferY

             //      COMPLETAR
      
//        Opera sobre as linhas das duas imagens de entrada
          for(c=0; c<ncol; c++){
                   result = ((float)bufferY[c]-(float)bufferX[c])/((float)bufferY[c]+(float)bufferX[c]);
                   bufferX[c] = (unsigned char) (ganho*result + offset);
          }

//        Escreve linha l no arquivo de saida
          fwrite(bufferX, sizeof(char), ncol, fpo);    

      }

//     Fecha arquivos e libera memória alocada
       fclose (fpiX);
       fclose (fpiY);
       fclose (fpo);
       free (bufferX);  free(bufferY);
       getchar();

}

Up