Você está na página 1de 146
Introdução ao Processamento de Imagens Digitais em Java / API JAI Escola de Verão da

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

Escola de Verão da Unifesp

Rafael Santos

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!

Introdução

Introdução Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 3 /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.

Imagens Digitais

Imagem = matriz de pixels.

Pixel = medida, conjunto de medidas ou índice para tabela de valores.

Metadados: dados adicionais sobre a imagem.

de valores. ● Metadados: dados adicionais sobre a imagem. Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 5

Imagens e Pixels

Imagens e Pixels 34 34 42 42 34 12 42 29 29 49 49 29 30
Imagens e Pixels 34 34 42 42 34 12 42 29 29 49 49 29 30
Imagens e Pixels 34 34 42 42 34 12 42 29 29 49 49 29 30

34

34

42

42

34

12

42

29

29

49

49

29

30

49

105

105

97

97

105

105

97

12

34

14

34

34

42

34

30

29

48

29

29

49

29

105

105

97

105

105

97

105

34

34

69

36

12

12

42

29

29

76

54

30

30

49

105

105

97

104

105

105

97

34

12

85

113

36

34

34

29

30

103

108

54

29

29

105

105

85

72

104

105

105

34

34

58

100

90

14

34

29

29

53

123

115

48

29

105

105

105

66

78

97

105

12

34

42

90

107

85

42

30

29

49

115

136

103

49

105

105

97

78

58

85

97

34

42

35

85

111

105

42

29

49

41

103

132

119

49

105

97

105

85

60

86

97

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

DPI ou mais – 3 bandas – Discretização variável Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 7
DPI ou mais – 3 bandas – Discretização variável Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 7

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.

Outros Tipos de Imagens Digitais

Outros Tipos de Imagens Digitais 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 9 /146

Outros Tipos de Imagens Digitais

Outros Tipos de Imagens Digitais Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 10 /146

Imagens Digitais: Multiespectrais

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

Imagens Digitais: Hiperespectrais

Imagens Digitais: Hiperespectrais http://www.cossa.csiro.au/hswww/Overview.htm Fevereiro/2010

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

Hiperespectrais http://www.cossa.csiro.au/hswww/Overview.htm Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 12
Hiperespectrais http://www.cossa.csiro.au/hswww/Overview.htm Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 12
Processamento de Imagens em Java Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 13 /146

Processamento de Imagens em Java

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.

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.

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?

Representação de Imagens em Java Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 17 /146

Representação de Imagens em Java

Representação de Imagens: Java

RenderedImage ColorModel Raster ColorSpace SampleModel DataBuffer
RenderedImage
ColorModel
Raster
ColorSpace
SampleModel
DataBuffer

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

Existem limitações mútuas.

Representação de Imagens: TiledImage (JAI)

Representação de Imagens: TiledImage (JAI) 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 19 /146

Fevereiro/2010

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

19 /146

Representação de Imagens

RenderedImage

Representação de Imagens RenderedImage WritableRenderedImage BufferedImage API JAI Fevereiro/2010

WritableRenderedImage

de Imagens RenderedImage WritableRenderedImage BufferedImage API JAI Fevereiro/2010
de Imagens RenderedImage WritableRenderedImage BufferedImage API JAI Fevereiro/2010

BufferedImage

de Imagens RenderedImage WritableRenderedImage BufferedImage API JAI Fevereiro/2010

API JAI

Criando Imagens em Java Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 21 /146

Criando Imagens em Java

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.

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);

raster.setPixel(w,h,cor1); else raster.setPixel(w,h,cor2); } ImageIO. write (image, "PNG" , new File(

}

ImageIO.write(image,"PNG",new File("checkerboard.png"));

}

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.

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");

,tiledImage, "jaigl.png" , "PNG" ); Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 25

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.

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};

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);

}

}

Criando Imagens (com JAI)

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

,tiledImage, "rgbtile.tiff" , "TIFF" ,tep); Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 29
Armazenando e Recuperando Imagens Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 30 /146

Armazenando e Recuperando Imagens

Entrada e Saída

Sem JAI (BufferedImage):

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");

}

Com JAI (PlanarImage):

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");

}

Entrada e Saída

Sem JAI (BufferedImage):

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"));

}

Com JAI (PlanarImage):

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");

}

Acesso Direto a Pixels Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 33 /146

Acesso Direto a Pixels

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++)

Memória!
Memória!

{

raster.getPixel(w,h,pixel); if ((pixel[0] == 255) && (pixel[1] == 255) && (pixel[2] == 255)) brancos++;

}

System.out.println(brancos+" pixels brancos");

}

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.

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

Exibindo Imagens

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.

JFrame
JFrame
Componentes de UI
Componentes de UI

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).

Display de Imagens (sem JAI)

Display de Imagens (sem JAI) Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 39 /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 (!?).

