Upload
stkim
View
133
Download
3
Embed Size (px)
Citation preview
Digital Image Processing (using Java)
(p.22)
java ResolutionSimulator mathead.png
low spatial frequency = coarsely-sampling
Nyquist criterion: sampling freq ≧ 2 * (highest spatial freq.)
Effect of aliasing – do not meet Nyquist criterion
e.g. Fig3.2 (a) & (b) Fig3.4 (b)
- 1 -
(p.24)
anti-aliasing → a process that meets the Nyquist Criterion
(p.26)
- 2 -
java LogPolar matthead.png 256 256
java QuantisationSimulator matthead.png
(p.29)
- 3 -
(p.30)
- 4 -
(p.31)
HSICalc.java: RGB → HIS
(0~255) (0~1)
LISTING 3.1 A program to convert RGB colours into values of hue, saturation and
intensity. A conversion method from Java’s Color class is used.
import java.awt.Color;
import java.text.DecimalFormat;
public class HSICalc {
public static void main(String[] argv) {
if (argv.length > 2) {
int[] rgb = new int[3];
for (int i = 0; i < 3; ++i)
rgb[i] = Integer.parseInt(argv[i]);
float[] values = Color.RGBtoHSB(rgb[0], rgb[1], rgb[2], null);
String[] labels = { "H=", "S=", "I=" };
DecimalFormat floatValue = new DecimalFormat("0.000");
for (int i = 0; i < 3; ++i)
System.out.println(labels[i] + floatValue.format(values[i]));
}
else {
System.err.println("usage: java HSICalc <r> <g> <b>");
System.exit(1);
}
}
}
- 5 -
(p.34)
(p.36)
java Limits
LISTING 3.2 Program to print limits for the primitive types. The limits are defined as
static constants in the standard Java ‘wrapper’ classes Byte, Short, Integer, Float
and Double.
public class Limits {
public static void main(String[] argv) {
java.io.PrintStream s = System.out;
s.println("Min byte value = " + Byte.MIN_VALUE);
s.println("Max byte value = " + Byte.MAX_VALUE);
s.println("Min short value = " + Short.MIN_VALUE);
s.println("Max short value = " + Short.MAX_VALUE);
s.println("Min int value = " + Integer.MIN_VALUE);
s.println("Max int value = " + Integer.MAX_VALUE);
s.println("Min float value = " + Float.MIN_VALUE);
s.println("Max float value = " + Float.MAX_VALUE);
s.println("Min double value = " + Double.MIN_VALUE);
s.println("Max double value = " + Double.MAX_VALUE);
}
}
- 6 -
(p.37)
(p.37)
- 7 -
(p.38)
(p.39)
- 8 -
(p.40)
- 9 -
(p.41)
(p.42)
LISTING 3.3 Example of polymorphism.
public class Mean {
public static void print1 (ByteImage image){
System.out.println (“mean value is “ + image.getMeanValue());
}
public static void print2 (BaseImage image){
System.out.println (“mean value is “ + image.getMeanValue());
}
public static void print3 (BaseArray array){
System.out.println (“mean value is “ + array.getMeanValue());
}
}
- 10 -
(p.79)
usage: java convert infile outfile
LISTING 5.6 A simple image format conversion program.
public class Convert {
public static void main(String[] argv) {
if (argv.length > 1) {
try {
ImageDecoder input = ImageFile.createImageDecoder(argv[0]);
BufferedImage image = input.decodeAsBufferedImage();
ImageEncoder output = ImageFile.createImageEncoder(argv[1]);
output.encode(image);
System.exit(0);
}
catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
else {
System.err.println("usage: java Convert <infile> <outfile>");
System.exit(1);
}
}
}
(p.85)
usage: java Display imagefile
LISTING 5.9 A simple image display application using Swing components.
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import com.pearsoneduc.ip.io.*;
public class Display extends JFrame{
public Display (String filename){
super (filename);
ImageDecoder input = ImageFile.createImageDecoder (filename);
BufferedImage image = input.decodeAsBufferedImage();
- 11 -
Jlabel view = new Jlabel (new ImageIcon (image));
getContentPane().add(view);
addWindowListener (new WindowEvent event){
public void windowClosing (WindowEvent event){
System.exit(0);
}
});
}
public static void main (String[] argv){
if (argv.length > 0){
try {
JFrame frame = new Display (argv[0]);
frame.pack();
frame.setVisible (true);
}
catch (Exception e){
System.err.println (e);
System.exit (1);
}
}
else {
System.err.println (“usage: java Display imagefile”);
System.exit (1);
}
}
}
(p.86)
usage: java ImageViewer matthead.png
LISTING 5.10 A Swing component to display images.
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
public class ImageView extends JLabel implements Scrollable{
private BufferedImage image; //image to be displayed
private Dimension viewSize; //size of view, if in a JScrollPane
- 12 -
public ImageView (BufferedImage img){
image = img;
int width = Math.min (256, image.getWidth());
int height = Math.min (256, image.getHeight());
viewSize = new Dimension (width, height);
setPreferredSize (new Dimension (image.getWidth(), image.getHeight()));
}
public void paintComponent (Graphics g){
g.drawImage (image, 0, 0, this);
}
public void setViewSize (Dimension newSize){
viewSize.setSize (newSize);
}
public Dimension getPreferredScrollableViewportSize(){
return viewSize;
}
public int getScrollableUnitIncrement (Rectangle rect, int orient, int dir){
return 1;
}
public int getScrollableBlockIncrement (Rectangle rect, int orient, int dir){
if (orient == SwingConstants.HORIZONTAL)
return image.getWidth() / 10;
else
return image.getHeight() / 10;
}
public boolean getScrollableTracksViewportWidth(){
return false;
}
public boolean getScrollableTracksViewportHeight(){
return false;
}
}
- 13 -
(p.88)
halftoning: gray → binary
1. patterning-replacing each pixel by a pattern taken from a ‘binary font’.
(e.g.: fig 5.10)
2. Dithering:
Java Dither matthead.png
3. Error diffusion:
Java ErrorDiffustion matthead.png
- 14 -
(p.92)
ALGORITHM 5.1 Dithering BY Floyd-Steinberg error diffusion.
threshold = (black + white) /2
for all x and y do
if ƒ (x, y) < threshold then
g (x, y) = black
ε = ƒ (x, y) – black
else
g (x, y) = white
ε = ƒ (x, y) – white
end if
ƒ (x+1, y) = ƒ (x+1,y) + 7ε/16
ƒ (x-1, y+1) = ƒ (x-1, y+1) + 3ε/16
ƒ (x, y+1) = ƒ (x,y+1) + 5ε/16
ƒ (x+1, y+1) = ƒ (x+1, y+1) + ε/16
end for
(p.93)
LISTING 5.12 Java implementation of Algorithm 5.1
public static BufferedImage errorDiffusion (BufferedImage image) {
// Create a binary output image (0=black, 1=white)
int w = image.getWidth()-1;
int h = image.getHeight()-1;
outputImage = new BufferedImage(w, h, bufferedImage.TYPE_BYTE_BINARY);
- 15 -
// Copy input image because error diffusion modifies it
WritableRaster input = image.copyData(null);
WritableRaster output = outputImage.getRaster();
final int threshold = 128;
int value, error;
for (int y = 1; y < h; ++y)
for (int x = 1; x < w; ++x) {
// Threshold value and compute error
value = input.getSample(x, y, 0);
if (value < threshold) {
output.setSample(x, y, 0, 0); //set to black
error = value;
}
else {
output.setSample(x, y, 0, 1); //set to white
error = value - 255;
}
// Disperse error to pixels that are ahead of us
value = input.getSample(x+1, y, 0);
input.setSample(x+1, y, 0, clamp(value + 0.4375f * error));
value = input.getSample(x-1, y+1, 0);
input.setSample(x-1, y+1, 0, clamp(value + 0.1875f * error));
value = input.getSample(x, y+1, 0);
input.setSample(x, y+1, 0, clamp(value + 0.3125f * error));
value = input.getSample(x+1, y+1, 0);
input.setSample(x+1, y+1, 0, clamp(value + 0.0625f * error));
}
return outputImage;
}
// Rounds a float to the nearest int between 0 and 255
- 16 -
public static int clamp(float value) {
return Math.min(Math.max(Math.round(value), 0), 255);
}
ROI (region of interest)
Java MeanROI matthead.png
LISTING 5.13 Example of using a ROI in the calculation of mean grey level.
public double meanValue(BufferedImage img) {
Raster raster = img.getRaster();
double sum = 0.0;
for (int y = 0; y < img.getHeight(); ++y)
for (int x = 0; x < img.getWidth(); ++x)
sum += raster.getSample(x, y, 0);
return sum / (img.getWidth()*img.getHeight());
}
public double meanValue(BufferedImage img, Rectangle region) {
return meanValue(img.getSubimage(region.x, region.y,
region.width, region.height));
}
(p.97)
LISTING 5.14 Java code to enlarge an image by pixel replication.
public static BufferedImage enlarge (BufferedImage image, int n ){
int w = n*image.getWidth();
int h = n*image.getHeight();
BufferedImage enlargedImage = new BufferedImage(w, h, image.getType());
for (int y = 0; y < h; ++y)
for (int x=0; x < w; ++x)
enlargedImage.setRGB(x, y, image.getRGB(x/n, y/n));
return enlargedImage;
}
LISTING 5.15 Java code to shrink an image by skipping pixels.
- 17 -
public static BufferedImage shrink (BufferedImage image, int n ){
int w = image.getWidth() / n;
int h = image.getHeight() / n;
BufferedImage shrunkdImage = new BufferedImage(w, h, image.getType());
for (int y = 0; y<h; ++y)
for (int x=0; x<w; ++x)
shrunkImage.setRGB(x, y, image.getRGB(x*n, y*n));
return shrunkImage;
}
(p.98)
LISTING 5.16 Java code for in-place horizontal reflection of an image.
public static void xReflectionInPlace (BufferedImage image){
int w = image.getWidth();
int xmax = w/2;
for (int y = 0; y < image.getHeight(); ++y)
for (int x=0; x < xmax; ++x){
// swap value at (x,y) with its mirror image
int value = image.getRGB(x, y);
image.setRGB(x, y, image.getRGB(w-x-1, y));
image.setRGB(w-x-1, y, value);
}
}
(p.100)
- 18 -
LISTING 5.17 Java code to average a set of images.
public static BufferedImage average (BufferedImage[] imgArray ){
int n = imgArray.length;
int w = imgArray[0].getWidth(); //assume that they all have
int h = imgArray[0].getHeight(); //the same dimensions
BufferedImage average =
new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
for (int y = 0; y < h; ++y)
for (int x=0; x < w; ++x)
float sum = 0.0f;
for (int I = 0; I < n; ++i)
sum += imgArray[i].getRaster().getSample(x, y, 0);
ter.setSample (x, y, Math.round (sum/n)); ras
}
return average;
}
(p.102)
xercise 5.7
. Write a Java program that will
elect a region of interest (ROI)
file
.111)
E
1
(a) Display an image
(b) Allow the user to s
(c) Extract this ROI from the image
(d) Write the ROI to a user-specified
(p
- 19 -
(p.112)
IntervalTimer timer = new IntervalTimer();
Timer.start();
//some code to be timed
System.out.println(timer.elapsed()); //doesn’t stop the clock
//more code to be timed
System.out.println(timer.stop()); //stops clock
MapTest1.java
import java.util.Random;
public class MapTest1 {
public static void randomFill(short[] array) {
Random random = new Random();
for (int i = 0; i < array.length; ++i)
array[i] = (short) random.nextInt(256);
}
public static void main(String[] argv) {
int n = 512;
if (argv.length > 0)
n = Integer.parseInt(argv[0]);
// Create image and fill it with random values
- 20 -
int numPixels = n*n;
short[] image = new short[numPixels];
randomFill(image);
// Perform the mapping directly
IntervalTimer timer = new IntervalTimer();
timer.start();
for (int i = 0; i < numPixels; ++i)
image[i] = (short) Math.round(Math.sqrt(image[i]));
System.out.println("direct calculation: " + timer.stop() + " sec");
// Perform the mapping with a lookup table
randomFill(image);
timer.start();
short[] table = new short[256];
for (int i = 0; i < 256; ++i)
table[i] = (short) Math.round(Math.sqrt(i));
for (int i = 0; i < numPixels; ++i)
image[i] = table[image[i]];
System.out.println("lookup table: " + timer.stop() + " sec");
System.exit(0);
}
}
Java MapTest1
- 21 -
(p.113)
- 22 -
public LookupOp(LookupTable table, RenderingHints hints)
ex: byte[] table = new byte[256];
for (int I = 0; I < 256; ++i)
table[i] = (byte) (255-i);
ByteLookupTable invertTable = new ByteLookupTable(0, table);
LookupOp invertOp = new LookupOp(invertTable, null);
BufferedImage invertedImage = invertOp.filter(image, null);
(p.116)
LISTING 6.3 A Java class to perform mapping of grey levels in an image.
package com.pearsoneduc.ip.op;
import java.awt.image.*;
public abstract class GreyMapOp implements BufferedImageOp {
protected byte[] table = new byte[256];
public int getTableEntry(int i) {
if (table[i] < 0)
return 256 + (int) table[i];
else
return (int) table[i];
}
protected void setTableEntry(int i, int value) {
if (value < 0)
table[i] = (byte) 0;
else if (value > 255)
table[i] = (byte) 255;
else
table[i] = (byte) value;
}
public void computeMapping() {
computeMapping(0, 255);
}
- 23 -
public abstract void computeMapping(int low, int high);
public BufferedImage filter(BufferedImage src, BufferedImage dest) {
checkImage(src);
if (dest == null)
dest = createCompatibleDestImage(src, null);
LookupOp operation = new LookupOp(new ByteLookupTable(0, table), null);
operation.filter(src, dest);
return dest;
}
}
(p.117)
LISTING 6.4 A subclass of GreyMapOp that applies a linear grey level mapping
function to an image.
public class LinearOp extends GreyMapOp {
public LinearOp() {
computeMapping();
}
public LinearOp(int low, int high) {
computeMapping(low, high);
}
public void computeMapping(int low, int high) {
if (low < 0 || high > 255 || low >= high)
throw new java.awt.image.ImagingOpException("invalid mapping limits");
float scaling = 255.0f / (high - low);
for (int i = 0; i < 256; ++i)
setTableEntry(i, Math.round(scaling*(i - low)));
}
}
- 24 -
GreyMap.java
java GreyMap in.jpg out.jpg linear
java GreyMap in. jpg out.jpg log 10 220
//logarithmic mapping of [10,220] onto [0,255]
// lin: linear
inv: inverted linear
sq: square-root
exp: exponential
java GreyMapTool matthead.png
Grey MapTool.java
import java.awt.FlowLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.*;
import javax.swing.*;
public class GreyMapTool extends JFrame {
public GreyMapTool(String filename)
throws IOException, ImageDecoderException, HistogramException {
// Load image from file and create display component
super("GreyMapTool: " + filename);
ImageDecoder input = ImageFile.createImageDecoder(filename);
BufferedImage image = input.decodeAsBufferedImage();
LinearOp op = new LinearOp();
ImageView imageView = new ImageView(image, op);
// Create and store a set of grey level mapping operations
Hashtable ops = new Hashtable();
ops.put("linear", op);
ops.put("square-root", new SquareRootOp());
ops.put("logarithmic", new LogOp());
ops.put("exponential", new ExpOp());
ops.put("inverted", new InvertOp());
ops.put("thresholded", new ThresholdOp(128));
ops.put("equalised", new EqualiseOp(new Histogram(image)));
// Create labels for the operations
- 25 -
Vector names = new Vector();
names.addElement("linear");
names.addElement("square-root");
names.addElement("logarithmic");
names.addElement("exponential");
names.addElement("inverted");
names.addElement("thresholded");
names.addElement("equalised");
// Add a control panel and a scrolling image display to the frame
JPanel pane = new JPanel();
pane.setLayout(new FlowLayout());
pane.add(new GreyMapPanel(imageView, ops, names));
pane.add(new JScrollPane(imageView));
setContentPane(pane);
addWindowListener(new WindowMonitor());
}
public static void main(String[] argv) {
if (argv.length > 0) {
try {
JFrame frame = new GreyMapTool(argv[0]);
frame.pack();
frame.setVisible(true);
}
catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
else {
System.err.println("usage: java GreyMapTool <imagefile>");
System.exit(1);
}
}
}
- 26 -
(p.119)
ALGORITHM 6.3 Calculation of an image histogram.
Create an array histogram with 2b elements
for all grey levels, i , do
histogram[i ] = 0;
end for
for all pixel coordinates, x and y, do
Increment histogram[ƒ (x,y)] by 1
end for
(p.123)
- 27 -
(p.124)
HistogramTool.java
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.swing.*;
import javax.swing.border.*;
public class HistogramTool extends JFrame implements ActionListener {
private Histogram histogram; // histogram data to be displayed
private HistogramView[] view; // plot of the histogram
private HistogramInfoPane infoPane; // displays value and frequency
private JPanel mainPane; // contains histogram and info panel
private JMenu menu; // input/output menu
private JFileChooser fileChooser = // handles selection of files
new JFileChooser(System.getProperty("user.dir"));
public HistogramTool(Histogram theHistogram, String description) {
super(description); // labels the frame
// Create components to display histogram and information
histogram = theHistogram;
infoPane = new HistogramInfoPane(histogram);
mainPane = new JPanel(new BorderLayout());
if (histogram.getNumBands() == 3)
createMultipleViews(); // three views (R, G, B) in a tabbed pane
else
createSingleView();
mainPane.add(infoPane, BorderLayout.SOUTH);
setContentPane(mainPane);
// Add a menu bar to support image input and histogram output
JMenuBar menuBar = new JMenuBar();
menuBar.setBorder(new BevelBorder(BevelBorder.RAISED));
createFileMenu();
- 28 -
menuBar.add(menu);
setJMenuBar(menuBar);
addWindowListener(new WindowMonitor());
}
// Creates a single HistogramView object to display a
// greyscale histogram and adds it to the GUI
public void createSingleView() {
view = new HistogramView[1];
view[0] = new HistogramView(histogram, infoPane);
mainPane.add(view[0], BorderLayout.CENTER);
}
// Creates three HistogramView objects for the red, green
// and blue bands of a colour histogram, places these in a
// tabbed pane and adds the tabbed pane to the GUI
public void createMultipleViews() {
view = new HistogramView[3];
Color[] bandColor = { Color.red, Color.green, Color.blue };
String[] tabLabel = { "Red", "Green", "Blue" };
JTabbedPane views = new JTabbedPane(JTabbedPane.BOTTOM);
for (int i = 0; i < 3; ++i) {
view[i] = new HistogramView(histogram, i, infoPane);
view[i].setColor(bandColor[i]);
views.add(tabLabel[i], view[i]);
}
mainPane.add(views, BorderLayout.CENTER);
}
// Creates a menu to support image input, histogram output
// and termination of the application
public void createFileMenu() {
menu = new JMenu("File");
menu.setMnemonic('F');
String[] itemName = { "Load image", "Save histogram", "Exit" };
- 29 -
char[] shortcut = { 'L', 'S', 'X' };
for (int i = 0; i < 3; ++i) {
JMenuItem item = new JMenuItem(itemName[i], shortcut[i]);
item.addActionListener(this);
menu.add(item);
}
}
// Handles Action events triggered by menu selections
public void actionPerformed(ActionEvent event) {
String command = event.getActionCommand();
if (command.startsWith("Load")) {
loadImage();
repaint();
}
else if (command.startsWith("Save")) {
saveHistogram();
repaint();
}
else if (command.equals("Exit")) {
setVisible(false);
dispose();
System.exit(0);
}
}
// Loads a new image, computes its histogram and updates the GUI
public void loadImage() {
fileChooser.setDialogTitle("Load image");
if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
// Load image and compute its histogram
try {
File file = fileChooser.getSelectedFile();
ImageDecoder input =
ImageFile.createImageDecoder(file.getAbsolutePath());
- 30 -
BufferedImage image = input.decodeAsBufferedImage();
histogram.computeHistogram(image);
setTitle(file.getName());
}
catch (FileNotFoundException e) {
error("File not found.");
return;
}
catch (ImageDecoderException e) {
error("Cannot read this image format.");
return;
}
catch (IOException e) {
error("Failed to read image data.");
return;
}
catch (HistogramException e) {
error("Cannot compute histogram for this image type.");
return;
}
// Rebuild GUI
mainPane.removeAll();
if (histogram.getNumBands() == 3)
createMultipleViews();
else
createSingleView();
mainPane.add(infoPane, BorderLayout.SOUTH);
mainPane.invalidate();
validate();
pack();
}
}
// Saves current histogram to a file selected by user
public void saveHistogram() {
if (histogram.getNumBands() == 0) {
- 31 -
error("No histogram data to save!");
return;
}
else {
fileChooser.setDialogTitle("Save histogram");
if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
try {
File file = fileChooser.getSelectedFile();
if (file.exists()) {
int response = JOptionPane.showConfirmDialog(this,
"File will be overwritten! Are you sure?", "File exists",
JOptionPane.OK_CANCEL_OPTION);
if (response != JOptionPane.OK_OPTION)
return;
}
histogram.write(new FileWriter(file));
fileChooser.rescanCurrentDirectory();
}
catch (IOException e) {
error("Cannot open output file.");
}
}
}
}
// Displays an error message in a dialog box
public void error(String message) {
JOptionPane.showMessageDialog(this, message, "Error",
JOptionPane.ERROR_MESSAGE);
}
public static void main(String[] argv) {
if (argv.length > 0) {
try {
ImageDecoder input = ImageFile.createImageDecoder(argv[0]);
BufferedImage image = input.decodeAsBufferedImage();
Histogram hist = new Histogram(image);
HistogramTool histTool = new HistogramTool(hist, argv[0]);
- 32 -
histTool.pack();
histTool.setVisible(true);
}
catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
else {
Histogram hist = new Histogram();
HistogramTool histTool = new HistogramTool(hist, "HistogramTool");
histTool.pack();
histTool.setVisible(true);
}
}
}
CalcHist.java
import java.awt.image.BufferedImage;
import java.io.FileWriter;
public class CalcHist {
public static void main(String[] argv) {
if (argv.length > 1) {
try {
ImageDecoder input = ImageFile.createImageDecoder(argv[0]);
BufferedImage image = input.decodeAsBufferedImage();
Histogram histogram = new Histogram(image);
FileWriter histFile = new FileWriter(argv[1]);
histogram.write(histFile);
if (argv.length > 2) {
FileWriter cumHistFile = new FileWriter(argv[2]);
histogram.writeCumulative(cumHistFile);
}
System.exit(0);
}
catch (Exception e) {
System.err.println(e);
- 33 -
System.exit(1);
}
}
else {
System.err.println(
"usage: java CalcHist <imageFile> <histFile> [<cumHistFile>]");
System.exit(1);
}
}
}
Java HistogramTool
(p.125)
- 34 -
(p.126)
(p.128)
LISTING 6.5 A subclass of GreyMapOp that performs histogram equalisation.
pckage com.pearsoneduc.ip.op;
public class EqualiseOp extends GreyMapOp {
/**
* Constructs an EqualiseOp object using histogram data.
* @param hist Histogram of the image to be equalised
* @exception HistogramException if the histogram is from a colour image.
*/
public EqualiseOp(Histogram hist) throws HistogramException {
float scale = 255.0f / hist.getNumSamples();
for (int i = 0; i < 256; ++i)
table[i] = (byte) Math.round(scale*hist.getCumulativeFrequency(i));
}
public void computeMapping(int low, int high) {
// Does nothing - limits are meaningless in histogram equalisation
}
}
- 35 -
Histogram hist = new Histogram(image);
EqualiseOp eq = new EqualiseOp(hist);
BufferedImage eq_img = eq.filter(image, null);
(p.130)
(p.135)
- 36 -
(p.136)
- 37 -
(p.138)
- 38 -
(p.142)
- 39 -
(p.144)
(p.145)
(p.146)
Listing 7.1: WriteKernel.java
LISTING 7.1 A simple program demonstrating how a convolution kernel can be
created and written to an output stream.
import java.io.OutputStreamWriter;
import com.pearsoneduc.ip.op.StandardKernel;
public class WriteKernel{
- 40 -
public static void main(String[] argv){
float[] data = new float[25];
float coeff = 0.04f;
for (int i = 0; I < 25; ++i)
data[i] = coeff;
StandardKernel kernel = new StandardKernel (5, 5, data, 2);
Kernel.write(new OutputStreamWriter(System.out));
}
}
(p.147)
Reader input = new FileReader(“test.ker”);
Kernel kernel = StandardKernel.createJerbek(input);
Listing 7.2: Convolve.java
usage: java ConvolutionTool matthead.png
(p.152)
LISTING 7.2 A convolution application with a command line interface.
import java.awt.image.*;
import java.io.*;
import com.pearsoneduc.ip.io.*;
import com.pearsoneduc.ip.op.*;
import com.pearsoneduc.ip.util.IntervalTimer;
public class Convolve {
public static void main(String[] argv) {
if (argv.length > 5) {
try {
// Parse command line arguments
ImageDecoder input = ImageFile.createImageDecoder(argv[0]);
ImageEncoder output = ImageFile.createImageEncoder(argv[1]);
Reader kernelInput = new FileReader(argv[2]);
boolean normaliseKernel = (Integer.parseInt(argv[3]) != 0);
int borderStrategy =
- 41 -
Math.max(1, Math.min(4, Integer.parseInt(argv[4])));
int rescaleStrategy =
Math.max(1, Math.min(3, Integer.parseInt(argv[5])));
// Load image and kernel
BufferedImage inputImage = input.decodeAsBufferedImage();
Kernel kernel =
StandardKernel.createKernel(kernelInput, normaliseKernel);
// Create convolution operator and convolve image
ConvolutionOp convOp = new ConvolutionOp(kernel,
borderStrategy, ConvolutionOp.SINGLE_PASS, rescaleStrategy);
IntervalTimer timer = new IntervalTimer();
timer.start();
BufferedImage outputImage = convOp.filter(inputImage, null);
System.out.println("Convolution finished [" + timer.stop() + " sec]");
// Write results to output file
output.encode(outputImage);
System.exit(0);
}
catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
else {
System.err.println("usage: java Convolve " +
"<infile> <outfile> <kernel> <norm> <border> <rescale>");
System.exit(1);
}
}
}
- 42 -
(p.153)
(p.155)
low pass filtering: convolution kernel >0
e.g.: mean filter (Fig.7.12、Fig.7.13), Gaussian filter ((7.13)、Fig.7.14)
(p.156)
- 43 -
(p.157)
- 44 -
(p.158)
High pass filtering: kernel has +, - coefficients.
(p.159)
high boost filtering:
if c=8 => high pass filter
if c is more close to 8 => sharper
(p.160)
Listing 7.3: High PassKernel.java
Usage:java HighPassKernel > highpass.ker
LISTING 7.3 A kernel class that performs high pass filtering.
public class HighPassKernel extends StandardKernel {
private static final float[] data = { -1.0f, -1.0f, -1.0f,
-1.0f, 8.0f, -1.0f,
-1.0f, -1.0f, -1.0f };
public HighPassKernel() {
super(3, 3, data, 0);
- 45 -
}
public static void main(String[] argv) {
StandardKernel kernel = new HighPassKernel();
kernel.write(new java.io.OutputStreamWriter(System.out));
}
}
(p.161)
java GaussianKernel: generate for σ=1.0 Gussian filter
BufferedImage outputImage = ConvolutionOp.gaussianBlur(inputImage, 3.0f);
↖σ
(p.162)
LISTING 7.4 A kernel class to support Gaussian low pass filtering
public class GaussianKernel extends StandardKernel {
public GaussianKernel() {
this(1.0f);
}
public GaussianKernel(float sigma) {
super(getSize(sigma), getSize(sigma), createKernelData(sigma));
}
public static int getSize(float sigma) {
int radius = (int) Math.ceil(4.0f*sigma);
return 2*radius+1;
}
public static float[] createKernelData(float sigma) {
int n = (int) Math.ceil(4.0f*sigma);
int size = 2*n+1;
float[] data = new float[size*size];
double r, s = 2.0*sigma*sigma;
float norm = 0.0f;
- 46 -
int i = 0;
for (int y = -n; y <= n; ++y)
for (int x = -n; x <= n; ++x, ++i) {
r = Math.sqrt(x*x + y*y);
data[i] = (float) Math.exp(-r*r/s);
norm += data[i];
}
for (i = 0; i < size*size; ++i)
data[i] /= norm;
return data;
}
public static void main(String[] argv) {
float sigma = 1.0f;
if (argv.length > 0)
sigma = Float.valueOf(argv[0]).floatValue();
StandardKernel kernel = new GaussianKernel(sigma);
kernel.write(new java.io.OutputStreamWriter(System.out));
}
}
- 47 -
(p.163)
LISTING 7.5 A mean filtering application.
import java.awt.image.*;
import com.pearsoneduc.ip.io.*;
import com.pearsoneduc.ip.op.MeanKernel;
import com.pearsoneduc.ip.util.IntervalTimer;
public class MeanFilter {
public static void main(String[] argv) {
if (argv.length > 3) {
try {
ImageDecoder input = ImageFile.createImageDecoder(argv[0]);
ImageEncoder output = ImageFile.createImageEncoder(argv[1]);
int w = Integer.parseInt(argv[2]);
int h = Integer.parseInt(argv[3]);
BufferedImage inputImage = input.decodeAsBufferedImage();
Kernel kernel = new MeanKernel(w, h);
ConvolveOp blurOp = new ConvolveOp(kernel);
IntervalTimer timer = new IntervalTimer();
timer.start();
BufferedImage outputImage = blurOp.filter(inputImage, null);
System.out.println("Mean filtering finished [" +
timer.stop() + " sec]");
output.encode(outputImage);
System.exit(0);
}
catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
else {
System.err.println("usage: java MeanFilter <infile> <outfile> <w> <h>");
System.exit(1);
}
}
}
- 48 -
w 3 h←3 1←σ
←
java MeanFilter matthead.png matt-mean.png
java GaussianBlur matthead.png matt-gaus.png
(p.164)
Edge detection:
gx(x,y) = hx * ƒ (x,y),
gy(x,y) = hy * ƒ (x,y),
Prewitt kernel:
Sobel kernel:
(p.165)
gradient vector
gradient magnitude: ≒
gradient direction:
- 49 -
(p.166)
- 50 -
(p.169)
Laplacian
- 51 -
LOG (Laplacian of Gaussian) filter
(p.171)
DOG (Difference of Gaussian) filter
(p.172)
algorithm 7.7 -Canny edge dector
(p.175)
BufferedImageOp op = new CannyEdgeOp(2.0f, 50, 100);
BufferedImage edgeMap = op.filter (image, null);
BufferedImage mapImage = op.getGradientOrientationImage();
BufferedImage orientImage = op.getGradientOrientationImage();
- 52 -
(p.176)
mean filter:
median filter: sort the 9 elements. Find the median value. (for impulsive noise)
java MedianTest matthead.png 3 3
java MinTest matthead.png 3 3
java RankFilterTool matthead.png
- 53 -
(p.184)
σ-trimmed mean filter: f1≦ f2≦…≦ fn2
σ: the number of values removed from each end of the list.
(p.186)
MMSE filter:
java MMSEFilter matthead.png matt-mmse.png
(p.189)
- 54 -
(P.190)
- 55 -
(p.194)
(p.231)
affine transformation
(p.232)
- 56 -
import java.awt.geom.AffineTransform
AffineTransform transform = new AffineTransform();
AffineTransform scale = AffineTransform.getScaleInstance(1.5, 1.5);
// increase image size by 50%
double angle = Math.Pi/4.0;
double x = image.getWidth()/2.0;
double y = image.getHeight()/2.0;
AffineTransform rotate = AffineTransform.getRotateInstance(angel, x, y);
(p.233)
(p.234)
(p.235)
- 57 -
AffineTransform t = new AffineTransform();
t.setToTranslation(10,20);
t.rotate(30.0*Math.PI/180.0);
AffineTransform translate = AffineTransform.getTranslateInstance(5. 0);
t.concatenate(translate);
AffineTransform scale = AffineTransform.getScaleInstance(0.3, 0.3);
t.preConcatenate(scale);
(p.236)
(p.237)
- 58 -
(p.238)
LISTING 9.2 Java implementation of Algorithm 9.1
public static BufferedImage rotate(BufferedImage input, double angle){
int width = input.getWidth();
int height = input.getHeight();
BufferedImage output = new BufferedImage(width, height, input.getType());
double a0 = Math.cos(angle*Math.PI/180.0);
double b0 = Math.sin(angle*Math.PI/180.0);
double a1 = -b0, b1 = a0;
int rx, ry;
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x){
rx = (int) Math.round(a0*x + a1*y);
ry = (int) Math.round(b0*x + b1*y);
if (rx >= 0 && rx < width && ry >=0 && ry < height)
output.setRGB(rx, ry, input.getRGB(x,y));
}
return output;
}
(p.239)
- 59 -
(p.240)
- 60 -
(p.241)
(p.243)
- 61 -
(p.244)
LISTING 9.3 A method to calculate the bounding box of a transformed image.
public static Rectangle2D getBoundingBox(BufferedImage image, AffineTransform
transformation){
// Apply transformation to image corners
int xmax = image.getWidth()-1;
int ymax = image.getHeight()-1;
Point2D[] corners = new Point2D.Double[4];
Corners[0] = new Point2D.Double(, );
}
java Rotate1 input.png output.png 0 62.5
java Rotate2 input.png output.png ↑interp. ↑angle
java AffineTransformTool matthead.png
(p.247)
- 62 -
(p.248)
(p.255)
- 63 -
(p.258)
LISTING 10.1 A Java class to perform grey level thresholding.
public class ThresholdOp extends GreyMapOp {
public ThresholdOp(int threshold) {
computeMapping(threshold, 255);
}
public ThresholdOp(int low, int high) {
computeMapping(low, high);
}
public void setThreshold(int threshold) {
computeMapping(threshold, 255);
}
public void setThresholds(int low, int high) {
computeMapping(low, high);
}
public void computeMapping(int low, int high) {
if (low < 0 || high > 255 || low >= high)
throw new java.awt.image.ImagingOpException("invalid thresholds");
int i;
for (i = 0; i < low; ++i)
table[i] = (byte) 0;
for (; i <= high; ++i)
table[i] = (byte) 255;
for (; i < 256; ++i)
table[i] = (byte) 0;
}
}
(p.259)
java Threshold grey.jpg thresholded.pbm 175
java Threshold grey.jpg thresholded.pbm 92 140
java Threshold color.jpg thresholded.pbm 200 75 128
- 64 -
(p.260)
(p.261)
LISTING 10.2 Java code to perform connected region labelling, taken from the
RegionLabelOp class.
public BufferedImage filter(BufferedImage src, BufferedImage dest) {
checkImage(src);
if (dest == null)
dest = createCompatibleDestImage(src, null);
width = src.getWidth();
height = src.getHeight();
WritableRaster in = src.copyData(null);
WritableRaster out = dest.getRaster();
int n = 1;
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x)
if (in.getSample(x, y, 0) > 0) {
label(in, out, x, y, n);
++n;
if (n > MAX_REGIONS)
return dest;
}
return dest;
}
private void label(WritableRaster in, WritableRaster out,
- 65 -
int x, int y, int n) {
in.setSample(x, y, 0, 0);
out.setSample(x, y, 0, n);
int j, k;
for (int i = 0; i < connectivity; ++i) {
j = x + delta[i].x;
k = y + delta[i].y;
if (inImage(j, k) && in.getSample(j, k, 0) > 0)
label(in, out, j, k, n);
}
}
private final boolean inImage(int x, int y) {
return x >= 0 && x < width && y >= 0 && y < height;
}
}
(p.263)
- 66 -
java RegionGrowingTool matthead.png ↗4-connet
java RegionGrow test.jpg region.pbm 100 100 4 35
( x , y ) ↘threshold
(p.273)
- 67 -
(p.277)
Erosion: ƒΘ s (ƒ→ image; s→ stnecturing element)
(p.278)
boundary: ƒ – (ƒΘ s) = ƒ – erosion(ƒ)
- 68 -
Dilation: ƒ ♁ s
(p.282)
LISTING 11.1 BinaryErodeOp’s filter() method.
public BufferedImage filter(BufferedImage src, BufferedImage dest) {
checkImage(src);
if (dest == null)
dest = createCompatibleDestImage(src, null);
int w = src.getWidth();
int h = src.getHeight();
Raster srcRaster = src.getRaster();
WritableRaster destRaster = dest.getRaster();
// Determine range of pixels for which operation can be performed
Point origin = structElement.getOrigin(null);
int xmin = Math.max(origin.x, 0);
int ymin = Math.max(origin.y, 0);
int xmax = origin.x + w - structElement.getWidth();
- 69 -
int ymax = origin.y + h - structElement.getHeight();
xmax = Math.min(w-1, xmax);
ymax = Math.min(h-1, ymax);
// Fit structuring element into source image
for (int y = ymin; y <= ymax; ++y)
for (int x = xmin; x <= xmax; ++x)
if (structElement.fits(srcRaster, x, y))
destRaster.setSample(x, y, 0, nonZeroValue);
return dest;
}
BinaryStructElement structElement = new
BinaryStructElement(StructElementType.DIAMOND_5x5);
BufferedImageOp erosion = new BinaryErodeOp(structElement);
# binary structuring element
# width = 5
# height = 5
# xorigin = 2
# yorigin = 2
00|00
00|00
11|11
00|00
00|00
java BinaryErode reg1.jpg ero1.jpg disk.se
java BinaryDilate reg1.jpg dia1.jpg disk.se
(p.283)
Binary operations:
complement:
intersection:
- 70 -
union:
(p.284)
Opening = erosion dilate
(p.286)
closing: dilate → erode
(p.287)
hit and miss transform:
(p.288)
- 71 -
(p.289)
LISTING 11.2 filter() method of BinaryOpenOp.
public BufferedImage filter(BufferedImage src, BufferedImage dest){
BinaryErodeOp erodeOp = new BinaryErodeOp(structElement);
BinaryDilateOp dilateOp = new BinaryDilateOp(structElement);
if (dest == null)
dest = createCompatibleDestImage(src, null);
return dilateOp.filter(erodeOp.filter(src, null), dest);
}
# binary structuring
element
# width =3
# height = 3
# xorigin = 1
# yorigin = 1
111
111
111 width
height
java BinaryOpen input.pbm result.pbm sq3x3.bas
java HitAndMiss <image> <innerElement>
<outerElement> <outfile>
java BinaryMorphologyTool char_a.png 5 5
- 72 -
(p.291)
- 73 -
(p.292)
greyscale erosion
greyscale dilation
(p.293)
- 74 -
(p.294)
closing
open
detect peakstop-hat transform: g = ƒ -(ƒ ο s).
(ƒ․s) – ƒ → detect valleys in the grey level surface
(p.296)
GreyErode
GreyDilate
GreyOpen
GreyClose
(p.297)
Exercises
4. Using the classes described in this chapter, write a Java program to perform the
top-hat transform.
(p.301)
RMS error (root-mean-square) for MXN image
- 75 -
(p.303)
entropy
(p.304)
information redundancy
compression ratio
insideContour(A,B) = A – erode(A,B)
outsideContour(A,B) = dilate(A,B)-A
middleContour(A,B) = dilate(A,B)-erode(A,B)
http://iris.usc.edu/ision-Notes/bibliogruphy/text/contents.html
http://140.115.11.235/~chen/course/vision
- 76 -