Upload
joao-milton
View
200
Download
2
Embed Size (px)
Citation preview
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
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!
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 3 /146
Introdução
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.
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.
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
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
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.
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 9 /146
Outros Tipos de Imagens Digitais
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 10 /146
Outros Tipos de Imagens Digitais
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 11 /146
Imagens Digitais: Multiespectrais
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 12 /146
Imagens Digitais: Hiperespectrais
http://www.cossa.csiro.au/hswww/Overview.htm
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 13 /146
Processamento de Imagens em 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.
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.
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?
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 17 /146
Representação de Imagens em 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
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 19 /146
Representação de Imagens: TiledImage (JAI)
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 20 /146
Representação de Imagens
RenderedImage
PlanarImage
WritableRenderedImage BufferedImage
RenderedOp
ImageJAI
TiledImageAPI JAI
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 21 /146
Criando Imagens em 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.
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")); }
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.
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");
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.
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};
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); } }
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);
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 30 /146
Armazenando e Recuperando Imagens
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"); }
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"); }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 33 /146
Acesso Direto a Pixels
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!
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.
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 36 /146
Exibindo Imagens
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
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).
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 39 /146
Display de Imagens (sem JAI)
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 (!?).
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 41 /146
Exibindo Imagens(Soluções Específicas)
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.
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.
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); }
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()); }
}
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); } }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 47 /146
Imagens Sincronizadas: Exemplo
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.
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]; }
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); } }
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); } }
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
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); }
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*(i127)/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)(255i); lut[i][1] = (short)(255i); lut[i][2] = (short)(255i); } return lut; }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 55 /146
Imagens Substitutas: LUTs
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.
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);
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);
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 59 /146
Desenhando em Imagens
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 60 /146
Operadores da API JAI
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.
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 62 /146
Relembrando: Representação de Imagens
RenderedImage
PlanarImage
WritableRenderedImage BufferedImage
RenderedOp
ImageJAI
TiledImageAPI JAI
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); }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 64 /146
Operadores da API JAI: invert
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); }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 66 /146
Operadores da API JAI: binarize
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); }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 68 /146
Operadores da API JAI: convolve
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); }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 70 /146
Operadores da API JAI: convolve
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); }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 72 /146
Operadores da API JAI: dilate
Regiões brancas são dilatadas!
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); }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 74 /146
Operadores da API JAI: erode
Regiões brancas são dilatadas!
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); }
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)
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
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);
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); }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 80 /146
Operadores da API JAI: rotate
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); }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 82 /146
Operadores da API JAI: scale
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]);
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);
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);
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
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");
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); }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 89 /146
Operadores da API JAI: histogram
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.
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);
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); }
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);
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 94 /146
Aplicação: Pan Sharpening
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 95 /146
Aplicação: Pan Sharpening
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 96 /146
Criando Novos Operadorescom a API JAI
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.
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.
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).
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
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
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); } }
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"};
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;
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; } }
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); }
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; }
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++; } }
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; }
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); } }
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"};
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); }
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; } }
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); }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 115 /146
Extras: I/O
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
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.
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); }
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
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
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 121 /146
Extras: Pixels
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.
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.
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);
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);
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);
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); } }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 128 /146
Extras: Display
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;
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();
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; }
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; }
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); }
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); }
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; } }
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)); }
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); }
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; }
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); }
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); }
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 141 /146
Mini-aplicação: Visualizador com Ícone
Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 142 /146
Outros Tópicos
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
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)
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)
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)