JAI: Java Advanced Imaging - IRIS...

35
JAI: Java Advanced Imaging Rafael Santos 1 Resumo: Apesar da popularização de pacotes de software para processamento de imagens, em muitos casos é necessário implementar novas operações ou algoritmos usando uma linguagem de programação alto nível. Java é uma excelente alternativa por ser portátil entre sistemas operacionais, por ser relativamente simples e em espe- cial por já ter mais de uma API (Application Programming Interface) para represen- tação, processamento e entrada e saída de imagens. Uma das APIs é a Java Advanced Imaging (JAI), que provê métodos para representação e processamento de imagens de grande porte. Este tutorial apresenta os conceitos básicos de representação e processamento de ima- gens usando Java e a API JAI, com ênfase em exemplos simples. Abstract: In spite of the popularization of image processing software, for some tasks one must implement new operations or algorithms using a high-level program- ming language. Java is an excellent alternative since it is portable, relatively simple and since it already have more than one API (Application Programming Interface) for image representation, processing and input/output. One of those APIs is the Java Advanced Imaging API, which provides methods for representation and processing of large images. This tutorial presents the basic concepts on image representation and processing using Java and the JAI API, with an emphasis on simple examples. 1 Introdução Técnicas de processamento de imagens digitais existem há quase noventa anos, com um crescimento explosivo em aplicações nos últimos quarenta anos[3]. Algoritmos para di- versos tipos de processamento já são parte integrante de pacotes de software e até de sistemas embarcados, mas frequentes avanços tecnológicos e novas áreas de aplicação demandam o estudo e criação de novos algoritmos e soluções. Alguns pacotes de software para processamento de imagens permitem a implementa- ção de novos algoritmos, geralmente usando uma linguagem de alto nível e a API da própria aplicação, mas frequentemente existe um custo na aquisição do pacote e/ou na aquisição do 1 Laboratório Associado de Computação e Matemática Aplicada Instituto Nacional de Pesquisas Espaciais Av. dos Astronautas, 1758 – Jardim da Granja – CEP 12227-010 São José dos Campos – SP – Brasil [email protected]

Transcript of JAI: Java Advanced Imaging - IRIS...

Page 1: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced ImagingRafael Santos1

Resumo: Apesar da popularização de pacotes de software para processamento deimagens, em muitos casos é necessário implementar novas operações ou algoritmosusando uma linguagem de programação alto nível. Java é uma excelente alternativapor ser portátil entre sistemas operacionais, por ser relativamente simples e em espe-cial por já ter mais de uma API (Application Programming Interface) para represen-tação, processamento e entrada e saída de imagens. Uma das APIs é a Java AdvancedImaging (JAI), que provê métodos para representação e processamento de imagens degrande porte.Este tutorial apresenta os conceitos básicos de representação e processamento de ima-gens usando Java e a API JAI, com ênfase em exemplos simples.

Abstract: In spite of the popularization of image processing software, for sometasks one must implement new operations or algorithms using a high-level program-ming language. Java is an excellent alternative since it is portable, relatively simpleand since it already have more than one API (Application Programming Interface)for image representation, processing and input/output. One of those APIs is the JavaAdvanced Imaging API, which provides methods for representation and processing oflarge images.This tutorial presents the basic concepts on image representation and processing usingJava and the JAI API, with an emphasis on simple examples.

1 Introdução

Técnicas de processamento de imagens digitais existem há quase noventa anos, comum crescimento explosivo em aplicações nos últimos quarenta anos[3]. Algoritmos para di-versos tipos de processamento já são parte integrante de pacotes de software e até de sistemasembarcados, mas frequentes avanços tecnológicos e novas áreas de aplicação demandam oestudo e criação de novos algoritmos e soluções.

Alguns pacotes de software para processamento de imagens permitem a implementa-ção de novos algoritmos, geralmente usando uma linguagem de alto nível e a API da própriaaplicação, mas frequentemente existe um custo na aquisição do pacote e/ou na aquisição do

1Laboratório Associado de Computação e Matemática AplicadaInstituto Nacional de Pesquisas EspaciaisAv. dos Astronautas, 1758 – Jardim da Granja – CEP 12227-010São José dos Campos – SP – [email protected]

Page 2: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

módulo de desenvolvimento de novos módulos, e possíveis restrições na disponibilizaçãodestes novos módulos para outros usuários.

Uma alternativa interessante para o desenvolvimento de rotinas de processamento deimagens é a linguagem Java2, em especial quando usada em conjunto com a API (ApplicationProgramming Interface) JAI (Java Advanced Imaging)3. A linguagem e API são portáteisentre sistemas operacionais, podem ser obtidas gratuitamente e contém operadores e clas-ses para representação, processamento e entrada e saída de imagens flexíveis e poderosas,permitindo também a criação de novos operadores.

Neste tutorial veremos alguns conceitos de representação de imagens digitais, seuprocessamento, armazenamento e visualização usando Java e a API JAI. Este tutorial é decaráter introdutório e prático, com ênfase em exemplos simples que podem facilmente sermodificados e adaptados. Informações sobre a arquitetura das APIs mencionadas não serãoapresentadas, e alguns conceitos serão apresentados de forma simplificada para que o leitorpossa colocá-los em prática o mais rápido possível. Assume-se que o leitor tenha conheci-mentos básicos de programação em Java ou em outra linguagem moderna, e que tenha noçõesde processamento de imagens e matemática.

O tutorial é organizado da seguinte forma: cada seção apresenta uma tarefa genérica derepresentação, processamento, visualização ou entrada e saída de imagens, alguns conceitosteóricos (se necessário) e exemplos de código que ilustram como realizar a tarefa. Algunsexemplos requerem somente APIs que fazem parte da distribuição básica de Java (AWT,Abstract Window Toolkit, Swing, ImageIO), enquanto outros são específicos da API JAI.

Algumas listagens deste tutorial não foram reproduzidas integralmente – nestas, so-mente as linhas de código relevantes ao exemplo foram incluidas. Todas as listagens comple-tas podem ser copiadas do site do autor (http://www.lac.inpe.br/∼rafael.santos) ou de [7].

2 Representando imagens digitais

O primeiro passo necessário para implementar algoritmos de processamento de ima-gens é entender o que é uma imagem digital e como a mesma é representada na memória.Uma imagem digital pode ser considerada como uma matriz regular bidimensional de ele-mentos chamados pixels. Para a grande maioria das aplicações consideramos que os pixelssão quadrados, isto é, tem a mesma largura e altura; que a matriz é regular, sem buracosou descontinuidades; e que a coordenada superior esquerda da matriz é (0, 0). Considera-mos também que um pixel pode representar um ou mais valores (correspondendo ao númeroda bandas na imagem) e que são todos do mesmo tipo básico. Valores dos pixels podem

2http://java.sun.com/javase/downloads/index.jsp3http://java.sun.com/javase/technologies/desktop/media/jai/

2 RITA • Volume – • Número – • —-

Page 3: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

ser interpretados através de diversos sistemas de cores, ou mesmo ser somente índices paratabelas de cores. Outras variações na representação de imagens são possíveis mas não asconsideraremos neste tutorial.

Para exemplificar, uma imagem de câmera digital é uma matriz de dimensões cor-respondentes à resolução da câmera, com três bandas que representam a intensidade doscomponentes vermelho, verde e azul, ou seja, com três valores (normalmente entre 0 e 255)associados a cada pixel. Uma imagem digital correspondente a um modelo de elevação deterreno, usada em várias aplicações de sensoriamento remoto, é uma matriz regular ondecada pixel corresponde a um único valor, frequentemente de ponto flutuante, podendo atérepresentar valores negativos, correspondentes à altura do terreno naquela posição.

Imagens digitais podem ser armazenadas em dispositivos ou transmitidas por diversosmeios – quando isto é feito, a estrutura que compõe uma imagem é codificada usando algumformato específico para armazenamento ou transmissão e decodificada para leitura para amemória. Existem diversos tipos de formatos de armazenamento de imagens digitais, entreeles, TIFF (Tagged Image File Format), PNG (Portable Network Graphics), JPEG (JointPhotographic Experts Group) e outros.

O formato de uma imagem armazenada em dispositivos não deve ser confundido como formato de imagens na memória – os primeiros sempre dependem da estrutura do codi-ficador e do algoritmo usado pelo mesmo, enquanto os últimos são baseados nas estruturasdescritas na próxima subseção. O desenvolvedor só deve se preocupar em entender as limi-tações dos codificadores e decodificadores. Por exemplo, o formato JPEG causa perda deprecisão na representação dos pixels, e o formato TIFF permite armazenamento de imagenscom pixels com mais de quatro bandas e/ou com valores de ponto flutuante.

2.1 Representação de imagens digitais em Java

De uma forma geral uma imagem digital é representada em Java usando-se uma ins-tância de classe que representa uma imagem. Esta classe, por sua vez, contém instâncias deduas classes (ou herdeiras) que são [5]:

• Uma instância de Raster, que contém os valores dos pixels da imagem. Um raster,por sua vez, é representado por uma instância de DataBuffer, que contém os valoresdos pixels e de SampleModel, que indica como estes valores são organizados namemória.

• Uma instância de ColorModel, que indica como os valores dos pixels serão interpre-tados para renderização da imagem em dispositivos. Esta instância pode conter umareferência a instância de ColorSpace que representa alguns dos tipos básicos deespaços de cores.

RITA • Volume – • Número – • —- 3

Page 4: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

Tanto a API básica de representação de imagens em Java quanto a API JAI contémmétodos para criar e manipular imagens usando instâncias destas classes. Várias combina-ções de classes e parâmetros são possíveis, permitindo a representação de uma enorme gamade tipos de imagens digitais.

Um ponto que gera alguma confusão é a existência de várias classes e interfaces, tantoem Java quanto na API JAI, que podem ser usadas para representar imagens. Algumas destasclasses e interfaces são descritas a seguir.

• BufferedImage é uma classe padrão de Java (isto é, não faz parte da API JAI), quepode ser usada para a criação de imagens simples, que possivelmente atendem a maiorparte das necessidades de processamento de imagens.

• RenderedImage é uma interface padrão de Java, implementada pela classe Buf-feredImage, que define que métodos devem ser implementados por classes que con-tenham dados na forma de Rasters.

• PlanarImage é uma classe da API JAI que também implementa RenderedImagemas que pode representar imagens de forma bem mais complexa do que Buffered-Image, por exemplo, incluindo a capacidade de representar imagens como nós em umgrafo, possibilitando, por exemplo, que uma imagem seja definida em função de outras,mas permitindo somente a criação e leitura (mas não modificação!) de seus pixels.

• TiledImage é uma subclasse mais flexível de PlanarImage que permite acessodireto a seus pixels e tiles. Um tile ou ladrilho é um segmento retangular da imagemque pode ser processado independente dos outros segmentos, possibilitando o proces-samento parcial de imagens, o que é muito útil para o processamento de imagens degrande porte.

Existem métodos de conversão de instâncias de imagens de um tipo para outro. Deforma simples, para criar imagens na memória usamos BufferedImage para imagens sim-ples e TiledImage para imagens com características mais complexas. Usaremos instânciasde PlanarImage para representar nós em grafos de processamento com os operadores daAPI JAI, como será mostrado posteriormente.

Como primeiro exemplo de criação de imagens na memória, vejamos como criar umaimagem colorida em Java usando a classe BufferedImage (ou seja, sem usar a API JAI).Instâncias desta classe podem ser criadas de forma simples, ocultando a complexidade internada classe. Com a instância criada podemos obter uma instância de WritableRaster emanipular os pixels da imagem através desta instância. O trecho de código mostrado naListagem 1 mostra como isto pode ser feito.

Listagem 1. Criando uma imagem RGB (sem usar a API JAI)12 public static void main(String[] args) throws IOException

4 RITA • Volume – • Número – • —-

Page 5: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

13 {14 int width = 256;15 int height = 256;16 BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);17 WritableRaster raster = image.getRaster();18 int[] cor1 = new int[]{255,0,0};19 int[] cor2 = new int[]{0,0,255};20 int cont=0;21 for(int h=0;h<height;h++)22 for(int w=0;w<width;w++)23 {24 if ((((w/32)+(h/32)) % 2) == 0) raster.setPixel(w,h,cor1);25 else raster.setPixel(w,h,cor2);26 }27 ImageIO.write(image,"PNG",new File("checkerboard.png"));28 }

