Intro Pi Java

146
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 1 /146 Introdução ao Processamento de Imagens Digitais em Java / API JAI Escola de Verão da Unifesp Rafael Santos

Transcript of Intro Pi Java

Page 1: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 1 /146

Introdução ao Processamento de Imagens Digitais em Java / API JAI

Escola de Verão da Unifesp

Rafael Santos

Page 2: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 2 /146

Objetivo

● Apresentar conceitos, técnicas e exemplos básicos de aplicação de processamento de imagens digitais.

● Implementações em Java opcionalmente com a API JAI (Java Advanced Imaging).

● Parte reduzida do livro on-line Java Image Processing Cookbook (http://www.lac.inpe.br/JIPCookbook/index.jsp).

● Código!

Page 3: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 3 /146

Introdução

Page 4: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 4 /146

Aplicações de Processamento de Imagens

● Sensoriamento Remoto:– Geologia (estudo da composição da superfície).– Agricultura (determinação da cobertura vegetal).– Engenharia Florestal (idem).– Cartografia (mapeamento da superfície).– Meteorologia.

● Medicina e Biologia.● Astronomia (macro) e Física (micro).● Produção e Controle de Qualidade.● Segurança e Monitoramento.● Documentos, Web, etc.

Page 5: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 5 /146

Imagens Digitais

● Imagem = matriz de pixels.● Pixel = medida, conjunto de medidas ou índice para tabela

de valores.● Metadados: dados adicionais sobre a imagem.

Page 6: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 6 /146

Imagens e Pixels

34 34 42 42 34 12 4229 29 49 49 29 30 49

105 105 97 97 105 105 9712 34 14 34 34 42 3430 29 48 29 29 49 29

105 105 97 105 105 97 10534 34 69 36 12 12 4229 29 76 54 30 30 49

105 105 97 104 105 105 9734 12 85 113 36 34 3429 30 103 108 54 29 29

105 105 85 72 104 105 10534 34 58 100 90 14 3429 29 53 123 115 48 29

105 105 105 66 78 97 10512 34 42 90 107 85 4230 29 49 115 136 103 49

105 105 97 78 58 85 9734 42 35 85 111 105 4229 49 41 103 132 119 49

105 97 105 85 60 86 97

Page 7: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 7 /146

Tipos mais comuns

● Câmera Digital– 3264x2448 elementos sensores– Resolução: não se aplica– 3 bandas– Cada pixel é discretizado com

valores entre 0 e 255

● Scanner– Array móvel de elementos sensores– Resolução: 2400 DPI ou mais– 3 bandas– Discretização variável

Page 8: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 8 /146

Outros Tipos de Imagens Digitais

● Não somos limitados à imagens como as de câmeras e scanners!– Pixels podem ter mais que três valores associados a eles.– Pixels podem ter valores fora do tradicional intervalo [0, 255].– Pixels não precisam representar valores inteiros ou positivos!

● Exemplos:– Imagens multispectrais e hiperspectrais.– Imagens de modelos de terreno, médicas (Raio-X), etc.

Page 9: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 9 /146

Outros Tipos de Imagens Digitais

Page 10: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 10 /146

Outros Tipos de Imagens Digitais

Page 11: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 11 /146

Imagens Digitais: Multiespectrais

Page 12: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 12 /146

Imagens Digitais: Hiperespectrais

http://www.cossa.csiro.au/hswww/Overview.htm

Page 13: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 13 /146

Processamento de Imagens em Java

Page 14: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 14 /146

Processamento de Imagens em Java

● Preciso saber Java?– Ajuda e muito, mas não é imprescindível.– Experiência com C++, C#, outras linguagens pode ajudar.

● Todo o código está no livro on-line (http://www.lac.inpe.br/JIPCookbook), completo e comentado.

Page 15: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 15 /146

Processamento de Imagens em Java

● Popularidade e flexibilidade de Java.● Temos APIs para representação, visualização e I/O simples

de imagens como parte do JSE.● Temos a API Java Advanced Imaging para operações muito

mais poderosas, flexíveis e complexas!● E a questão da performance?

– Melhor do que esperado!– Não estou preocupado com real time.– Mais valor à clareza e simplicidade de código.

Page 16: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 16 /146

Processamento de Imagens em Java: JAI

● Java (Swing) tem classes e operadores básicos.● Java Advanced Imaging

– API adicional (download separado).– Projeto do java.net – público mas não totalmente aberto.

● Muitos operadores específicos para processamento de imagens.

● Execução postergada e cadeias de operadores.● Representação mais poderosa e flexível de imagens (tiles).● Alguns operadores acelerados (implementação nativa).● Dúvida: terá apoio da Oracle?

Page 17: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 17 /146

Representação de Imagens em Java

Page 18: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 18 /146

Representação de Imagens: Java

● Formato de representação na memória é diferente de formato de arquivo!

● Existem limitações mútuas.

RenderedImage

ColorModel Raster

SampleModel

DataBuffer

ColorSpace

Page 19: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 19 /146

Representação de Imagens: TiledImage (JAI)

Page 20: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 20 /146

Representação de Imagens

RenderedImage

PlanarImage

WritableRenderedImage BufferedImage

RenderedOp

ImageJAI

TiledImageAPI JAI

Page 21: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 21 /146

Criando Imagens em Java

Page 22: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 22 /146

Criando Imagens (sem JAI)

Imagens simples (RGB, puro preto-e-branco, níveis de cinza; pixels são arrays de bytes).

1.Criamos instância de BufferedImage.2.Criamos instância de WritableRaster associada à BufferedImage.

3.Manipulamos os pixels do WritableRaster.

Page 23: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 23 /146

Criando Imagens (sem JAI)

public static void main(String[] args) throws IOException { int width = 256; int height = 256; BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); WritableRaster raster = image.getRaster(); int[] cor1 = new int[]{255,0,0}; int[] cor2 = new int[]{0,0,255}; int cont=0; for(int h=0;h<height;h++) for(int w=0;w<width;w++) { if ((((w/32)+(h/32)) % 2) == 0) raster.setPixel(w,h,cor1); else raster.setPixel(w,h,cor2); } ImageIO.write(image,"PNG",new File("checkerboard.png")); }

Page 24: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 24 /146

Criando Imagens (com JAI)

Imagens simples (RGB, puro preto-e-branco, níveis de cinza) ou multibandas; pixels podem ser arrays de qualquer tipo nativo.

1.Criamos instância de SampleModel usando RasterFactory.

2.Criamos um TiledImage com este SampleModel.3.Criamos um WritableRaster a partir da TiledImage.

4.Manipulamos os pixels do WritableRaster.

Page 25: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 25 /146

Criando Imagens (com JAI)

int width = 640; int height = 640;SampleModel sampleModel =    RasterFactory.createBandedSampleModel(DataBuffer.TYPE_BYTE,                                         width,height,1);  TiledImage tiledImage =    new TiledImage(0,0,width,height,0,0,sampleModel,null);WritableRaster wr = tiledImage.getWritableTile(0,0);for(int h=0;h<height/32;h++)  for(int w=0;w<width/32;w++)    {    int[] fill = new int[32*32]; // A block of pixels...    Arrays.fill(fill,(int)(Math.random()*256));     wr.setSamples(w*32,h*32,32,32,0,fill);     }JAI.create("filestore",tiledImage,           "jaigl.png","PNG");

Page 26: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 26 /146

Criando Imagens (com JAI)

Para imagens com tiles é um pouco mais complicado...

1.Criamos instância de SampleModel usando RasterFactory.

2.Criamos um TiledImage com este SampleModel.3.Para cada tile:

1. criamos um WritableRaster a partir da TiledImage.

2. Manipulamos os pixels do WritableRaster.

Page 27: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 27 /146

Criando Imagens (com JAI)

int width = 483; int height = 483;int tWidth = 64; int tHeight = 64;SampleModel sampleModel =    RasterFactory.createBandedSampleModel(DataBuffer.TYPE_BYTE,                                          tWidth,tHeight,3);ColorModel cm = TiledImage.createColorModel(sampleModel);TiledImage tiledImage =    new TiledImage(0,0,width,height,0,0,sampleModel,cm);

// Create the colors.int[] red = new int[]{255,0,0};int[] green = new int[]{0,255,0};int[] blue = new int[]{0,0,255};int[] yellow = new int[]{255,255,0};int[] black = new int[]{0,0,0};

Page 28: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 28 /146

Criando Imagens (com JAI)

for(int th=tiledImage.getMinTileY();th<=tiledImage.getMaxTileY();th++) for(int tw=tiledImage.getMinTileX();tw<=tiledImage.getMaxTileX();tw++)   {   WritableRaster wr = tiledImage.getWritableTile(tw,th);   for(int ih=0;ih<tHeight;ih++)     for(int iw=0;iw<tWidth;iw++)       {       int w = wr.getMinX()+iw;        int h = wr.getMinY()+ih;        if ((w >= 17)&&(w < 17+216)&&(h >= 17)&&(h < 17+216))          wr.setPixel(w,h,red);             else if ((w >= 250)&&(w < 250+216)&&(h >= 17)&&(h < 17+216))           wr.setPixel(w,h,green);             else if ((w >= 17)&&(w < 17+216)&&(h >= 250)&&(h < 250+216))          wr.setPixel(w,h,yellow);             else if ((w >= 250)&&(w < 250+216)&&(h >= 250)&&(h < 250+216))          wr.setPixel(w,h,blue);             else wr.setPixel(w,h,black);       }    }

Page 29: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 29 /146

Criando Imagens (com JAI)

TIFFEncodeParam tep = new TIFFEncodeParam();tep.setWriteTiled(true);tep.setTileSize(tWidth,tHeight);JAI.create("filestore",tiledImage,"rgbtile.tiff","TIFF",tep);

Page 30: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 30 /146

Armazenando e Recuperando Imagens

Page 31: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 31 /146

Entrada e Saída

● Sem JAI (BufferedImage):

● Com JAI (PlanarImage):

public static void main(String[] args) throws IOException { File f = new File(args[0]); BufferedImage image = ImageIO.read(f); System.out.println("Dimensões: "+ image.getWidth()+"x"+image.getHeight()+" pixels"); }

public static void main(String[] args) throws IOException { PlanarImage image = JAI.create("fileload",args[0]); System.out.println("Dimensões: "+ image.getWidth()+"x"+image.getHeight()+" pixels"); }

Page 32: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 32 /146

Entrada e Saída

● Sem JAI (BufferedImage):

● Com JAI (PlanarImage):

public static void main(String[] args) throws IOException { int width = 256; int height = 256; BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); ... ImageIO.write(image,"PNG",new File("checkerboard.png")); }

public static void main(String[] args) throws IOException { ... TiledImage tiledImage = new TiledImage(0,0,width,height,0,0,sampleModel,colorModel); ... JAI.create("filestore",tiledImage,"floatpattern.tif","TIFF"); }

Page 33: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 33 /146

Acesso Direto a Pixels

Page 34: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 34 /146

Acesso a pixels (sem JAI)

public static void main(String[] args) throws IOException { File f = new File(args[0]); BufferedImage imagem = ImageIO.read(f); Raster raster = imagem.getRaster(); int[] pixel = new int[3]; int brancos = 0; for(int h=0;h<imagem.getHeight();h++) for(int w=0;w<imagem.getWidth();w++) { raster.getPixel(w,h,pixel); if ((pixel[0] == 255) && (pixel[1] == 255) && (pixel[2] == 255)) brancos++; } System.out.println(brancos+" pixels brancos"); }

Memória!

Page 35: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 35 /146

Acesso a pixels (com JAI)

public static void main(String[] args) throws IOException { File f = new File(args[0]); BufferedImage imagem = ImageIO.read(f); RandomIter iterator = RandomIterFactory.create(imagem,null); int[] pixel = new int[3]; int brancos = 0; for(int h=0;h<imagem.getHeight();h++) for(int w=0;w<imagem.getWidth();w++) { iterator.getPixel(w,h,pixel); if ((pixel[0] == 255) && (pixel[1] == 255) && (pixel[2] == 255)) brancos++; } System.out.println(brancos+" pixels brancos"); }

● Existem também RectIter e RookIter.

Page 36: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 36 /146

Exibindo Imagens

Page 37: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 37 /146

Visualização de Imagens

● Componentes de interfaces gráficas para mostrar imagens.● Geralmente bem simples, melhorias como interatividade,

processamento, etc. ficam por conta do programador...– … o que é fácil de fazer graças ao mecanismo de herança!

● Conhecimentos de programação de interfaces gráficas em Java são úteis: só conhecimento de design não adiantam.

JFrameComponentes de UI

Page 38: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 38 /146

Display de Imagens (sem JAI)

public static void main(String[] args) throws IOException { BufferedImage image = ImageIO.read(new File(args[0])); JFrame frame = new JFrame("Display Image: "+args[0]); ImageIcon icon = new ImageIcon(image); JLabel imageLabel = new JLabel(icon); frame.getContentPane().add(new JScrollPane(imageLabel)); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(600,300); frame.setVisible(true); }

● BufferedImage → ImageIcon → JLabel (JScrollPane).

Page 39: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 39 /146

Display de Imagens (sem JAI)

Page 40: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 40 /146

Display de Imagens (com JAI)

public static void main(String[] args) throws IOException { BufferedImage image = ImageIO.read(new File(args[0])); JFrame frame = new JFrame("Display Image: "+args[0]); DisplayJAI display = new DisplayJAI(image); frame.getContentPane().add(new JScrollPane(display)); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(600,300); frame.setVisible(true); }

● DisplayJAI é mais flexível, permite alguma interação (não implementada).

● Não é parte da API JAI (!?).

Page 41: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 41 /146

Exibindo Imagens(Soluções Específicas)

Page 42: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 42 /146

Imagens Sincronizadas

● Componente que mostra duas imagens sincronizadas:– Modificação no viewport de uma causa modificação no

viewport da outra.● Composição de instâncias de DisplayJAI.

Page 43: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 43 /146

Imagens Sincronizadas

public class DisplayTwoSynchronizedImages extends JPanel implements AdjustmentListener { protected DisplayJAI dj1; protected DisplayJAI dj2; protected JScrollPane jsp1; protected JScrollPane jsp2;

● Exibição de duas instâncias de DisplayJAI de forma sincronizada.

Page 44: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 44 /146

Imagens Sincronizadas

public DisplayTwoSynchronizedImages(RenderedImage im1, RenderedImage im2) { super(); // Cria componente com duas imagens com JScrollPanes setLayout(new GridLayout(1,2)); dj1 = new DisplayJAI(im1); dj2 = new DisplayJAI(im2); jsp1 = new JScrollPane(dj1); jsp2 = new JScrollPane(dj2); add(jsp1); add(jsp2); // Registra listeners para os scroll bars do JScrollPanes jsp1.getHorizontalScrollBar().addAdjustmentListener(this); jsp1.getVerticalScrollBar().addAdjustmentListener(this); jsp2.getHorizontalScrollBar().addAdjustmentListener(this); jsp2.getVerticalScrollBar().addAdjustmentListener(this); }

Page 45: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 45 /146

Imagens Sincronizadas

public void adjustmentValueChanged(AdjustmentEvent e) { if (e.getSource() == jsp1.getHorizontalScrollBar()) jsp2.getHorizontalScrollBar().setValue(e.getValue()); if (e.getSource() == jsp1.getVerticalScrollBar()) jsp2.getVerticalScrollBar().setValue(e.getValue()); if (e.getSource() == jsp2.getHorizontalScrollBar()) jsp1.getHorizontalScrollBar().setValue(e.getValue()); if (e.getSource() == jsp2.getVerticalScrollBar()) jsp1.getVerticalScrollBar().setValue(e.getValue()); }

}

Page 46: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 46 /146

Imagens Sincronizadas: Exemplo

public class Borda { public static void main(String[] args) { PlanarImage imagem = JAI.create("fileload", args[0]); float[] kernelMatrix = { -1, -2, -1, 0, 0, 0, 1, 2, 1 }; KernelJAI kernel = new KernelJAI(3,3,kernelMatrix); PlanarImage bordas = JAI.create("convolve",imagem,kernel); JFrame frame = new JFrame("Bordas horizontais"); frame.add(new DisplayTwoSynchronizedImages(imagem,bordas)); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }

Page 47: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 47 /146

Imagens Sincronizadas: Exemplo

Page 48: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 48 /146

Imagens Substitutas

● Uso de imagens substitutas (surrogate images):● Criamos uma imagem normalizada com pixels entre valores

0-255● Transformamos o tipo da imagem para bytes.● Uma classe que herda de DisplayJAI pode executar

estes passos.

Page 49: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 49 /146

Imagens Substitutaspublic class DisplaySurrogateImage extends DisplayJAI { protected PlanarImage surrogateImage; protected int width,height; public DisplaySurrogateImage(PlanarImage image) { width = image.getWidth(); height = image.getHeight(); // Recuperamos valores extremos da imagem. ParameterBlock pbMaxMin = new ParameterBlock(); pbMaxMin.addSource(image); PlanarImage extrema = JAI.create("extrema", pbMaxMin); double[] allMins = (double[])extrema.getProperty("minimum"); double[] allMaxs = (double[])extrema.getProperty("maximum"); double minValue = allMins[0]; double maxValue = allMaxs[0]; for(int v=1;v<allMins.length;v++) { if (allMins[v] < minValue) minValue = allMins[v]; if (allMaxs[v] > maxValue) maxValue = allMaxs[v]; }

Page 50: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 50 /146

Imagens Substitutas

// Reescalamos os níveis de cinza da imagem. double[] subtract = new double[1]; subtract[0] = minValue; double[] multiplyBy = new double[1]; multiplyBy[0] = 255./(maxValue-minValue); ParameterBlock pbSub = new ParameterBlock(); pbSub.addSource(image); pbSub.add(subtract); surrogateImage = (PlanarImage)JAI.create("subtractconst",pbSub); ParameterBlock pbMult = new ParameterBlock(); pbMult.addSource(surrogateImage); pbMult.add(multiplyBy); surrogateImage = (PlanarImage)JAI.create("multiplyconst",pbMult); // Convertemos para bytes. ParameterBlock pbConvert = new ParameterBlock(); pbConvert.addSource(surrogateImage); pbConvert.add(DataBuffer.TYPE_BYTE); surrogateImage = JAI.create("format", pbConvert); // Usamos esta imagem para display. set(surrogateImage); } }

Page 51: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 51 /146

Imagens Substitutaspublic class DemonstraDisplaySurrogateImage { public static void main(String[] args) { PlanarImage image = JAI.create("fileload", args[0]); JFrame frame = new JFrame("Mostrando "+args[0]); frame.getContentPane().add( new JScrollPane(new DisplaySurrogateImage(image))); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } }

Page 52: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 52 /146

Imagens Substitutas: LUTs

● Uso de imagens substitutas (surrogate images) com LUTs:● Look-up Tables (LUTs): tabela de cores.

00 00 00 00

00 00 10 00

00 00 21 00

00 11 22 01

0 1131

255 0233

0 1240

255 25525510 11 22 11 0

00

11

22

32

23

00

00

01

12

12

00 11 32 01 022 12

00 00 21 00 022 01

00 00 10 00 011 00

00 00 00 00 000 00

0

0

0

0

0

1

2

3

R BGÍndice

Page 53: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 53 /146

Imagens Substitutas: LUTspublic void setLUT(short[][] lut)  {  SampleModel sampleModel = surrogateImage.getSampleModel();  SampleModel newSampleModel =     RasterFactory.createBandedSampleModel(DataBuffer.TYPE_BYTE,                        sampleModel.getWidth(),sampleModel.getHeight(),3);  byte[] reds = new byte[256];  byte[] greens = new byte[256];  byte[] blues = new byte[256];  for(int i=0;i<256;i++)    {    reds[i] = (byte)lut[i][0];     greens[i] = (byte)lut[i][1];    blues[i] = (byte)lut[i][2];    }  ColorModel colorModel = new IndexColorModel(8,256,reds,greens,blues);  ImageLayout layout = new ImageLayout(surrogateImage);  layout.setColorModel(colorModel);  HashMap<RenderingHints.Key,ImageLayout> map =     new HashMap<RenderingHints.Key,ImageLayout>();  map.put(JAI.KEY_IMAGE_LAYOUT,layout);  RenderingHints hints = new RenderingHints(map);  ParameterBlock pb = new ParameterBlock();  pb.addSource(surrogateImage);  PlanarImage newSurrogateImage = JAI.create("format",pb,hints);  set(newSurrogateImage);  }

Page 54: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 54 /146

Imagens Substitutas: LUTs

  /** The sin lut (rgb order) */  public final static short[][] sin_rgb()    {    short[][] lut = new short[256][3];    for(short i=0;i<256;i++)      {      lut[i][0] = (short)(127*(1+Math.sin(Math.PI*(i­127)/255)));      lut[i][1] = (short)(127*(1+Math.sin(Math.PI*(i    )/255)));      lut[i][2] = (short)(127*(1+Math.sin(Math.PI*(i+127)/255)));      }    return lut;    }

  /** The inverted gray lut */  public final static short[][] invGray()    {    short[][] lut = new short[256][3];    for(short i=0;i<256;i++)      {      lut[i][0] = (short)(255­i);      lut[i][1] = (short)(255­i);      lut[i][2] = (short)(255­i);      }    return lut;    }

Page 55: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 55 /146

Imagens Substitutas: LUTs

Page 56: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 56 /146

Desenhando em Imagens

● Podemos obter contextos gráficos de BufferedImages e PlanarImages..

– .. e usá-los para desenhar sobre a imagem.● As imagens são modificadas (na memória) e podem ser

visualizadas e/ou armazenadas com os gráficos.

Page 57: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 57 /146

Desenhando em ImagensBufferedImage baseImage = ImageIO.read(new File("sjc_region.png"));int[][] coords = new int[][] {      {714,219},    {822,256},    {797,329},    {710,300},    {711,293},    {666,271}};Path2D.Float regionOfInterest = new Path2D.Float();boolean isFirst = true;double firstX=0,firstY=0;for(int[] coord:coords)  {  int x = coord[0]; int y = coord[1];  if (isFirst)     {     regionOfInterest.moveTo(x,y);     firstX = x;    firstY = y;    isFirst = false;     }  else { regionOfInterest.lineTo(x,y); }  }regionOfInterest.lineTo(firstX,firstY);

Page 58: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 58 /146

Desenhando em ImagensPath2D.Float pathForWholeImage = new Path2D.Float();pathForWholeImage.moveTo(0,0);pathForWholeImage.lineTo(baseImage.getWidth(),0);pathForWholeImage.lineTo(baseImage.getWidth(),baseImage.getHeight());pathForWholeImage.lineTo(0,baseImage.getHeight());pathForWholeImage.lineTo(0,0);Area wholeImage = new Area(pathForWholeImage);wholeImage.subtract(new Area(regionOfInterest));

Graphics2D g2d = (Graphics2D)baseImage.getGraphics();g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,                     RenderingHints.VALUE_ANTIALIAS_ON);g2d.setColor(new Color(255,255,255,100));g2d.fill(wholeImage);g2d.setStroke(new BasicStroke(5f));g2d.setColor(new Color(255,0,0,200));g2d.draw(regionOfInterest);

JFrame frame = new JFrame("Highlighting image regions");ImageIcon icon = new ImageIcon(baseImage);JLabel label = new JLabel(icon);frame.getContentPane().add(new JScrollPane(label));frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack();     frame.setVisible(true); 

Page 59: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 59 /146

Desenhando em Imagens

Page 60: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 60 /146

Operadores da API JAI

Page 61: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 61 /146

Operadores da API JAI: Introdução

● Classe JAI provê método create.● Vários operadores são registrados, chamados de forma

unificada. ● Parâmetros (se houver) são passados através de instância

de ParameterBlock. ● Método retorna instância de RenderedOp → cast para PlanarImage se necessário.

Page 62: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 62 /146

Relembrando: Representação de Imagens

RenderedImage

PlanarImage

WritableRenderedImage BufferedImage

RenderedOp

ImageJAI

TiledImageAPI JAI

Page 63: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 63 /146

Operadores da API JAI: invert

● Inverte os valores dos pixels.– Tipos com sinal: saída = -entrada– Tipos sem sinal: saída = máximo - entrada

public static void main(String[] args) { PlanarImage input = JAI.create("fileload", args[0]); PlanarImage output = JAI.create("invert", input); JFrame frame = new JFrame(); frame.setTitle("Invert image "+args[0]); frame.getContentPane().add( new DisplayTwoSynchronizedImages(input,output)); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); }

Page 64: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 64 /146

Operadores da API JAI: invert

Page 65: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 65 /146

Operadores da API JAI: binarize

● Transforma pixels em valores binários por comparação com constante (1 se ≥ constante).

public static void main(String[] args) { PlanarImage imagem = JAI.create("fileload", args[0]); ParameterBlock pb = new ParameterBlock(); pb.addSource(imagem); pb.add(127.0); PlanarImage binarizada = JAI.create("binarize", pb); JFrame frame = new JFrame("Imagem binarizada"); frame.add(new DisplayTwoSynchronizedImages(imagem,binarizada)); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }

Page 66: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 66 /146

Operadores da API JAI: binarize

Page 67: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 67 /146

Operadores da API JAI: convolve

● Convolução com um kernel.– Este exemplo: suavização.

public static void main(String[] args) { PlanarImage imagem = JAI.create("fileload", args[0]); float[] kernelMatrix = { 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f}; KernelJAI kernel = new KernelJAI(5,5,kernelMatrix); PlanarImage bordas = JAI.create("convolve",imagem,kernel); JFrame frame = new JFrame("Suavização da imagem"); frame.add(new DisplayTwoSynchronizedImages(imagem,bordas)); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }

Page 68: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 68 /146

Operadores da API JAI: convolve

Page 69: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 69 /146

Operadores da API JAI: convolve

● Convolução com um kernel.– Este exemplo: detecção de bordas horizontais (Sobel).

public static void main(String[] args) { PlanarImage imagem = JAI.create("fileload", args[0]); float[] kernelMatrix = { -1, -2, -1, 0, 0, 0, 1, 2, 1 }; KernelJAI kernel = new KernelJAI(3,3,kernelMatrix); PlanarImage bordas = JAI.create("convolve",imagem,kernel); JFrame frame = new JFrame("Bordas horizontais"); frame.add(new DisplayTwoSynchronizedImages(imagem,bordas)); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }

Page 70: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 70 /146

Operadores da API JAI: convolve

Page 71: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 71 /146

Operadores da API JAI: dilate

● Expansão de regiões da imagem com elemento estrutural. public static void main(String[] args) { PlanarImage imagem = JAI.create("fileload", args[0]); float[] estrutura = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}; KernelJAI kernel = new KernelJAI(7,7,estrutura); ParameterBlock p = new ParameterBlock(); p.addSource(imagem); p.add(kernel); PlanarImage dilatada = JAI.create("dilate",p); JFrame frame = new JFrame("Imagem dilatada"); frame.add(new DisplayTwoSynchronizedImages(imagem,dilatada)); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }

Page 72: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 72 /146

Operadores da API JAI: dilate

Regiões brancas são dilatadas!

Page 73: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 73 /146

Operadores da API JAI: erode

● Redução de regiões da imagem com elemento estrutural. public static void main(String[] args) { PlanarImage imagem = JAI.create("fileload", args[0]); float[] estrutura = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}; KernelJAI kernel = new KernelJAI(7,7,estrutura); ParameterBlock p = new ParameterBlock(); p.addSource(imagem); p.add(kernel); PlanarImage erodida = JAI.create("erode",p); JFrame frame = new JFrame("Imagem erodida"); frame.add(new DisplayTwoSynchronizedImages(imagem,erodida)); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }

Page 74: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 74 /146

Operadores da API JAI: erode

Regiões brancas são dilatadas!

Page 75: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 75 /146

Operadores da API JAI: rotate

● Rotação dos pixels da imagem em redor de um ponto. public static void main(String[] args) { PlanarImage imagem = JAI.create("fileload",args[0]); float angle = (float)Math.toRadians(45); // Usamos o centro da imagem para rotação float centerX = imagem.getWidth()/2f; float centerY = imagem.getHeight()/2f; ParameterBlock pb = new ParameterBlock(); pb.addSource(imagem); pb.add(centerX); pb.add(centerY); pb.add(angle); pb.add(new InterpolationBilinear()); PlanarImage rotacionada = JAI.create("rotate", pb); JFrame frame = new JFrame("Imagem rotacionada"); frame.add(new DisplayTwoSynchronizedImages(imagem,rotacionada)); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }

Page 76: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 76 /146

Operadores da API JAI: rotate

Coordenadas dos cantos da imagem rotacionada: (-39, -136) – (558, 461)

Page 77: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 77 /146

Translação da Origem de Imagens

Original Região para recorte

Origem 200,200

Tamanho400x300

Região recortada Recorte e translaçãoMínimo

200,200

Tamanho400x300

Máximo600,500

Mínimo 0,0

Tamanho400x300

Máximo400,300

Page 78: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 78 /146

Operadores da API JAI: rotate

● JAI permite imagens com pixels com coordenadas negativas!– DisplayJAI, ImageIO e JAI.create(“filestore”) não.

– Solução: mover a origem da imagem com o operador translate.

public static void main(String[] args) { PlanarImage imagem = JAI.create("fileload",args[0]); float angle = (float)Math.toRadians(45); // Usamos o centro da imagem para rotação float centerX = imagem.getWidth()/2f; float centerY = imagem.getHeight()/2f; ParameterBlock pb = new ParameterBlock(); pb.addSource(imagem); pb.add(centerX); pb.add(centerY); pb.add(angle); pb.add(new InterpolationBilinear()); PlanarImage rotacionada = JAI.create("rotate", pb);

Page 79: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 79 /146

Operadores da API JAI: rotate

// Ajustamos a origem da imagem pb = new ParameterBlock(); pb.addSource(rotacionada); pb.add((float)-rotacionada.getMinX()); pb.add((float)-rotacionada.getMinY()); PlanarImage rotacionadaOK = JAI.create("translate",pb,null); JFrame frame = new JFrame("Imagem rotacionada"); frame.add( new DisplayTwoSynchronizedImages(imagem,rotacionadaOK)); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }

Page 80: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 80 /146

Operadores da API JAI: rotate

Page 81: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 81 /146

Operadores da API JAI: scale

● Aumenta ou diminui a quantidade de pixels na imagem.– Valores dos pixels podem ser interpolados.

public static void main(String[] args) { PlanarImage imagem = JAI.create("fileload",args[0]); float scale = 0.3f; ParameterBlock pb = new ParameterBlock(); pb.addSource(imagem); pb.add(scale); pb.add(scale); pb.add(0.0F); pb.add(0.0F); pb.add(new InterpolationNearest()); PlanarImage reescalada = JAI.create("scale", pb); JFrame frame = new JFrame("Imagem reescalada"); frame.add(new DisplayTwoSynchronizedImages(imagem,reescalada)); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }

Page 82: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 82 /146

Operadores da API JAI: scale

Page 83: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 83 /146

Operadores da API JAI: crop, translate, scale

● Pequena aplicação que recorta e amplia uma região em uma imagem.

● Parâmetros passados pela linha de comando.public static void main(String[] args) { PlanarImage imagem = JAI.create("fileload",args[0]); ParameterBlock pb = new ParameterBlock(); float x = Float.parseFloat(args[1]); float y = Float.parseFloat(args[2]); float w = Float.parseFloat(args[3]); float h = Float.parseFloat(args[4]); float z = Float.parseFloat(args[5]);

Page 84: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 84 /146

Operadores da API JAI: crop, translate, scale

// Recorta pb.addSource(imagem); pb.add(x); pb.add(y); pb.add(w); pb.add(h); PlanarImage recortada = JAI.create("crop",pb,null); // Reposiciona pb = new ParameterBlock(); pb.addSource(recortada); pb.add((float)-x); pb.add((float)-y); PlanarImage recortadaOK = JAI.create("translate",pb,null);

Page 85: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 85 /146

Operadores da API JAI: crop, translate, scale

// Amplia (2 versões) pb = new ParameterBlock(); pb.addSource(recortadaOK); pb.add(z); pb.add(z); pb.add(0.0F); pb.add(0.0F); pb.add(new InterpolationNearest()); PlanarImage resultado1 = JAI.create("scale", pb); pb = new ParameterBlock(); pb.addSource(recortadaOK); pb.add(z); pb.add(z); pb.add(0.0F); pb.add(0.0F); pb.add(new InterpolationBicubic(2)); PlanarImage resultado2 = JAI.create("scale", pb);

Page 86: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 86 /146

Operadores da API JAI: crop, translate, scale

JFrame frame = new JFrame("Recorte ampliado"); frame.add( new DisplayTwoSynchronizedImages(resultado1,resultado2)); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }

java wvc/operadores/Recorta astro013.jpg 461 896 24 27 20

Page 87: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 87 /146

Operadores da API JAI: histogram

● Operador sem imagem resultante: calcula histogramas em uma imagem.– Histogramas são recuperadors como propriedades do RenderedOp resultante.

public static void main(String[] args) { PlanarImage image = JAI.create("fileload", args[0]); // Primeiro histograma com 256 bins. ParameterBlock pb1 = new ParameterBlock(); pb1.addSource(image); pb1.add(null); pb1.add(1); pb1.add(1); pb1.add(new int[]{256}); pb1.add(new double[]{0}); pb1.add(new double[]{256}); PlanarImage dummyImage1 = JAI.create("histogram", pb1); Histogram histo1 = (Histogram)dummyImage1.getProperty("histogram");

Page 88: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 88 /146

Operadores da API JAI: histogram

// Segundo histograma com 32 bins. ParameterBlock pb2 = new ParameterBlock(); pb2.addSource(image); pb2.add(null); pb2.add(1); pb2.add(1); pb2.add(new int[]{32}); pb2.add(new double[]{0}); pb2.add(new double[]{256}); PlanarImage dummyImage2 = JAI.create("histogram", pb2); Histogram histo2 = (Histogram)dummyImage2.getProperty("histogram"); // Exibimos os histogramas usando um componente específico. JFrame f = new JFrame("Histogramas"); DisplayHistogram dh1 = new DisplayHistogram(histo1,"256 bins"); dh1.setBinWidth(2); dh1.setHeight(160); dh1.setIndexMultiplier(1); DisplayHistogram dh2 = new DisplayHistogram(histo2,"32 bins"); dh2.setBinWidth(16); dh2.setHeight(160);dh2.setIndexMultiplier(8); dh2.setSkipIndexes(2); f.getContentPane().setLayout(new GridLayout(2,1)); f.getContentPane().add(dh1); f.getContentPane().add(dh2); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.pack(); f.setVisible(true); }

Page 89: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 89 /146

Operadores da API JAI: histogram

Page 90: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 90 /146

Aplicação: Pan Sharpening

● Alguns satélites tem bandas com resoluções diferentes.● Podemos usar combinações de bandas (cores e

pancromáticas) para obter melhor resolução espacial.

Page 91: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 91 /146

Aplicação: Pan SharpeningPlanarImage iRed = JAI.create("fileload",args[0]);PlanarImage iGreen = JAI.create("fileload",args[1]);PlanarImage iBlue = JAI.create("fileload",args[2]);PlanarImage panImage = JAI.create("fileload",args[3]);ParameterBlock pb = new ParameterBlock();pb.addSource(iRed);pb.addSource(iGreen);pb.addSource(iBlue);PlanarImage rgbImage = JAI.create("bandmerge", pb);

pb = new ParameterBlock();pb.addSource(rgbImage);float scaleX = (1f*panImage.getWidth()/iRed.getWidth());float scaleY = (1f*panImage.getHeight()/iRed.getHeight());pb.add(scaleX);pb.add(scaleY);rgbImage = JAI.create("scale",pb);

Page 92: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 92 /146

Aplicação: Pan SharpeningIHSColorSpace ihs = IHSColorSpace.getInstance();ColorModel IHSColorModel =   new ComponentColorModel(ihs,                           new int []{8,8,8},                           false,false,                           Transparency.OPAQUE,                           DataBuffer.TYPE_BYTE);pb = new ParameterBlock();pb.addSource(rgbImage);pb.add(IHSColorModel);RenderedImage imageIHS  = JAI.create("colorconvert", pb);

PlanarImage[] IHSBands = new PlanarImage[3];for(int band=0;band<3;band++)  {  pb = new ParameterBlock();  pb.addSource(imageIHS);  pb.add(new int[]{band});  IHSBands[band] = JAI.create("bandselect",pb);  }

Page 93: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 93 /146

Aplicação: Pan Sharpening

ImageLayout imageLayout = new ImageLayout();imageLayout.setColorModel(IHSColorModel);imageLayout.setSampleModel(imageIHS.getSampleModel());RenderingHints rendHints =   new RenderingHints(JAI.KEY_IMAGE_LAYOUT,imageLayout);pb = new ParameterBlock();pb.addSource(panImage);pb.addSource(IHSBands[1]);pb.addSource(IHSBands[2]);RenderedImage panSharpenedIHSImage =    JAI.create("bandmerge", pb, rendHints);

pb = new ParameterBlock();pb.addSource(panSharpenedIHSImage);pb.add(rgbImage.getColorModel()); // RGB color modelPlanarImage finalImage = JAI.create("colorconvert", pb);

JFrame frame = new JFrame("IHS Pan Sharpening");frame.add(new DisplayTwoSynchronizedImages(rgbImage,finalImage));frame.pack();frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);

Page 94: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 94 /146

Aplicação: Pan Sharpening

Page 95: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 95 /146

Aplicação: Pan Sharpening

Page 96: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 96 /146

Criando Novos Operadorescom a API JAI

Page 97: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 97 /146

Criando Novos Operadores

● Qual tipo de operação estamos implementando?– SourcelessOpImage: operadores sem imagens de entrada.– PointOpImage: pixels da saída dependem dos mesmos pixels

da entrada.– AreaOpImage: pixels da saída dependem de área ao redor dos

da entrada.– GeometricOpImage: pixels da saída podem depender de

todos da entrada.– StatisticsOpImage: operadores que calculam estatísticas

sobre imagem de entrada.

Page 98: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 98 /146

Criando Novos Operadores

● Receita de bolo (relativamente) simples para imagens renderizadas.

1.Criar classe que herda de XXXOpImage.– Como XXXOpImage é abstrata, devemos implementar métodos

que fazem o processamento (quase sempre computeTile).

2.Criar classe que implementa RenderedImageFactory.– Implementa método create.

Page 99: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 99 /146

Criando Novos Operadores

3.Criar classe que implementa OperationDescriptor ou herda de OperationDescriptorImpl, que descreve os parâmetros e valores default do operador.

4.Registrar o novo operador junto ao OperationRegistry e RIFRegistry (podemos criar método para registro na classe do passo anterior).

Page 100: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 100 /146

Novo Operador: segmenta3

● Segmentador por limiar semelhante a binarize, mas com 2 limiares.

● Classe que herda de PointOpImage.– Contém construtor para inicializar atributos e método computeTile.

public class Segmenta3OpImage extends PointOpImage { private RenderedImage source; private int threshold1,threshold2;

public Segmenta3OpImage(RenderedImage source,int th1,int th2, ImageLayout layout,RenderingHints hints, boolean b) { super(source,layout,hints,b); this.source = source; this.threshold1 = th1; this.threshold2 = th2; }

Passo 1

Page 101: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 101 /146

Novo Operador: segmenta3

public Raster computeTile(int x,int y) { Raster r = source.getTile(x,y); int minX = r.getMinX(); int minY = r.getMinY(); int width = r.getWidth(); int height = r.getHeight(); // Criamos um WritableRaster da região sendo considerada. WritableRaster wr = r.createCompatibleWritableRaster(minX,minY,width,height); for(int l=0;l<r.getHeight();l++) for(int c=0;c<r.getWidth();c++) for(int b=0;b<r.getNumBands();b++) { int p = r.getSample(c+minX,l+minY,b); if (p < threshold1) p = 0; else if (p > threshold2) p = 255; else p = 127; wr.setSample(c+minX,l+minY,b,p); } return wr; }

Passo 1

Page 102: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 102 /146

Novo Operador: segmenta3

Passo 2public class Segmenta3RIF implements RenderedImageFactory { public RenderedImage create(ParameterBlock paramBlock, RenderingHints hints) { RenderedImage source = paramBlock.getRenderedSource(0); int threshold1 = paramBlock.getIntParameter(0); int threshold2 = paramBlock.getIntParameter(1); ImageLayout layout = new ImageLayout(source); return new Segmenta3OpImage(source,threshold1,threshold2, layout,hints,false); } }

Page 103: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 103 /146

Novo Operador: segmenta3

Passo 3public class Segmenta3Descriptor extends OperationDescriptorImpl { private static final String opName = "segmenta3"; private static final String vendorName = "Hypothetical Image Processing Lab"; private static final String[][] resources = { {"GlobalName", opName}, {"LocalName", opName}, {"Vendor", vendorName}, {"Description","A simple 3-level image segmentation operator"}, {"DocURL", "http://www.lac.inpe.br/~rafael.santos"}, {"Version", "1.0"}, {"arg0Desc", "First Threshold Value"}, {"arg1Desc", "Second Threshold Value"} }; private static final String[] supportedModes = {"rendered"};

Page 104: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 104 /146

Novo Operador: segmenta3

Passo 3 private static final String[] paramNames = {"1st threshold", "2nd threshold"}; private static final Class[] paramClasses = {Integer.class, Integer.class}; private static final Object[] paramDefaults = {new Integer(85), new Integer(170) }; private static final Range[] validParamValues = { new Range(Integer.class, Integer.MIN_VALUE, Integer.MAX_VALUE), new Range(Integer.class, Integer.MIN_VALUE, Integer.MAX_VALUE) }; private static final int numSources = 1; private static boolean registered = false;

Page 105: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 105 /146

Novo Operador: segmenta3

Passo 3/4 public Segmenta3Descriptor() { super(resources,supportedModes,numSources,paramNames, paramClasses,paramDefaults,validParamValues); }

public static void register() { if (!registered) { OperationRegistry op = JAI.getDefaultInstance().getOperationRegistry(); Segmenta3Descriptor desc = new Segmenta3Descriptor(); op.registerDescriptor(desc); Segmenta3RIF rif = new Segmenta3RIF(); RIFRegistry.register(op,opName,vendorName,rif); registered = true; } }

Page 106: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 106 /146

Novo Operador: segmenta3

public static void main(String[] args) { Segmenta3Descriptor.register(); PlanarImage imagem = JAI.create("fileload", args[0]); ParameterBlock p = new ParameterBlock(); p.addSource(imagem); p.add(new Integer(120)); p.add(new Integer(200)); PlanarImage resultado = JAI.create("segmenta3",p); JFrame frame = new JFrame("Imagem binarizada"); frame.add(new DisplayTwoSynchronizedImages(imagem,resultado)); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }

Page 107: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 107 /146

Novo Operador: contapixels

● Conta número de pixels com cor semelhante a um parâmetro (com tolerância).

● Classe que herda de StatisticsOpImage.– Contém construtor para inicializar atributos e vários métodos para

acumular estatísticas.

Passo 1public class ContaPixelsOpImage extends StatisticsOpImage { private Color target; private Float tolerance; private Long count; public ContaPixelsOpImage(RenderedImage source, Color target,Float tolerance) { super(source,null,source.getMinX(),source.getMinY(),1,1); this.target = target; this.tolerance = tolerance; count = null; }

Page 108: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 108 /146

Novo Operador: contapixels

Passo 1 protected void accumulateStatistics(String name, Raster raster, Object stats) { if (count == null) count = new Long(0); int r,g,b; for(int l=0;l<raster.getHeight();l++) for(int c=0;c<raster.getWidth();c++) { int x = raster.getMinX()+c; int y = raster.getMinY()+l; r = raster.getSample(x,y,0); g = raster.getSample(x,y,1); b = raster.getSample(x,y,2); float dist = (target.getRed()-r)*(target.getRed()-r)+ (target.getGreen()-g)*(target.getGreen()-g)+ (target.getBlue()-b)*(target.getBlue()-b); if (dist<=tolerance*tolerance) count++; } }

Page 109: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 109 /146

Novo Operador: contapixels

Passo 1 protected Object createStatistics(String arg0) { if (count == null) count = new Long(0); return count; } protected String[] getStatisticsNames() { return new String[]{"count"}; } public Object getProperty(String name) { if (count == null) super.getProperty(name); return count; }

Page 110: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 110 /146

Novo Operador: contapixels

Passo 2

public class ContaPixelsRIF implements RenderedImageFactory { public RenderedImage create(ParameterBlock paramBlock, RenderingHints hints) { RenderedImage source = paramBlock.getRenderedSource(0); Color target = (Color)paramBlock.getObjectParameter(0); Float tolerance = (Float)paramBlock.getObjectParameter(1); return new ContaPixelsOpImage(source,target,tolerance); } }

Page 111: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 111 /146

Novo Operador: contapixels

Passo 3public class ContaPixelsDescriptor extends OperationDescriptorImpl { private static final String opName = "contapixels"; private static final String vendorName =

"Hypothetical Image Processing Lab"; private static final String[][] attributes = { {"GlobalName", opName}, {"LocalName", opName}, {"Vendor", vendorName}, {"Description", "A simple RGB pixel counting operator"}, {"DocURL", "http://www.lac.inpe.br/~rafael.santos"}, {"Version", "1.0"}, {"arg0Desc", "Target value (color used for similarity)"}, {"arg1Desc", "Tolerance value"}, }; private static final String[] modes = {"rendered"};

Page 112: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 112 /146

Novo Operador: contapixels

Passo 3 private static final int numSources = 1; private static final String[] paramNames = {attributes[6][0], attributes[7][0]}; private static final Class[] paramClasses = {Color.class, Float.class}; private static final Object[] paramDefaults = { new Color(0,0,0),new Float(0) }; private static boolean registered = false;

public ContaPixelsDescriptor() { super(attributes,modes,numSources,paramNames, paramClasses,paramDefaults,null); }

Page 113: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 113 /146

Novo Operador: contapixels

Passo 4 public static void register() { if (!registered) { OperationRegistry op = JAI.getDefaultInstance().getOperationRegistry(); ContaPixelsDescriptor desc = new ContaPixelsDescriptor(); op.registerDescriptor(desc); ContaPixelsRIF rif = new ContaPixelsRIF(); RIFRegistry.register(op,opName,vendorName,rif); registered = true; } }

Page 114: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 114 /146

Novo Operador: contapixels

public static void main(String[] args) { ContaPixelsDescriptor.register(); PlanarImage input = JAI.create("fileload", args[0]); int r = Integer.parseInt(args[1]); int g = Integer.parseInt(args[2]); int b = Integer.parseInt(args[3]); float t = Float.parseFloat(args[4]); Color color = new Color(r,g,b); ParameterBlock p = new ParameterBlock(); p.addSource(input); p.add(color); p.add(t); PlanarImage output = JAI.create("contapixels",p); Long count = (Long)output.getProperty("count"); System.out.println("Existem "+count+ " pixels com cores semelhantes a "+color); }

Page 115: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 115 /146

Extras: I/O

Page 116: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 116 /146

Que formatos são suportados?

public static void main(String[] args) { String[] iFormatos = ImageIO.getReaderMIMETypes(); System.out.println("Leitura: "); for(String f:iFormatos) { System.out.print(f+" "); } System.out.println("\nGravação: "); String[] oFormatos = ImageIO.getWriterMIMETypes(); for(String f:oFormatos) { System.out.print(f+" "); } System.out.println(); }

Leitura: image/jpeg image/png image/x-png image/vnd.wap.wbmp image/gif image/bmp Gravação: image/png image/jpeg image/x-png image/vnd.wap.wbmp image/bmp image/gif

Page 117: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 117 /146

Compactando Imagens

● Forma mais simples: abrir imagem em formato menos compactado e salvar em formato mais compactado.

● Lembrar sempre que existe perda de informações com JPEG e GIF!

● Método mais inteligente: controlar a compactação.

Page 118: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 118 /146

Como compactar mais?

Veja mais em http://www.lac.inpe.br/JIPCookbook

public static void main(String[] args) throws IOException { // Load the image (it is hard-coded here to make the code // simpler). String imageFile = "/tmp/folhas.tif"; BufferedImage i = ImageIO.read(new File(imageFile)); showImage("Original Image", i); // Show results with different compression ratio. compressAndShow(i, 0.5f); }

Page 119: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 119 /146

Como compactar mais?public static void compressAndShow(BufferedImage image, float quality) throws IOException { // Get a ImageWriter for jpeg format. Iterator<ImageWriter> writers = ImageIO.getImageWritersBySuffix("jpeg"); if (!writers.hasNext()) throw new IllegalStateException("No writers found"); ImageWriter writer = (ImageWriter) writers.next(); // Create the ImageWriteParam to compress the image. ImageWriteParam param = writer.getDefaultWriteParam(); param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(quality); // The output will be a ByteArrayOutputStream (in memory) ByteArrayOutputStream bos = new ByteArrayOutputStream(32768); ImageOutputStream ios = ImageIO.createImageOutputStream(bos); writer.setOutput(ios); writer.write(null, new IIOImage(image, null, null), param); ios.flush(); // otherwise the buffer size will be zero! // From the ByteArrayOutputStream create a RenderedImage. ByteArrayInputStream in = new ByteArrayInputStream(bos.toByteArray()); RenderedImage out = ImageIO.read(in); int size = bos.toByteArray().length; showImage("Compressed to " + quality + ": " + size + " bytes", out); }

Veja mais em http://www.lac.inpe.br/JIPCookbook

Page 120: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 120 /146

Como compactar mais?

private static void showImage(String title,RenderedImage image) { JFrame f = new JFrame(title); f.getContentPane().add(new DisplayJAI(image)); f.pack(); f.setVisible(true); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); }

Original 0.5 0.1

Veja mais em http://www.lac.inpe.br/JIPCookbook

Page 121: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 121 /146

Extras: Pixels

Page 122: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 122 /146

Mini-aplicação: Conversão RGB ↔ IHS

● IHS: Intensity, Hue and Saturation.● Cores representadas por:

– Intensity: brilho percebido da cor, 0 a 100% (preto = 0%).– Hue (croma): cor “bruta”, 0 a 359 graus (0 graus = vermelho).– Saturation: intensidade da cor, 0 a 100% (branco = 100%).

● Variantes: HSV, HSB, HSL, etc.

Page 123: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 123 /146

Mini-aplicação: Conversão RGB ↔ IHS

● Integração de imagens de diferentes sensores– RGB → IHS, substitui banda I por banda de maior resolução,

converte novamente IHS → RGB● Manipulação de contraste e brilho

– RGB → IHS, manipula brilho e contraste da banda I, converte novamente IHS → RGB

● Nosso exemplo: converte RGB → IHS, substitui I e S por 100% constantes, reconverte para RGB.

Page 124: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 124 /146

Mini-aplicação: Conversão RGB ↔ IHS

public class RGBtoIHS { public static void main(String[] args) { PlanarImage imagem = JAI.create("fileload", args[0]); // Converte para o modelo de cores IHS. IHSColorSpace ihs = IHSColorSpace.getInstance(); ColorModel modeloIHS = new ComponentColorModel(ihs, new int []{8,8,8}, false,false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE) ; ParameterBlock pb = new ParameterBlock(); pb.addSource(imagem); pb.add(modeloIHS); RenderedImage imagemIHS = JAI.create("colorconvert", pb);

Page 125: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 125 /146

Mini-aplicação: Conversão RGB ↔ IHS

// Extraímos as bandas I, H e S. RenderedImage[] bandas = new RenderedImage[3]; for(int band=0;band<3;band++) { pb = new ParameterBlock(); pb.addSource(imagemIHS); pb.add(new int[]{band}); bandas[band] = JAI.create("bandselect",pb); } // Criamos bandas constantes para as bandas I e S. pb = new ParameterBlock(); pb.add((float)imagem.getWidth()); pb.add((float)imagem.getHeight()); pb.add(new Byte[]{(byte)255}); RenderedImage novaIntensidade = JAI.create("constant",pb); pb = new ParameterBlock(); pb.add((float)imagem.getWidth()); pb.add((float)imagem.getHeight()); pb.add(new Byte[]{(byte)255}); RenderedImage novaSaturação = JAI.create("constant",pb);

Page 126: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 126 /146

Mini-aplicação: Conversão RGB ↔ IHS // Juntamos as bandas H e as I e S constantes. // Devemos passar um RenderingHint que indica que o modelo // de cor IHS será usado. ImageLayout imageLayout = new ImageLayout(); imageLayout.setColorModel(modeloIHS); imageLayout.setSampleModel(imagemIHS.getSampleModel()); RenderingHints rendHints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT,imageLayout); pb = new ParameterBlock(); pb.addSource(novaIntensidade); pb.addSource(bandas[1]); pb.addSource(novaSaturação); RenderedImage imagemIHSModificada = JAI.create("bandmerge", pb, rendHints); // Convertemos de volta para RGB. pb = new ParameterBlock(); pb.addSource(imagemIHSModificada); pb.add(imagem.getColorModel()); // Imagem original era em RGB! RenderedImage imagemFinal = JAI.create("colorconvert", pb);

Page 127: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 127 /146

Mini-aplicação: Conversão RGB ↔ IHS JFrame frame = new JFrame("Modificação via IHS"); frame.add(new DisplayTwoSynchronizedImages(imagem,imagemFinal)); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }

Page 128: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 128 /146

Extras: Display

Page 129: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 129 /146

Mini-aplicação: Visualizador com Ícone

● Mostra imagens grandes com pequeno ícone e pan.public class DisplayThumbnail extends DisplayJAI implements MouseMotionListener,MouseListener { private PlanarImage originalImage; private float scale; private int imageXTiles,imageYTiles; private int imageTileWidth,imageTileHeight; private int imageWidth,imageHeight; private int visibleRegionWidth,visibleRegionHeight; private int thumbWidth,thumbHeight; // The size of the border around the thumbnail. private final int border = 10; // The scaled viewport (dimensions are scaled/translated by the border). private Rectangle2D scaledViewport; private Color viewportColor; // Colors to be used when the mouse is/isn't over the viewport. private static Color viewportOn = new Color(120,255,120); private static Color viewportOff = new Color(0,192,0); // Coordinates obtained when we click (press) the mouse button to start // dragging the viewport. private int lastX,lastY; // Those coordinates represent the region where we can safely drag the // viewport without "falling outside" the image boundaries. private int minValidX,minValidY,maxValidX,maxValidY;

Page 130: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 130 /146

Mini-aplicação: Visualizador com Ícone public DisplayThumbnail(PlanarImage image,float scale, int width,int height) { this.scale = scale; originalImage = image; visibleRegionWidth = width; visibleRegionHeight = height; // Get some stuff about the image. imageXTiles = image.getNumXTiles(); imageYTiles = image.getNumYTiles(); imageTileWidth = image.getTileWidth(); imageTileHeight = image.getTileHeight(); imageWidth = image.getWidth(); imageHeight = image.getHeight(); // Must create a thumbnail image using that scale. ParameterBlock pb = new ParameterBlock(); pb.addSource(image); pb.add(scale); pb.add(scale); pb.add(0.0F); pb.add(0.0F); pb.add(new InterpolationNearest()); PlanarImage thumbnail = JAI.create("scale", pb, null); // Let's get the width and height of the thumbnail. thumbWidth = thumbnail.getWidth(); thumbHeight = thumbnail.getHeight();

Page 131: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 131 /146

Mini-aplicação: Visualizador com Ícone // Now let's add a border. pb = new ParameterBlock(); pb.addSource(thumbnail); pb.add(new Integer(border)); pb.add(new Integer(border)); pb.add(new Integer(border)); pb.add(new Integer(border)); pb.add(new BorderExtenderConstant(new double[]{0,0,128})); thumbnail = JAI.create("border",pb); // Shift the image to the original position pb = new ParameterBlock(); pb.addSource(thumbnail); pb.add(1.0f*border); pb.add(1.0f*border); thumbnail = JAI.create("translate",pb,null); // Use this thumbnail as the image for the DisplayJAI component. set(thumbnail); // We'd like to listen to mouse movements. addMouseMotionListener(this); addMouseListener(this); // Initially the scaled viewport will be positioned at border,border. scaledViewport = new Rectangle2D.Float(border,border,width*scale,height*scale); // We assume that the mouse is off the viewport. viewportColor = viewportOff; }

Page 132: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 132 /146

Mini-aplicação: Visualizador com Ícone public synchronized void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; // Paint the tile grid. g2d.setColor(Color.YELLOW); g2d.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER,0.5f)); float x1,x2,y1,y2; // Vertical tiles' boundaries. x1 = x2 = border; y1 = border; y2 = border+thumbHeight; for(int tx=0;tx<=imageXTiles;tx++) { g2d.drawLine((int)x1,(int)y1,(int)x2,(int)y2); x1 += imageTileWidth*scale; x2 += imageTileWidth*scale; }

Page 133: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 133 /146

Mini-aplicação: Visualizador com Ícone // Horizontal tiles' boundaries. x1 = border; x2 = border+thumbWidth; y1 = y2 = border; for(int ty=0;ty<=imageYTiles;ty++) { g2d.drawLine((int)x1,(int)y1,(int)x2,(int)y2); y1 += imageTileHeight*scale; y2 += imageTileHeight*scale; } // Paint a red border. g2d.setColor(Color.RED); g2d.drawRect(border,border,thumbWidth,thumbHeight); // Paint the viewport. g2d.setColor(viewportColor); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1f)); Stroke stroke = new BasicStroke(2f); g2d.setStroke(stroke); g2d.draw(scaledViewport); }

Page 134: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 134 /146

Mini-aplicação: Visualizador com Ícone public void mouseMoved(MouseEvent e) { int x = e.getX(); int y = e.getY(); // Ignore events outside the border. if ((x < border) || (y < border) || (x > border+thumbWidth) || (y > border+thumbHeight)) return; // Are we inside the viewport rectangle ? if (scaledViewport.contains(x,y)) viewportColor = viewportOn; else viewportColor = viewportOff; // Hopefully it will repaint only the needed section. Rectangle repaintBounds = new Rectangle((int)scaledViewport.getX()-5, (int)scaledViewport.getY()-5, (int)scaledViewport.getWidth()+10, (int)scaledViewport.getHeight()+10); repaint(repaintBounds); }

Page 135: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 135 /146

Mini-aplicação: Visualizador com Ícone public void mousePressed(MouseEvent e) { // Store the new dragging starting points. lastX = e.getX(); lastY = e.getY(); // Calculate the new window w/ viewport movements. minValidX = lastX-(int)scaledViewport.getX()+border; minValidY = lastY-(int)scaledViewport.getY()+border; maxValidX = border+thumbWidth-(int)scaledViewport.getWidth()+ (lastX-(int)scaledViewport.getX()); maxValidY = border+thumbHeight-(int)scaledViewport.getHeight()+ (lastY-(int)scaledViewport.getY()); } public void mouseDragged(MouseEvent e) { int x = e.getX(); int y = e.getY(); if (x > maxValidX) x = maxValidX-1; if (x < minValidX) x = minValidX; if (y > maxValidY) y = maxValidY-1; if (y < minValidY) y = minValidY; if ((x >= minValidX) && (y >= minValidY) && (x <= maxValidX) && (y <= maxValidY)) { updateLocation(x, y); lastX = x; lastY = y; } }

Page 136: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 136 /146

Mini-aplicação: Visualizador com Ícone public void updateLocation(int x,int y) { // Store the approximate region where the viewport was before the change. Rectangle initBounds = new Rectangle((int)scaledViewport.getX()-5, (int)scaledViewport.getY()-5, (int)scaledViewport.getWidth()+10, (int)scaledViewport.getHeight()+10); // Recalculate new position for the viewport, based on mouse coordinates. double origX = scaledViewport.getX()+x-lastX; double origY = scaledViewport.getY()+y-lastY; // Reposition the viewport. scaledViewport.setFrame(origX,origY, scaledViewport.getWidth(),scaledViewport.getHeight()); // Store the approximate region where the viewport is after the change. Rectangle finalBounds = new Rectangle((int)scaledViewport.getX()-5, (int)scaledViewport.getY()-5, (int)scaledViewport.getWidth()+10, (int)scaledViewport.getHeight()+10); // Repaint only that section. repaint(finalBounds.union(initBounds)); }

Page 137: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 137 /146

Mini-aplicação: Visualizador com Íconepublic PlanarImage getImage() { // Get the boundaries in the original image coordinates. float fromX = (float)Math.round((scaledViewport.getX()-border)/scale); float fromY = (float)Math.round((scaledViewport.getY()-border)/scale); float width = (float)Math.round(scaledViewport.getWidth()/scale); float height = (float)Math.round(scaledViewport.getWidth()/scale); // Fix rounding errors to avoid exceptions on the crop. fromX = Math.min(fromX,(imageWidth-visibleRegionWidth)); fromY = Math.min(fromY,(imageHeight-visibleRegionHeight)); // Create a ParameterBlock with information for the cropping. ParameterBlock pb = new ParameterBlock(); pb.addSource(originalImage); pb.add(fromX); pb.add(fromY); pb.add(width); pb.add(height); // Create the output image by cropping the input image. PlanarImage output = JAI.create("crop",pb,null); // Translate the image origin. pb = new ParameterBlock(); pb.addSource(output); pb.add(-fromX); pb.add(-fromY); // Create the output image by translating itself. return JAI.create("translate",pb,null); }

Page 138: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 138 /146

Mini-aplicação: Visualizador com Ícone

public Rectangle getCroppedImageBounds() { int fromX = (int)Math.round((scaledViewport.getX()-border)/scale); int fromY = (int)Math.round((scaledViewport.getY()-border)/scale); int width = (int)Math.round(scaledViewport.getWidth()/scale); int height = (int)Math.round(scaledViewport.getWidth()/scale); return new Rectangle(fromX,fromY,width,height); }

public Rectangle getViewportBounds() { Rectangle temp = scaledViewport.getBounds(); temp.setBounds((int)temp.getX()-border,(int)temp.getY()-border, (int)temp.getWidth(),(int)temp.getHeight()); return temp; }

Page 139: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 139 /146

Mini-aplicação: Visualizador com Íconepublic class DisplayThumbnailApp extends JFrame implements MouseMotionListener { private DisplayThumbnail dt; private DisplayJAI dj; private JLabel world,view;

public DisplayThumbnailApp(PlanarImage image,int dWidth,int dHeight) { super("Interactive Thumbnail Example"); SpringLayout layout = new SpringLayout(); Container contentPane = getContentPane(); contentPane.setLayout(layout); dt = new DisplayThumbnail(image,0.05f,dWidth,dHeight); dt.addMouseMotionListener(this); dj = new DisplayJAI(dt.getImage()); dj.setPreferredSize(new Dimension(dWidth,dHeight)); dj.setMinimumSize(new Dimension(dWidth,dHeight)); dj.setMaximumSize(new Dimension(dWidth,dHeight)); JPanel borderDisplay = new JPanel(new BorderLayout()); borderDisplay.add(dj); borderDisplay.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); JLabel worldL = new JLabel("World: "); world = new JLabel(""); JLabel viewL = new JLabel("Thumb: "); view = new JLabel(""); contentPane.add(dt); contentPane.add(borderDisplay); contentPane.add(worldL); contentPane.add(world); contentPane.add(viewL); contentPane.add(view); (organização do layout removida) setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setResizable(false); pack(); setVisible(true); }

Page 140: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 140 /146

Mini-aplicação: Visualizador com Ícone public void mouseDragged(MouseEvent e) { dj.set(dt.getImage()); // Gets some information about the viewport and cropped image. Rectangle crop = dt.getCroppedImageBounds(); Rectangle viewp = dt.getViewportBounds(); // Change the labels' contents with this information. world.setText(""+crop.x+","+crop.y+ " ("+crop.width+"x"+crop.height+")"); view.setText(""+viewp.x+","+viewp.y+ " ("+viewp.width+"x"+viewp.height+")"); }

public void mouseMoved(MouseEvent e) { }

public static void main(String[] args) { PlanarImage image = JAI.create("fileload", args[0]); new DisplayThumbnailApp(image,640,640); }

Page 141: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 141 /146

Mini-aplicação: Visualizador com Ícone

Page 142: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 142 /146

Outros Tópicos

Page 143: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 143 /146

Tópicos para Pesquisa e Desenvolvimento

● Processamento por pixels x processamento por regiões– Imagens de alta resolução– Novos algoritmos de classificação– Modelagem de conhecimento– Modelos de mistura– Inteligência artificial

http://gras.ku.dk/software/ecognition.htm

Page 144: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 144 /146

Para saber mais...

● http://www.lac.inpe.br/JIPCookbook http://www.lac.inpe.br/~rafael.santos

● Introductory Digital Image Processing: A Remote Sensing Perspective (John R. Jensen)

● Digital Image Processing Algorithms and Applications (Ioannis Pitas)● Digital Image Processing (Rafael C. Gonzalez, Richard E. Woods)

Page 145: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 145 /146

Para saber mais...

● Fundamentals of Digital Image Processing (Anil K. Jain)● Classification Methods for Remotely Sensed Data (Brandt Tso, Paul M.

Mather)● Pattern Recognition and Image Analysis (Earl Gose, Richard

Johnsonbaugh, Steve Jost)

Page 146: Intro Pi Java

Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 146 /146

Para saber mais...

● Fuzzy Algorithms: With Applications to Image Processing and Pattern Recognition (Zheru Chi, H. Yan, Z.R. Chi, Hong Yan, Tuan Pham)

● The Pocket Handbook of Image Processing Algorithms In C (Harley R. Myler, Arthur R. Weeks)

● Intelligence: The Eye, the Brain, and the Computer (Martin A. Fischler, Oscar Firschein)