Exibindo Imagens (Soluções Específicas) Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 41 /146

Exibindo Imagens (Soluções Específicas)

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.

Imagens Sincronizadas

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

public class DisplayTwoSynchronizedImages extends JPanel implements AdjustmentListener

{

protected DisplayJAI dj1; protected DisplayJAI dj2; protected JScrollPane jsp1; protected JScrollPane jsp2;

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);

}

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());

}

}

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);

}

Imagens Sincronizadas: Exemplo

Imagens Sincronizadas: Exemplo Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 47 /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.

Imagens Substitutas

public 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];

}

Imagens Substitutas

}

// Reescalamos os níveis de cinza da imagem.

double[] subtract = new double[1];

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);

}

subtract[0]

= minValue;

Imagens Substitutas

public 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);

}

EXIT_ON_CLOSE ); frame.pack(); frame.setVisible( true ); } Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 51

Imagens Substitutas: LUTs

Uso de imagens substitutas (surrogate images) com LUTs:

Look-up Tables (LUTs): tabela de cores.

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

1

1

1

0

0

0

0

0

0

0

0

0

1

2

2

2

1

0

0

0

0

0

0

1

1

2

2

2

3

2

1

1

0

0

0

1

1

1

2

2

3

2

2

1

1

1

0

0

0

1

1

2

3

2

2

2

1

1

0

0

0

0

0

0

1

2

2

2

1

0

0

0

0

0

0

0

0

0

1

1

1

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

Índice

R

G

B

0

0 0 131 1

0 131

1

1

1 255 233 0

255

233

0

2

2 0 0 124

0

0 124

3

3 255 255 255

255

255 255

255 233 0 2 0 0 124 3 255 255 255 Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 52

Imagens Substitutas: LUTs

public 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

53 /146

Imagens Substitutas: LUTs

/** 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;

}

/** 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)));

)/255)));

lut[i][2] = (short)(127*(1+Math.sin(Math.PI*(i+127)/255)));

}

lut[i][1] = (short)(127*(1+Math.sin(Math.PI*(i

return lut;

}

Imagens Substitutas: LUTs

Imagens Substitutas: LUTs Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 55 /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.

Desenhando em Imagens

BufferedImage 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);

Desenhando em Imagens

Path2D.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);

Desenhando em Imagens

Desenhando em Imagens Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 59 /146
Operadores da API JAI Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 60 /146

Operadores da API JAI

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.

Relembrando: Representação de Imagens

RenderedImage

Relembrando: Representação de Imagens RenderedImage WritableRenderedImage BufferedImage API JAI Fevereiro/2010

WritableRenderedImage

de Imagens RenderedImage WritableRenderedImage BufferedImage API JAI Fevereiro/2010

BufferedImage

API JAI

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);

}

Operadores da API JAI: invert

Operadores da API JAI: invert Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 64 /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);

}

Operadores da API JAI: binarize

Operadores da API JAI: binarize Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 66 /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);

}

Operadores da API JAI: convolve

Operadores da API JAI: convolve Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 68 /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);

}

Operadores da API JAI: convolve

Operadores da API JAI: convolve Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 70 /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);

}

Operadores da API JAI: dilate

Operadores da API JAI: dilate Regiões brancas são dilatadas! Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos

Regiões brancas são dilatadas!

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);

}

Operadores da API JAI: erode

Operadores da API JAI: erode Regiões brancas são dilatadas! Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 74

Regiões brancas são dilatadas!

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);

}

Operadores da API JAI: rotate

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

Coordenadas dos cantos da imagem rotacionada:

(-39, -136) – (558, 461)

Translação da Origem de Imagens

Mínimo

200,200

Tamanho

400x300

Máximo

600,500

Original

Região para recorte

400x300 Máximo 600,500 Original Região para recorte Origem 200,200 Tamanho 400x300 Região recortada Recorte e

Origem

200,200

Tamanho

400x300

Região recortada

Recorte e translação

Tamanho 400x300 Região recortada Recorte e translação Mínimo 0,0 Tamanho 400x300 Máximo 400,300 Fevereiro/2010

Mínimo

0,0

Tamanho

400x300

Máximo

400,300

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);

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);

}

Operadores da API JAI: rotate

Operadores da API JAI: rotate Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 80 /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);

}

Operadores da API JAI: scale

Operadores da API JAI: scale Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 82 /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]);

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);

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);

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);

}

EXIT_ON_CLOSE ); frame.setVisible( true ); } java wvc/operadores/Recorta astro013.jpg 461 896 24 27 20

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

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");

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);

}

Operadores da API JAI: histogram

Operadores da API JAI: histogram 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 89 /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.

e pancromáticas) para obter melhor resolução espacial. Fevereiro/2010 http://www.lac.inpe.br/~rafael.santos 90