O código mostrado na Listagem 1 cria uma imagem com duas cores básicas formandoum padrão de tabuleiro de xadrez. A imagem terá três bandas, usando o sistema de coresRGB, ou seja, cada pixel terá um componente vermelho, um verde e um azul. A imagem seráarmazenada no formato PNG em um arquivo local.

A criação de uma imagem usando a API JAI é consideravelmente mais complexa, masesta complexidade permite enorme flexibilidade nos formatos e estruturas internas. Os passossão os seguintes:

1. Criar os dados da imagem em um array unidimensional (o mapeamento das coordena-das da imagem deve ser feito pelo programador).

2. Com este array, criar uma instância de classe que herda de DataBuffer do tipoadequado.

3. Criar uma instância de SampleModel do tipo e dimensões adequadas. A classeRasterFactory contém métodos-fábrica que facilitam este passo.

4. Com a instância de SampleModel criar um ColorModel compatível.5. Usar as instâncias de DataBuffer e SampleModel para criar uma instância de

WritableRaster. Esta instância, depois de criada, pode ser também usada paramanipular os pixels desta imagem.

6. Usar as instâncias de SampleModel e ColorModel para criar uma instância deTiledImage.

7. Associar o Raster à instância de TiledImage.

Ao fim destes passos a instância de TiledImage pode ser manipulada, visualizadaou armazenada. Como exemplo, vejamos o trecho de código na Listagem 2, que cria umaimagem com pixels de ponto flutuante. A imagem é armazenada no formato TIFF, que per-mite o armazenamento de pixels de ponto flutuante.

RITA • Volume – • Número – • —- 5

Page 6: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

Listagem 2. Criando uma imagem com pixels de ponto flutuante (usando JAI)18 public static void main(String[] args) throws IOException19 {20 int width = 256;21 int height = 256;22 float[] imageData = new float[width*height];23 int count = 0;24 for(int h=0;h<height;h++)25 for(int w=0;w<width;w++)26 imageData[count++] = 20f*w*h;27 DataBufferFloat dbuffer = new DataBufferFloat(imageData,width*height);28 SampleModel sampleModel =29 RasterFactory.createBandedSampleModel(DataBuffer.TYPE_FLOAT,width,height,1);30 ColorModel colorModel = PlanarImage.createColorModel(sampleModel);31 WritableRaster raster =32 RasterFactory.createWritableRaster(sampleModel,dbuffer,new Point(0,0));33 TiledImage tiledImage = new TiledImage(0,0,width,height,0,0,sampleModel,colorModel);34 tiledImage.setData(raster);35 JAI.create("filestore",tiledImage,"floatpattern.tif","TIFF");36 }

3 Entrada e Saída

Os exemplos mostrados até agora ilustram como imagens podem ser criadas na me-mória, mas a grande maioria das aplicações de processamento de imagens assume que asimagens já existem em um dispositivo, e que devem ser carregadas na memória para proces-samento ou exibição, podendo opcionalmente ser armazenadas novamente (com as modifica-ções) nos dispositivos.

Na seção 2 vimos que o formato de armazenamento das imagens em disco é diferentedo formato de armazenamento na memória. Para armazenar e recuperar imagens em dispo-sitivos devemos usar codificadores e decodificadores para transformar de um formato para ooutro. Estes codificadores e decodificadores são implementados em classes de Java em duasAPIs: a API ImageIO e em métodos da API JAI. As duas APIs se complementam, pois nemtodos os formatos são suportados pelas duas.

A classe ImageIO contém métodos estáticos para codificar e decodificar imagens(instâncias de BufferedImage) de e para arquivos e streams e para descobrir quais co-dificadores e decodificadores estão disponíveis para uso em uma determinada instalação damáquina virtual Java. O método getImageReadersByFormatName recebe um nomede um formato como parâmetro ("tiff", "jpg", etc) e retorna um Iterator de instâncias deImageReaders que podem ser usados para decodificar as imagens. O método getIma-geWritersByFormatName também recebe um nome de formato e retorna um Iteratorde instâncias de ImageWriters que podem ser usados para codificar as imagens. Para sa-ber quais codificadores e decodificadores podem ser usados basta percorrer este Iterator

6 RITA • Volume – • Número – • —-

Page 7: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

procurando uma instância adequada.

O armazenamento de uma imagem em arquivo ou stream pode ser feito usando ométodo estático ImageIO.write que, na sua forma mais usada, recebe três parâmetros: ainstância de classe que implementa RenderedImage, uma string com o formato no quala imagem será codificada e uma instância da classe File representando o arquivo onde aimagem será armazenada. Este método retorna o booleano false se não houver codificadorapropriado para a imagem. Um exemplo deste método pode ser visto na última linha daListagem 1.

Uma imagem pode ser decodificada de um arquivo ou stream com uma chamada aométodo estático ImageIO.read que na sua forma mais usada recebe um único argumento:uma instância da classe File representando o arquivo onde a imagem está armazenada.Este método retorna uma instância de BufferedImage ou null caso não seja possíveldecodificar a imagem. A Listagem 3 mostra uma classe que lê uma imagem de um arquivoem disco e mostra as suas dimensões.

Listagem 3. Decodificando uma imagem com o método ImageIO.read11 public static void main(String[] args) throws IOException12 {13 File f = new File(args[0]);14 BufferedImage image = ImageIO.read(f);15 System.out.println("Dimensões: "+image.getWidth()+"x"+image.getHeight()+" pixels");16 }

A codificação (gravação) e decodificação (leitura) de imagens podem ser feita tambémusando um método estático da API JAI. Este método é o método genérico de chamada deoperadores da API, executado com o parâmetro filestore, que indica que a imagem seráarmazenada em um arquivo local. O método é o JAI.create, e recebe quatro parâmetros:o primeiro é uma string contendo a operação (filestore), o segundo é a instância de classeque implementa RenderedImage, o terceiro é o nome do arquivo para armazenamento e oquarto é o formato de codificação a ser usado. Um exemplo de chamada deste operador podeser visto na última linha da Listagem 2. Para leitura de imagens usando a API JAI podemosusar o método estático JAI.create com o primeiro parâmetro igual à string fileload eo segundo contendo o nome do arquivo a ser lido. Se a imagem for decodificada com sucessoserá armazenada em uma instância de classe que herda de PlanarImage.

4 Acesso a valores de pixels

Para a implementação de alguns algoritmos específicos de processamento de imagens,é necessário obter os valores dos pixels em determinadas posições ou regiões. A forma maissimples de fazer isto é usando uma imagem já na memória (instância de BufferedImage

RITA • Volume – • Número – • —- 7

Page 8: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

ou PlanarImage, por exemplo), obtendo o Raster associado a ela e usando um dosmétodos getSample ou getPixel. Estes métodos recuperam um valor de um pixel outodos os valores (array) associado a um pixel, respectivamente.

Um exemplo é dado pelo trecho de código na Listagem 4, que varre todos os pixels deuma imagem contando os que são exatamente brancos.

Listagem 4. Recuperando valores de pixels (sem usar JAI)12 public static void main(String[] args) throws IOException13 {14 File f = new File(args[0]);15 BufferedImage imagem = ImageIO.read(f);16 Raster raster = imagem.getRaster();17 int[] pixel = new int[3];18 int brancos = 0;19 for(int h=0;h<imagem.getHeight();h++)20 for(int w=0;w<imagem.getWidth();w++)21 {22 raster.getPixel(w,h,pixel);23 if ((pixel[0] == 255) && (pixel[1] == 255) && (pixel[2] == 255)) brancos++;24 }25 System.out.println(brancos+" pixels brancos");26 }

Valores de pixels também podem ser obtidos diretamente de uma instância de Rende-redImage com o método getRGB, que retorna os bytes correspondentes aos valores R, G,B e A (transparência) compactados em um valor inteiro.

A API JAI permite também a recuperação de Rasters de instâncias de PlanarI-mage, mas também provê uma forma alternativa de obter os valores dos pixels. A classeRandomIter permite a criação de um mecanismo de iteração associado a uma imagem pararecuperação dos valores, como mostrado no exemplo da Listagem 5, que faz basicamente omesmo que a classe na Listagem 4 usando iteradores.

Listagem 5. Recuperando valores de pixels (usando JAI)13 public static void main(String[] args) throws IOException14 {15 File f = new File(args[0]);16 BufferedImage imagem = ImageIO.read(f);17 RandomIter iterator = RandomIterFactory.create(imagem,null);18 int[] pixel = new int[3];19 int brancos = 0;20 for(int h=0;h<imagem.getHeight();h++)21 for(int w=0;w<imagem.getWidth();w++)22 {23 iterator.getPixel(w,h,pixel);24 if ((pixel[0] == 255) && (pixel[1] == 255) && (pixel[2] == 255)) brancos++;25 }26 System.out.println(brancos+" pixels brancos");27 }

8 RITA • Volume – • Número – • —-

Page 9: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

A API JAI provê também iteradores sequenciais através das classes RectIter eRookIter. Versões que permitem a modificação dos valores dos pixels são implementadaspelas classes WritableRandomIter, WritableRectIter e WritableRookIter.Pixels também podem ser modificados em instâncias de TiledImage através do métodosetSample, e em instâncias da classe WritableRaster através dos métodos setSam-ple, setPixel, setSamples e setPixels.

5 Display de imagens

Exibição de imagens em um monitor é uma função indispensável de um sistema deprocessamento de imagens. Aplicações que exibem imagens podem ser facilmente escritasusando Java, com ou sem a API JAI.

Podemos usar classes da API Swing para exibir instâncias de BufferedImagesatravés da criação de uma instância da classe JLabel que deve receber no seu construtor,como argumento uma instância de ImageIcon que por sua vez recebe como argumentouma instância de BufferedImage. A instância de JLabel pode ser adicionada à interfacegráfica de uma aplicação envolvida em uma instância de JScrollPane para conter barrasde rolagem que são necessárias para imagens com áreas maiores do que a disponível nainterface gráfica.

A técnica é exemplificada na classe mostrada na Listagem 6. A Figura 1 mostra ainterface criada pela aplicação na Listagem 6.

Listagem 6. Exibindo uma instância de BufferedImage (sem usar JAI)15 public static void main(String[] args) throws IOException16 {17 BufferedImage image = ImageIO.read(new File(args[0]));18 JFrame frame = new JFrame("Display Image: "+args[0]);19 ImageIcon icon = new ImageIcon(image);20 JLabel imageLabel = new JLabel(icon);21 frame.getContentPane().add(new JScrollPane(imageLabel));22 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);23 frame.setSize(600,300);24 frame.setVisible(true);25 }

A exibição de imagens também pode ser feita usando uma classe específica da APIJAI: DisplayJAI. Esta classe representa um componente gráfico que pode ser adicionado àqualquer interface gráfica de aplicações em Java. Apesar desta classe não ser parte oficial daAPI ela é provida pela implementação da Sun Microsystems, e além de ser mais flexível doque a combinação ImageIcon/JLabel ela permite a visualização de imagens de grandeporte, contanto que sejam ladrilhadas (tiled).

RITA • Volume – • Número – • —- 9

Page 10: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

Figura 1. Interface gráfica da classe DisplaySemJAI

O trecho de código na Listagem 7 mostra como usar uma instância de DisplayJAIpara exibir uma instância de BufferedImage. A interface com o usuário é semelhante àmostrada na Figura 1.

Listagem 7. Exibindo uma instância de BufferedImage (usando JAI)15 public static void main(String[] args) throws IOException16 {17 BufferedImage image = ImageIO.read(new File(args[0]));18 JFrame frame = new JFrame("Display Image: "+args[0]);19 DisplayJAI display = new DisplayJAI(image);20 frame.getContentPane().add(new JScrollPane(display));21 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);22 frame.setSize(600,300);23 frame.setVisible(true);24 }

A classe DisplayJAI pode ser usada como base para outras finalidades mais es-pecíficas. Consideremos, por exemplo, o problema de visualizar imagens cujos pixels nãosão valores inteiros, ou cujos pixels representem mais do que três valores. É relativamentesimples criar uma classe herdeira de DisplayJAI que, a partir de uma destas imagensespeciais, crie uma imagem substituta para visualização.

Para o caso de imagens cujos pixels podem assumir valores diferentes dos entre 0 e255, podemos normalizar os valores subtraindo de cada pixel o valor Vmin e multiplicandoo resultado por 255/(Vmax − Vmin), onde Vmin e Vmax são respectivamente os menorese maiores valores da imagem original. A imagem normalizada pode ser convertida parao tipo adequado (byte) e exibida normalmente. Os valores extremos das imagens podemser obtidos com operadores estatísticos da API JAI. Esta técnica é demonstrada na classeDisplaySurrogateImage, mostrada na Listagem 8.

10 RITA • Volume – • Número – • —-

Page 11: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

Listagem 8. Componente para criação e exibição de uma imagem substituta (usando JAI)1 package wvc.display;2

3 import java.awt.image.DataBuffer;4 import java.awt.image.renderable.ParameterBlock;5

6 import javax.media.jai.JAI;7 import javax.media.jai.PlanarImage;8

9 import com.sun.media.jai.widget.DisplayJAI;;10

11 public class DisplaySurrogateImage extends DisplayJAI12 {13

14 protected PlanarImage surrogateImage;15 protected int width,height;16

17 public DisplaySurrogateImage(PlanarImage image)18 {19 width = image.getWidth();20 height = image.getHeight();21 // Recuperamos valores extremos da imagem.22 ParameterBlock pbMaxMin = new ParameterBlock();23 pbMaxMin.addSource(image);24 PlanarImage extrema = JAI.create("extrema", pbMaxMin);25 double[] allMins = (double[])extrema.getProperty("minimum");26 double[] allMaxs = (double[])extrema.getProperty("maximum");27 double minValue = allMins[0];28 double maxValue = allMaxs[0];29 for(int v=1;v<allMins.length;v++)30 {31 if (allMins[v] < minValue) minValue = allMins[v];32 if (allMaxs[v] > maxValue) maxValue = allMaxs[v];33 }34 // Reescalamos os níveis de cinza da imagem.35 double[] subtract = new double[1]; subtract[0] = minValue;36 double[] multiplyBy = new double[1]; multiplyBy[0] = 255./(maxValue-minValue);37 ParameterBlock pbSub = new ParameterBlock();38 pbSub.addSource(image);39 pbSub.add(subtract);40 surrogateImage = (PlanarImage)JAI.create("subtractconst",pbSub);41 ParameterBlock pbMult = new ParameterBlock();42 pbMult.addSource(surrogateImage);43 pbMult.add(multiplyBy);44 surrogateImage = (PlanarImage)JAI.create("multiplyconst",pbMult);45 // Convertemos para bytes.46 ParameterBlock pbConvert = new ParameterBlock();47 pbConvert.addSource(surrogateImage);48 pbConvert.add(DataBuffer.TYPE_BYTE);49 surrogateImage = JAI.create("format", pbConvert);50 // Usamos esta imagem para display.51 set(surrogateImage);52 }53

54 }

Um exemplo de uso da classe DisplaySurrogateImage pode ser visto na Lista-

RITA • Volume – • Número – • —- 11

Page 12: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

gem 9. Este exemplo é usado para mostrar uma imagem substituta correspondente à imagemcom pixels de ponto flutuante gerada pela classe da Listagem 2. A interface gráfica da apli-cação é mostrada na Figura 2.

Listagem 9. Aplicação que demonstra a classe DisplaySurrogateImage10 public static void main(String[] args)11 {12 PlanarImage image = JAI.create("fileload", args[0]);13 JFrame frame = new JFrame("Mostrando "+args[0]);14 frame.getContentPane().add(new JScrollPane(new DisplaySurrogateImage(image)));15 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);16 frame.pack();17 frame.setVisible(true);18 }

Figura 2. Interface gráfica da classe DemonstraDisplaySurrogateImage

Um outro exemplo de extensão da classe DisplayJAI é mostrado a seguir. Podemosusar duas instâncias de DisplayJAI para exibir duas imagens com janelamento sincroni-zado – se as áreas imagens forem maiores do que a área disponível na interface gráfica elasaparecerão dentro de JScrollPanes, e quando uma das barras de rolagem de uma imagemfor reposicionada a outra imagem também será reposicionada para mostrar a região corres-pondente à primeira. Esta técnica é implementada através da criação de um novo componentegráfico, que herda de JPanel. A classe é mostrada na Listagem 10.

Listagem 10. Exibindo duas instâncias de DisplayJAI de forma sincronizada1 package wvc.display;2

3 import java.awt.GridLayout;4 import java.awt.event.AdjustmentEvent;5 import java.awt.event.AdjustmentListener;6 import java.awt.image.RenderedImage;7

12 RITA • Volume – • Número – • —-

Page 13: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

8 import javax.swing.JPanel;9 import javax.swing.JScrollPane;

10

11 import com.sun.media.jai.widget.DisplayJAI;12

13 public class DisplayTwoSynchronizedImages extends JPanel implements AdjustmentListener14 {15 protected DisplayJAI dj1;16 protected DisplayJAI dj2;17 protected JScrollPane jsp1;18 protected JScrollPane jsp2;19

20 public DisplayTwoSynchronizedImages(RenderedImage im1, RenderedImage im2)21 {22 super();23 // Cria componente com duas imagens com JScrollPanes24 setLayout(new GridLayout(1,2));25 dj1 = new DisplayJAI(im1);26 dj2 = new DisplayJAI(im2);27 jsp1 = new JScrollPane(dj1);28 jsp2 = new JScrollPane(dj2);29 add(jsp1);30 add(jsp2);31 // Registra listeners para os scroll bars do JScrollPanes32 jsp1.getHorizontalScrollBar().addAdjustmentListener(this);33 jsp1.getVerticalScrollBar().addAdjustmentListener(this);34 jsp2.getHorizontalScrollBar().addAdjustmentListener(this);35 jsp2.getVerticalScrollBar().addAdjustmentListener(this);36 }37

38 public void adjustmentValueChanged(AdjustmentEvent e)39 {40 if (e.getSource() == jsp1.getHorizontalScrollBar())41 jsp2.getHorizontalScrollBar().setValue(e.getValue());42 if (e.getSource() == jsp1.getVerticalScrollBar())43 jsp2.getVerticalScrollBar().setValue(e.getValue());44 if (e.getSource() == jsp2.getHorizontalScrollBar())45 jsp1.getHorizontalScrollBar().setValue(e.getValue());46 if (e.getSource() == jsp2.getVerticalScrollBar())47 jsp1.getVerticalScrollBar().setValue(e.getValue());48 }49

50 }

A classe DisplayTwoSynchronizedImages (Listagem 10) será bastante uti-lizada na seção seguinte para mostrar, lado a lado, algumas imagens originais e depois deprocessadas com operadores da API JAI.

6 Operadores da API Java Advanced Imaging

Já vimos que a API JAI provê um método estático JAI.create que recebe umnúmero variável de argumentos e que executa algum tipo de operação em instâncias de classesque representam imagens, que são passadas como argumentos ou que são retornadas deste

RITA • Volume – • Número – • —- 13

Page 14: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

método. O operador filestore foi mostrado na Listagem 2 e o operador fileload naListagem 9. Alguns outros operadores podem ser vistos na Listagem 8, e seu uso será descritocom detalhes nesta seção.

Como diferentes operadores podem ter diferentes números, tipos de parâmetros e aténúmero de imagens para processamento, é necessário ter uma forma simples de executá-los através do método JAI.create, sem ter inúmeras versões do mesmo. Isto é feitousando-se instâncias da classe ParameterBlock para armazenar as fontes (imagens) eos parâmetros necessários a cada tipo de operador, e chamando o método JAI.createcom esta instância de ParameterBlock. Como cada operador espera um determinadonúmero e tipo de parâmetros, os mesmos devem ser armazenados na ordem e com os tiposesperados na instância de ParameterBlock (exceto para operadores simples, com poucosparâmetros, embora o uso também seja possível). Exemplos específicos serão mostrados paraos diversos operadores.

Para simplificar a apresentação dos operadores, os mesmos foram divididos em cate-gorias. Para cada categoria alguns dos operadores mais usados serão exemplificados.

6.1 Operadores que modificam cores ou níveis de cinza

Alguns operadores podem ser usados para manipular os valores dos pixels das imagensde forma comum e independente para cada pixel. Um operador bem simples que implementaesta conceito é o operador invert, que inverte os valores de cada pixel em uma imagem. Seos pixels tiverem valores com sinais, o operador simplesmente inverte os sinais, caso contráriosubtrai os valores dos pixels do máximo valor representável na imagem. O uso deste operadoré realmente simples, e como somente o nome do operador e a imagem de origem são neces-sários como parâmetros, não precisamos usar uma instância de ParameterBlock. Umtrecho de código que carrega e inverte os pixels de uma imagem é mostrado na Listagem 11.

Listagem 11. Invertendo uma imagem (usando JAI)11 public static void main(String[] args)12 {13 PlanarImage input = JAI.create("fileload", args[0]);14 PlanarImage output = JAI.create("invert", input);15 JFrame frame = new JFrame();16 frame.setTitle("Invert image "+args[0]);17 frame.getContentPane().add(new DisplayTwoSynchronizedImages(input,output));18 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);19 frame.pack();20 frame.setVisible(true);21 }

Outro operador que implementa uma função bastante usada em processamento deimagens é o binarize, que recebe como parâmetros uma imagem e um valor limiar e

14 RITA • Volume – • Número – • —-

Page 15: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

binariza todos os pixels da imagem, ou seja, cria uma imagem com valores 1 se o valor dopixel original for maior que o limiar ou 0 caso contrário. Caso a imagem tenha mais de umabanda, estas serão binarizadas independentemente.

Um exemplo de binarização pode ser visto no trecho de código da Listagem 12.Como precisamos passar como parâmetros a imagem original e o valor limiar, usaremosuma instância de ParameterBlock para adicionar a imagem original (usando o métodoaddSource) e o valor limiar (com o método add). A classe também mostra como usar umainstância de DisplayTwoSynchronizedImages (Listagem 10) para exibir a imagemoriginal e binarizada.

Listagem 12. Binarizando uma imagem (usando JAI)13 public static void main(String[] args)14 {15 PlanarImage imagem = JAI.create("fileload", args[0]);16 ParameterBlock pb = new ParameterBlock();17 pb.addSource(imagem);18 pb.add(127.0);19 PlanarImage binarizada = JAI.create("binarize", pb);20 JFrame frame = new JFrame("Imagem binarizada");21 frame.add(new DisplayTwoSynchronizedImages(imagem,binarizada));22 frame.pack();23 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);24 frame.setVisible(true);25 }

Figura 3. Interface gráfica da classe Binariza

Um terceiro exemplo de operador que modificam cores é mostrado a seguir. Imagenscoloridas são representadas em um sistema de coordenadas de cores que considera três va-lores por pixel, cada um correspondente à intensidade nas bandas vermelho, verde e azul(RGB), devido à forma com que a maioria dos dispositivos de captura e exibição de imagens

RITA • Volume – • Número – • —- 15

Page 16: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

funcionam.

Outros sistemas de cores são baseados em outras características: um destes é o sistemaIHS (Intensity, Hue e Saturation; ou intensidade, croma e saturação) que representa coresde forma mais perceptual: intensidade indica o quanto uma cor é clara (com branco tendoalta intensidade e preto baixa intensidade); croma representa a cor base (vermelho, amarelo,verde, etc.) e saturação representando a quantidade de pigmentação da cor (vermelho sendobem saturado e rosa pouco saturado, por exemplo). Algumas operações de processamentode imagens baseadas no sistema IHS são reforço das cores, melhoria na clareza da imageme melhoria na qualidade de imagens de sensoriamento remoto através da mistura de imagensde diferentes resoluções [2].

É possível fazer operações em imagens, convertendo os pixels entre os sistemas RGBe IHS para obter diversos efeitos, usando os operadores de JAI. Para exemplificar, faremos amanipulação de uma imagem colorida, convertendo-a para o espaço de cores IHS, separandoa banda H, criando bandas constantes para I e S, compondo novamente a imagem IHS econvertendo-a para RGB, obtendo assim uma imagem com cores fortemente evidenciadas.Os passos são ilustrados pelo código na Listagem 13.

Listagem 13. Manipulando uma imagem no espaço de cores IHS21 public static void main(String[] args)22 {23 PlanarImage imagem = JAI.create("fileload", args[0]);24 // Converte para o modelo de cores IHS.25 IHSColorSpace ihs = IHSColorSpace.getInstance();26 ColorModel modeloIHS =27 new ComponentColorModel(ihs,28 new int []{8,8,8},29 false,false,30 Transparency.OPAQUE,31 DataBuffer.TYPE_BYTE) ;32 ParameterBlock pb = new ParameterBlock();33 pb.addSource(imagem);34 pb.add(modeloIHS);35 RenderedImage imagemIHS = JAI.create("colorconvert", pb);36 // Extraímos as bandas I, H e S.37 RenderedImage[] bandas = new RenderedImage[3];38 for(int band=0;band<3;band++)39 {40 pb = new ParameterBlock();41 pb.addSource(imagemIHS);42 pb.add(new int[]{band});43 bandas[band] = JAI.create("bandselect",pb);44 }45 // Criamos bandas constantes para as bandas I e S.46 pb = new ParameterBlock();47 pb.add((float)imagem.getWidth());48 pb.add((float)imagem.getHeight());49 pb.add(new Byte[]{(byte)255});50 RenderedImage novaIntensidade = JAI.create("constant",pb);51 pb = new ParameterBlock();52 pb.add((float)imagem.getWidth());

16 RITA • Volume – • Número – • —-

Page 17: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

53 pb.add((float)imagem.getHeight());54 pb.add(new Byte[]{(byte)255});55 RenderedImage novaSaturação = JAI.create("constant",pb);56 // Juntamos as bandas H e as I e S constantes.57 // Devemos passar um RenderingHint que indica que o modelo de cor IHS será usado.58 ImageLayout imageLayout = new ImageLayout();59 imageLayout.setColorModel(modeloIHS);60 imageLayout.setSampleModel(imagemIHS.getSampleModel());61 RenderingHints rendHints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT,imageLayout);62 pb = new ParameterBlock();63 pb.addSource(novaIntensidade);64 pb.addSource(bandas[1]);65 pb.addSource(novaSaturação);66 RenderedImage imagemIHSModificada = JAI.create("bandmerge", pb, rendHints);67 // Convertemos de volta para RGB.68 pb = new ParameterBlock();69 pb.addSource(imagemIHSModificada);70 pb.add(imagem.getColorModel()); // Imagem original era em RGB!71 RenderedImage imagemFinal = JAI.create("colorconvert", pb);72 JFrame frame = new JFrame("Modificação via IHS");73 frame.add(new DisplayTwoSynchronizedImages(imagem,imagemFinal));74 frame.pack();75 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);76 frame.setVisible(true);77 }

A classe na Listagem 13 usa os seguintes operadores de JAI:

• fileload, para decodificar a imagem;• colorconvert, para converter a imagem do espaço de cores RGB para IHS e vice-

versa;• bandselect, para separar a imagem IHS nas três bandas componentes;• constant, para criar imagens com as mesmas dimensões das originais mas com

somente uma banda de valor constante; e• bandmerge, para juntar as três bandas em uma nova imagem IHS.

A parte realmente mais complexa do código na Listagem 13 é a que faz a unificaçãodas bandas I, H e S – para que a unificação seja feita corretamente devemos informar ooperador bandmerge de que a imagem a ser composta deve estar no sistema de cores IHS,o que é feito entre as linhas 58 e 61 da Listagem.

O resultado da conversão e manipulação é uma imagem com cores fortemente realça-das, que é mostrada ao lado da imagem original. Um exemplo de aplicação deste conjunto deoperadores é mostrado na Figura 4.

6.2 Operadores em regiões

Outras funções usados frequentemente em processamento de imagens (e implementa-dos como operadores pela API JAI) envolvem o cálculo de um valor baseado em uma vizi-

RITA • Volume – • Número – • —- 17

Page 18: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

Figura 4. Interface gráfica da classe RGBtoIHS

nhança local de pixels – em outras palavras, alguns operadores calcularão o valor de um pixelem uma imagem de saída usando os valores dos pixels da imagem de entrada próximos àscoordenadas daquele pixel.

Um dos operadores mais simples desta categoria é o que filtra uma imagem suavizandoos valores de seus pixels. O valor de um pixel na imagem de saída é calculado tirando-se amédia dos valores dos pixels na vizinhança do pixel correspondente na imagem de entrada.Para calcular este valor precisamos de uma definição de região de vizinhança, que inclui opeso (multiplicador) que deve ser dado a cada pixel na vizinhança, com isto podemos usar ooperador convolve, como exemplificado pelo trecho de código na Listagem 14.

Listagem 14. Suavizando os pixels de uma imagem12 public static void main(String[] args)13 {14 PlanarImage imagem = JAI.create("fileload", args[0]);15 float[] kernelMatrix = { 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f,16 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f,17 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f,18 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f,19 1f/25f, 1f/25f, 1f/25f, 1f/25f, 1f/25f};20 KernelJAI kernel = new KernelJAI(5,5,kernelMatrix);21 PlanarImage bordas = JAI.create("convolve",imagem,kernel);22 JFrame frame = new JFrame("Suavização da imagem");23 frame.add(new DisplayTwoSynchronizedImages(imagem,bordas));24 frame.pack();25 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);26 frame.setVisible(true);

18 RITA • Volume – • Número – • —-

Page 19: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

27 }

O código na Listagem 14 declara uma vizinhança de 5×5 pixels, todos com o mesmopeso para multiplicação (1/25) e usa esta vizinhança para suavizar a imagem de entrada. Avizinhança é representada por uma instância da classe KernelJAI, que é criada com umarray de valores de ponto flutuante. A imagem original e a filtrada são mostradas em umainterface gráfica. Um resultado de aplicação é mostrado na Figura 5.

Figura 5. Interface gráfica da classe Suaviza

Outra operação que envolve vizinhanças é a identificação de bordas locais. Váriosoperadores existem para este tipo de operação [3, 4], sendo que um dos mais populares é ooperador Sobel, que requer a convolução da imagem com um filtro com valores específicospara deteção de bordas horizontais e verticais. O código na Listagem 15 mostra como aplicaro operador Sobel horizontal em uma imagem usando o operador convolve da API JAI.

Listagem 15. Detetando bordas horizontais em uma imagem12 public static void main(String[] args)13 {14 PlanarImage imagem = JAI.create("fileload", args[0]);15 float[] kernelMatrix = { -1, -2, -1,16 0, 0, 0,17 1, 2, 1 };18 KernelJAI kernel = new KernelJAI(3,3,kernelMatrix);19 PlanarImage bordas = JAI.create("convolve",imagem,kernel);20 JFrame frame = new JFrame("Bordas horizontais");21 frame.add(new DisplayTwoSynchronizedImages(imagem,bordas));22 frame.pack();

RITA • Volume – • Número – • —- 19

Page 20: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

23 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);24 frame.setVisible(true);25 }

A deteção de bordas horizontais pode ser vista na Figura 6, que mostra a imagemoriginal e a com as bordas detectadas lado a lado.

Figura 6. Interface gráfica da classe Borda

Ainda outros operadores que calculam valores de pixels usando regiões na imagem deentrada são relacionados com morfologia matemática [1]. Dois dos operadores mais comunsde morfologia matemática são dilatação e erosão: estes operadores usam uma região chamadaelemento estrutural que pode ser definida como uma vizinhança. A erosão de uma imagemem níveis de cinza causa a expansão ou crescimento de regiões da imagem; enquanto suadilatação causa a remoção de pequenas regiões da imagem.

As duas operações básicas são implementadas pela API JAI usando os operadoresdilate e erode. O trecho de código da Listagem 16 demonstra o uso do operadordilate com uma máscara semelhante à usada como vizinhança em exemplos anteriores.

Listagem 16. Dilatação de uma imagem14 public static void main(String[] args)15 {16 PlanarImage imagem = JAI.create("fileload", args[0]);17 float[] estrutura = { 0, 0, 0, 0, 0, 0, 0,18 0, 1, 1, 1, 1, 1, 0,

20 RITA • Volume – • Número – • —-

Page 21: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

19 0, 1, 1, 1, 1, 1, 0,20 0, 1, 1, 1, 1, 1, 0,21 0, 1, 1, 1, 1, 1, 0,22 0, 1, 1, 1, 1, 1, 0,23 0, 0, 0, 0, 0, 0, 0};24 KernelJAI kernel = new KernelJAI(7,7,estrutura);25 ParameterBlock p = new ParameterBlock();26 p.addSource(imagem);27 p.add(kernel);28 PlanarImage dilatada = JAI.create("dilate",p);29 JFrame frame = new JFrame("Imagem dilatada");30 frame.add(new DisplayTwoSynchronizedImages(imagem,dilatada));31 frame.pack();32 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);33 frame.setVisible(true);34 }

A Figura 7 mostra a imagem original e a dilatada, lado a lado. Podemos observar quesomente regiões que poderiam conter o elemento estrutural foram preservadas.

Figura 7. Interface gráfica da classe Dilata

O mesmo código pode ser adaptado para fazer a erosão da imagem com o mesmooperador, conforme mostrado no trecho de código da Listagem 17.

Listagem 17. Erosão de uma imagem26 p.addSource(imagem);27 p.add(kernel);28 PlanarImage erodida = JAI.create("erode",p);

O resultado da execução do código completo da Listagem 17 está na Figura 8, com aimagem original e a erodida, mostradas lado a lado.

RITA • Volume – • Número – • —- 21

Page 22: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

Figura 8. Interface gráfica da classe Erode

6.3 Operadores geométricos

Algumas operações servem para modificar a geometria (escala, posição, orientação,etc.) dos pixels das imagens. A API JAI também provê operadores para várias destas opera-ções. Um operador básico usado em tarefas de processamento de imagens de documentos é oque permite rotacionar os pixels de uma imagem em torno de uma determinada coordenada.Este operador (rotate) recebe como parâmetros a imagem de entrada, o ângulo de rotaçãoem radianos e a coordenada do ponto pivô da rotação. Outro parãmetro deste operador servepara determinar como os valores dos pixels serão calculados, já que um pixel na imagemde saída pode não ter correspondente direto em um pixel na imagem de entrada, sendo fre-quentemente calculado através de interpolações. O último parâmetro determina que tipo deinterpolação será usada neste cálculo.

A classe na Listagem 18 gira os pixels de uma imagem em torno do ponto centralda mesma, interpolando os valores com interpolação bilinear. O resultado, com a imagemoriginal e a rotacionada, é mostrado na Figura 9.

Listagem 18. Rotação de uma imagem em torno de um ponto14 public static void main(String[] args)15 {16 PlanarImage imagem = JAI.create("fileload",args[0]);17 float angle = (float)Math.toRadians(10);18 // Usamos o centro da imagem para rotação19 float centerX = imagem.getWidth()/2f;20 float centerY = imagem.getHeight()/2f;21 ParameterBlock pb = new ParameterBlock();22 pb.addSource(imagem);23 pb.add(centerX);

22 RITA • Volume – • Número – • —-

Page 23: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

24 pb.add(centerY);25 pb.add(angle);26 pb.add(new InterpolationBilinear());27 PlanarImage rotacionada = JAI.create("rotate", pb);28 JFrame frame = new JFrame("Imagem rotacionada");29 frame.add(new DisplayTwoSynchronizedImages(imagem,rotacionada));30 frame.pack();31 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);32 frame.setVisible(true);33 }

Figura 9. Interface gráfica da classe Rotaciona

Outra operação comum é a de modificar as dimensões de uma imagem, mudando aquantidade dos pixels da mesma. Pixels na imagem de saída devem ser calculados inter-polando os valores correspondentes dos pixels da imagem de entrada. O operador scalepermite a modificação das escalas da imagem de forma independente (vertical e horizontal),além de permitir uma translação opcional. O operador é demonstrado no trecho de códigomostrado na Listagem 19.

Listagem 19. Mudando a escala de uma imagem14 public static void main(String[] args)15 {16 PlanarImage imagem = JAI.create("fileload",args[0]);17 float scale = 0.3f;18 ParameterBlock pb = new ParameterBlock();19 pb.addSource(imagem);20 pb.add(scale);21 pb.add(scale);22 pb.add(0.0F);23 pb.add(0.0F);24 pb.add(new InterpolationNearest());25 PlanarImage reescalada = JAI.create("scale", pb);26 JFrame frame = new JFrame("Imagem reescalada");27 frame.add(new DisplayTwoSynchronizedImages(imagem,reescalada));

RITA • Volume – • Número – • —- 23

Page 24: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

28 frame.pack();29 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);30 frame.setVisible(true);31 }

No exemplo mostrado na Listagem 19 usamos a mesma escala horizontal e vertical nãofoi feita nenhuma translação nos pixels da imagem (parâmetros 0.0F usados nas linhas 22e 23). O resultado da aplicação pode ser visto na Figura 10.

Figura 10. Interface gráfica da classe Escala

6.4 Operadores estatísticos

Alguns operadores não criam, como resultado, imagens filtradas ou modificadas, e simsão usados para obter valores e medidas das imagens. A API JAI provê alguns operadoresestatísticos que podem ser usados para medir atributos das imagens. Um destes operadoresé o histogram, que calcula um histograma de uma imagem (contendo a distribuição dosvalores dos pixels em diversos intervalos ou bins) e que armazena este histograma em umapseudo-imagem de saída, como um atributo especial.

O trecho de código mostrado na Listagem 20 mostra como calcular dois histogramasda mesma imagem de entrada. O primeiro histograma usa 256 intervalos ou bins, e o segundo,somente 32. Os histogramas são exibidos usando uma classe que implementa um componentegráfico, não mostrada neste tutorial (veja [7] para o código-fonte desta classe).

Listagem 20. Calculando histogramas de uma imagem15 public static void main(String[] args)16 {

24 RITA • Volume – • Número – • —-

Page 25: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

17 PlanarImage image = JAI.create("fileload", args[0]);18 // Primeiro histograma com 256 bins.19 ParameterBlock pb1 = new ParameterBlock();20 pb1.addSource(image);21 pb1.add(null);22 pb1.add(1); pb1.add(1);23 pb1.add(new int[]{256});24 pb1.add(new double[]{0}); pb1.add(new double[]{256});25 PlanarImage dummyImage1 = JAI.create("histogram", pb1);26 Histogram histo1 = (Histogram)dummyImage1.getProperty("histogram");27 // Segundo histograma com 32 bins.28 ParameterBlock pb2 = new ParameterBlock();29 pb2.addSource(image);30 pb2.add(null);31 pb2.add(1); pb2.add(1);32 pb2.add(new int[]{32});33 pb2.add(new double[]{0}); pb2.add(new double[]{256});34 PlanarImage dummyImage2 = JAI.create("histogram", pb2);35 Histogram histo2 = (Histogram)dummyImage2.getProperty("histogram");36 // Exibimos os histogramas usando um componente específico.37 JFrame f = new JFrame("Histogramas");38 DisplayHistogram dh1 = new DisplayHistogram(histo1,"256 bins");39 dh1.setBinWidth(2); dh1.setHeight(160); dh1.setIndexMultiplier(1);40 DisplayHistogram dh2 = new DisplayHistogram(histo2,"32 bins");41 dh2.setBinWidth(16); dh2.setHeight(160); dh2.setIndexMultiplier(8);42 dh2.setSkipIndexes(2);43 f.getContentPane().setLayout(new GridLayout(2,1));44 f.getContentPane().add(dh1);45 f.getContentPane().add(dh2);46 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);47 f.pack();48 f.setVisible(true);

Para executar o operador histogram precisamos passar como parâmetros a imagemoriginal, uma área de interesse (ou null para usar toda a imagem), as taxas de amostragemhorizontal e vertical (podemos assim evitar amostrar todos os pixels na imagem), o númerode intervalos ou bins para cada banda da imagem e dois valores limiares inferior e superior.Pixels na imagem com valores abaixo do inferior ou acima do superior não serão consideradospara o cálculo do histograma. O histograma pode ser recuperado através da propriedadehistogram da pseudo-imagem de saída, que retorna uma instância da classe Histogramcom os valores dos histogramas.

A Figura 11 mostra os dois histogramas calculados com o código da Listagem 20. Aimagem usada é a mesma imagem mostrada na parte esquerda da Figura 10.

Outro operador estatístico da API JAI foi demonstrado na classe na Listagem 8: ooperador extrema calcula os valores mínimos e máximos em cada banda de uma imagem,e na sua forma mais simples recebe como parâmetro a imagem original, armazenando vetoresde valores na pseudo-imagem de saída nas propriedades minimum e maximum, que podemser recuperadas como arrays de valores do tipo double com os valores mínimos e máximosde cada banda.

RITA • Volume – • Número – • —- 25

Page 26: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

Figura 11. Interface gráfica da classe Histograma

6.5 Outros operadores

Existem mais de cem operadores já existentes na API JAI, que cobrem uma gamarazoável de operações comuns de processamento de imagens. Além dos operadores vistosem exemplos específicos temos operadores para modificação dos valores dos pixels usandoconstantes ou outras imagens como addconst, subtractconst (usado na Listagem 8),multiplyconst (também usado na Listagem 8), divideconst, dividebyconst,add subtract, multiply e divide e operadores que implementam operações boolea-nas entre imagens como or, and e xor.

Outros operadores geométricos são border, crop, shear, translate, trans-pose, border e warp. Outros operadores que permitem a modificação das cores dasimagens são lookup e colorquantizer. As bandas de uma imagem também podemser manipuladas com o operador bandcombine. Alguns operadores implementam funçõesrelacionadas com o domínio de frequência: DCT, IDCT, DFT e IDFT.

Outros operadores em regiões são relacionados com filtros, como, por exemplo, max-filter, medianfilter e boxfilter.

26 RITA • Volume – • Número – • —-

Page 27: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

O uso de alguns destes operadores, com código completo e comentado (em inglês) éexemplificado em [6] e [7].

7 Criando novos operadores

A API JAI permite também a criação de novos operadores e o registro dos mesmosjunto à classe JAI para que possam ser chamados pelo mesmo mecanismo usado no métodocreate, provendo ao programador uma interface única para a execução de operações. Ospassos para a criação de um operador são os seguintes:

1. Criar uma classe contendo operador que herde de uma das seguintes classes: Sour-celessOpImage para operadores que não precisem de imagens originais para cal-cular imagens de saída; PointOpImage para operadores que calculem os valores dospixels da imagem de saída baseado nos valores dos pixels correspondentes na imagemde entrada; AreaOpImage para operadores que precisem calcular valores de pixelsconsiderando pequenas áreas retangulares na imagem original; GeometricOpImagepara operadores que manipulem toda a imagem de entrada geometricamente ou Sta-tisticsOpImage para operadores que calculam valores estatísticos sobre a imagemde entrada.

2. Criar uma classe que implementa RenderedImageFactory que conterá um mé-todo que criará uma imagem a partir dos parâmetros do operador;

3. Criar uma classe que implementa OperationDescriptor ou herda de Opera-tionDescriptorImpl que descreve os parâmetros e valores default dos operado-res do mesmo.

4. Registrar o novo operador junto às instâncias estáticas de OperationRegistry eRIFRegistry. O registro pode ser feito por um método estático na própria classeque implementa OperationDescriptor.

7.1 Exemplo 1: um operador pontual

Como primeiro exemplo consideremos um segmentador por limiar ligeiramente dife-rente do implementado pelo operador binarize, que chamaremos de segmenta3: nesteoperador usaremos dois limiares t1 e t2, de forma que um pixel p na imagem de saída teráseu valor igual a 0 se o pixel na entrada tiver valor menor do que t1; 255 se tiver valor maiorque t2 e 127 nos outros casos.

A classe principal para implementação deste operador atua sobre os pontos das ima-gens de entrada e saída, portando deve herdar de PointOpImage. A classe completa émostrada na Listagem 21.

RITA • Volume – • Número – • —- 27

Page 28: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

Listagem 21. Classe que implementa o operador segmenta31 package wvc.operadores.segmenta;2

3 import java.awt.RenderingHints;4 import java.awt.image.Raster;5 import java.awt.image.RenderedImage;6 import java.awt.image.WritableRaster;7

8 import javax.media.jai.ImageLayout;9 import javax.media.jai.PointOpImage;

10

11 public class Segmenta3OpImage extends PointOpImage12 {13 private RenderedImage source;14 private int threshold1,threshold2;15

16 public Segmenta3OpImage(RenderedImage source,int th1,int th2,17 ImageLayout layout,RenderingHints hints, boolean b)18 {19 super(source,layout,hints,b);20 this.source = source;21 this.threshold1 = th1;22 this.threshold2 = th2;23 }24

25 public Raster computeTile(int x,int y)26 {27 Raster r = source.getTile(x,y);28 int minX = r.getMinX();29 int minY = r.getMinY();30 int width = r.getWidth();31 int height = r.getHeight();32 // Criamos um WritableRaster da região sendo considerada.33 WritableRaster wr = r.createCompatibleWritableRaster(minX,minY,width,height);34 for(int l=0;l<r.getHeight();l++)35 for(int c=0;c<r.getWidth();c++)36 for(int b=0;b<r.getNumBands();b++)37 {38 int p = r.getSample(c+minX,l+minY,b);39 if (p < threshold1) p = 0;40 else if (p > threshold2) p = 255;41 else p = 127;42 wr.setSample(c+minX,l+minY,b,p);43 }44 return wr;45 }46

47 }

A classe mostrada na Listagem 21 tem somente dois métodos: o construtor, que ini-cializa atributos da classe (uma referência à imagem original e os dois valores limiares)e o método computeTile, que será usado quando for necessário calcular uma região(tile) da imagem de saída. Este método varre o Raster da imagem original, criando umWritableRaster correspondente na imagem de saída e modificando os valores dos pi-xels no WritableRaster dependendo das comparações dos valores dos pixels da imagem

28 RITA • Volume – • Número – • —-

Page 29: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

original com os limiares. O exemplo é simples o bastante para ser facilmente reproduzido.

Além da classe que implementa o operador, temos que definir uma classe que criaráa imagem propriamente dita; esta classe deve implementar a interface RenderedImage-Factory que define um método create que retorna uma RenderedImage com a ima-gem resultante. Esta classe é mostrada na Listagem 22.

Listagem 22. RenderedImageFactory para o operador segmenta31 package wvc.operadores.segmenta;2

3 import java.awt.RenderingHints;4 import java.awt.image.RenderedImage;5 import java.awt.image.renderable.ParameterBlock;6 import java.awt.image.renderable.RenderedImageFactory;7

8 import javax.media.jai.ImageLayout;9

10 public class Segmenta3RIF implements RenderedImageFactory11 {12 public RenderedImage create(ParameterBlock paramBlock, RenderingHints hints)13 {14 RenderedImage source = paramBlock.getRenderedSource(0);15 int threshold1 = paramBlock.getIntParameter(0);16 int threshold2 = paramBlock.getIntParameter(1);17 ImageLayout layout = new ImageLayout(source);18 return new Segmenta3OpImage(source,threshold1,threshold2,layout,hints,false);19 }20 }

Finalmente precisamos de uma classe que descreva o operador, e que implementeOperationDescriptor ou herde de OperationDescriptorImpl. A classe paranosso exemplo é mostrada na Listagem 23.

Listagem 23. Classe que descreve o operador segmenta31 package wvc.operadores.segmenta;2

3 import javax.media.jai.JAI;4 import javax.media.jai.OperationDescriptorImpl;5 import javax.media.jai.OperationRegistry;6 import javax.media.jai.registry.RIFRegistry;7 import javax.media.jai.util.Range;8

9 public class Segmenta3Descriptor extends OperationDescriptorImpl10 {11 private static final String opName = "segmenta3";12 private static final String vendorName = "Hypothetical Image Processing Lab";13 private static final String[][] resources =14 {15 {"GlobalName", opName},16 {"LocalName", opName},17 {"Vendor", vendorName},18 {"Description", "A simple three-level image segmentation operator"},19 {"DocURL", "http://www.lac.inpe.br/~rafael.santos"},

RITA • Volume – • Número – • —- 29

Page 30: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

20 {"Version", "1.0"},21 {"arg0Desc", "First Threshold Value"},22 {"arg1Desc", "Second Threshold Value"}23 };24 private static final String[] supportedModes = {"rendered"};25 private static final String[] paramNames = {"1st threshold","2nd threshold"};26 private static final Class[] paramClasses = {Integer.class, Integer.class};27 private static final Object[] paramDefaults = { new Integer(85), new Integer(170) };28 private static final Range[] validParamValues =29 {30 new Range(Integer.class, Integer.MIN_VALUE, Integer.MAX_VALUE),31 new Range(Integer.class, Integer.MIN_VALUE, Integer.MAX_VALUE)32 };33 private static final int numSources = 1;34 private static boolean registered = false;35

36 public Segmenta3Descriptor()37 {38 super(resources,supportedModes,numSources,paramNames,39 paramClasses,paramDefaults,validParamValues);40 }41

42 public static void register()43 {44 if (!registered)45 {46 OperationRegistry op = JAI.getDefaultInstance().getOperationRegistry();47 Segmenta3Descriptor desc = new Segmenta3Descriptor();48 op.registerDescriptor(desc);49 Segmenta3RIF rif = new Segmenta3RIF();50 RIFRegistry.register(op,opName,vendorName,rif);51 registered = true;52 }53 }54

55 }

A classe na Listagem 23 contém algumas string estáticas descrevendo o operador e for-necedor, um mapa de indicadores (strings) e campos declarando o tipo de operador, os tiposde parâmetros, valores default, etc. O construtor desta classe simplesmente chama o constru-tor da classe ancestral com estes campos. O método estático register registra o descritordo operador na instância global de OperationRegistry e o RenderedImageFacto-ry para o operador na instância global de RIFRegistry.

Com estas classes prontas podemos usar o operador em qualquer aplicação. Um exem-plo do operador pode ser visto no trecho de código da Listagem 24. É importante observarque devemos registrar o operador antes de seu uso, conforme mostrado na linha 15.

Listagem 24. Exemplo de uso do operador segmenta313 public static void main(String[] args)14 {15 Segmenta3Descriptor.register();16 PlanarImage imagem = JAI.create("fileload", args[0]);

30 RITA • Volume – • Número – • —-

Page 31: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

17 ParameterBlock p = new ParameterBlock();18 p.addSource(imagem);19 p.add(new Integer(120));20 p.add(new Integer(200));21 PlanarImage resultado = JAI.create("segmenta3",p);22 JFrame frame = new JFrame("Imagem binarizada");23 frame.add(new DisplayTwoSynchronizedImages(imagem,resultado));24 frame.pack();25 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);26 frame.setVisible(true);27 }

A Figura 12 mostra uma imagem original e uma resultante da execução do operadorsegmenta3 lado a lado.

Figura 12. Interface gráfica da classe DemonstraSegmenta3

7.2 Exemplo 2: um operador estatístico

Como segundo exemplo consideremos um operador estatístico simples (contapi-xels) que conte o número de pixels semelhantes a uma determinada cor no espaço RGB,onde o critério de semelhança é implementado como a distância Euclideana entre o pixel daimagem e o valor referência – se a distância for menor ou igual a um valor de tolerânciaconsideramos o pixel como sendo igual àquela cor e incrementamos um contador.

A classe que implementa este operador só precisa dos parâmetros do operador e daimagem de entrada, portando deve herdar de StatisticsOpImage. A classe completa émostrada na Listagem 25.

RITA • Volume – • Número – • —- 31

Page 32: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

Listagem 25. Classe que implementa o operador contapixels1 package wvc.operadores.contapixels;2

3 import java.awt.Color;4 import java.awt.image.Raster;5 import java.awt.image.RenderedImage;6

7 import javax.media.jai.StatisticsOpImage;8

9 public class ContaPixelsOpImage extends StatisticsOpImage10 {11 private Color target;12 private Float tolerance;13 private Long count;14

15 public ContaPixelsOpImage(RenderedImage source,Color target,Float tolerance)16 {17 super(source,null,source.getMinX(),source.getMinY(),1,1);18 this.target = target;19 this.tolerance = tolerance;20 count = null;21 }22

23 protected void accumulateStatistics(String name, Raster raster, Object stats)24 {25 if (count == null) count = new Long(0);26 int r,g,b;27 for(int l=0;l<raster.getHeight();l++)28 for(int c=0;c<raster.getWidth();c++)29 {30 int x = raster.getMinX()+c;31 int y = raster.getMinY()+l;32 r = raster.getSample(x,y,0);33 g = raster.getSample(x,y,1);34 b = raster.getSample(x,y,2);35 float dist = (target.getRed()-r)*(target.getRed()-r)+36 (target.getGreen()-g)*(target.getGreen()-g)+37 (target.getBlue()-b)*(target.getBlue()-b);38 if (dist<=tolerance*tolerance) count++;39 }40 }41

42 protected Object createStatistics(String arg0)43 {44 if (count == null) count = new Long(0);45 return count;46 }47

48 protected String[] getStatisticsNames()49 {50 return new String[]{"count"};51 }52

53 public Object getProperty(String name)54 {55 if (count == null) super.getProperty(name);56 return count;57 }58

32 RITA • Volume – • Número – • —-

Page 33: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

59 }

O método principal da classe mostrada na Listagem 25 é o accumulateStatis-tics, que acumula as estatísticas de um determinado atributo para um Raster. Como esteoperador somente calcula o atributo count, o nome do atributo é ignorado pelos métodos daclasse. Outros métodos retornam o nome do atributo sendo calculado (getStatistics-Names) e seu valor (getProperty).

Também precisamos declarar uma classe que criará a pseudo-imagem resultante dooperador (esta pseudo-imagem não terá pixels mas sim propriedades estatísticas calculadas).Esta classe também deve implementar a interface RenderedImageFactory de formasemelhante à mostrada na Listagem 22. Esta classe é mostrada na Listagem 26.

Listagem 26. RenderedImageFactory para o operador contapixels1 package wvc.operadores.contapixels;2

3 import java.awt.Color;4 import java.awt.RenderingHints;5 import java.awt.image.RenderedImage;6 import java.awt.image.renderable.ParameterBlock;7 import java.awt.image.renderable.RenderedImageFactory;8

9 public class ContaPixelsRIF implements RenderedImageFactory10 {11 public RenderedImage create(ParameterBlock paramBlock, RenderingHints hints)12 {13 RenderedImage source = paramBlock.getRenderedSource(0);14 Color target = (Color)paramBlock.getObjectParameter(0);15 Float tolerance = (Float)paramBlock.getObjectParameter(1);16 return new ContaPixelsOpImage(source,target,tolerance);17 }18 }

Finalmente a descrição do operador e seu registro é feita na classe ContaPixels-Descriptor, mostrada na Listagem 27.

Listagem 27. Classe que descreve o operador contapixels1 package wvc.operadores.contapixels;2

3 import java.awt.Color;4

5 import javax.media.jai.JAI;6 import javax.media.jai.OperationDescriptorImpl;7 import javax.media.jai.OperationRegistry;8 import javax.media.jai.registry.RIFRegistry;9

10 public class ContaPixelsDescriptor extends OperationDescriptorImpl11 {12 private static final String opName = "contapixels";

RITA • Volume – • Número – • —- 33

Page 34: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

13 private static final String vendorName = "Hypothetical Image Processing Lab";14 private static final String[][] attributes =15 {16 {"GlobalName", opName},17 {"LocalName", opName},18 {"Vendor", vendorName},19 {"Description", "A simple RGB pixel counting operator"},20 {"DocURL", "http://www.lac.inpe.br/~rafael.santos"},21 {"Version", "1.0"},22 {"arg0Desc", "Target value (RGB color used for similarity)"},23 {"arg1Desc", "Tolerance value"},24 };25 private static final String[] modes = {"rendered"};26 private static final int numSources = 1;27 private static final String[] paramNames = {attributes[6][0],attributes[7][0]};28 private static final Class[] paramClasses = {Color.class,Float.class};29 private static final Object[] paramDefaults = { new Color(0,0,0),new Float(0) };30 private static boolean registered = false;31

32 public ContaPixelsDescriptor()33 {34 super(attributes,modes,numSources,paramNames,paramClasses,paramDefaults,null);35 }36

37 public static void register()38 {39 if (!registered)40 {41 OperationRegistry op = JAI.getDefaultInstance().getOperationRegistry();42 ContaPixelsDescriptor desc = new ContaPixelsDescriptor();43 op.registerDescriptor(desc);44 ContaPixelsRIF rif = new ContaPixelsRIF();45 RIFRegistry.register(op,opName,vendorName,rif);46 registered = true;47 }48 }49

50 }

Podemos demonstrar o uso do operador com uma aplicação simples, que recebe osparâmetros da linha de comando e que registra o operador antes de seu uso. O trecho principaldesta classe é mostrada na Listagem 28.

Listagem 28. Exemplo de uso do operador contapixels11 public static void main(String[] args)12 {13 ContaPixelsDescriptor.register();14 PlanarImage input = JAI.create("fileload", args[0]);15 int r = Integer.parseInt(args[1]);16 int g = Integer.parseInt(args[2]);17 int b = Integer.parseInt(args[3]);18 float t = Float.parseFloat(args[4]);19 Color color = new Color(r,g,b);20 ParameterBlock p = new ParameterBlock();21 p.addSource(input);22 p.add(color);

34 RITA • Volume – • Número – • —-

Page 35: JAI: Java Advanced Imaging - IRIS SERVERiris.sel.eesc.usp.br/wvc2008/Anais/Minicursos/JAI-WVC.pdf · JAI: Java Advanced Imaging módulo de desenvolvimento de novos módulos, e possíveis

JAI: Java Advanced Imaging

23 p.add(t);24 PlanarImage output = JAI.create("contapixels",p);25 Long count = (Long)output.getProperty("count");26 System.out.println("Existem "+count+" pixels com cores semelhantes a "+color);27 }

8 Conclusões e comentários

Neste tutorial vimos como implementar operações básicas de processamento de ima-gens usando as API AWT/Swing e Java Advanced Imaging, com exemplos simples que po-dem ser modificados para resolver problemas semelhantes.

A API JAI ainda provê muitos outros métodos, operadores e facilidades não cobertospelo tutorial. O leitor interessado pode encontrar mais informações na documentação da APIe nos sites [6] e [7].

Referências

[1] Edward R. Dougherty. An Introduction to Morphological Image Processing. SPIE Opti-cal Engineering Press, 1992.

[2] GeoSage. Image fusion and pan-sharpening: the big picture, 2008.http://www.geosage.com/highview/imagefusion.html. Verificado em 30 de Julho de2008.

[3] Rafael C. Gonzalez and Paul Wintz. Digital Image Processing. Addison-Wesley, 2ndedition, 1987.

[4] Martin D. Levine. Vision in Man and Machine. McGraw-Hill, 1985.

[5] L. H. Rodrigues. Building Imaging Applications with Java Technology. Addison-Wesley,2001.

[6] Rafael Santos. Java Advanced Imaging Stuff (repositório de exemplos on-line), 2008.https://jaistuff.dev.java.net. Verificado em 30 de Julho de 2008.

[7] Rafael Santos. Java Image Processing Cookbook (livro on-line), 2008.http://www.lac.inpe.br/∼rafael.santos/JIPCookbook/index.jsp. Verificado em 30 deJulho de 2008.

RITA • Volume – • Número – • —- 35