今天复习Otsu算法,它利用阈值奖图片转成黑白色。Otsu算法是二值算法,目的是求出一个Threshhold,来最小化Within-Class方差,这等价最大化between-class方差。下面用java来实现算法。
<img src="https://img-blog.csdn.net/20150401123028357" alt="" /><img src="https://img-blog.csdn.net/20150401123001341" alt="" />
package com.lili.ots<pre name="code" class="java">package com.lili.otsu;
public class OtsuThresholder
{
private int histData[];
private int maxLevelValue;
private int threshold;
public OtsuThresholder()
{
histData = new int[256];
}
public int[] getHistData()
{
return histData;
}
public int getMaxLevelValue()
{
return maxLevelValue;
}
public int getThreshold()
{
return threshold;
}
public int doThreshold(byte[] srcData, byte[] monoData)
{
int ptr;
// Clear histogram data
// Set all values to zero
ptr = 0;
while (ptr < histData.length) histData[ptr++] = 0;
// Calculate histogram and find the level with the max value
// Note: the max level value isn't required by the Otsu method
ptr = 0;
maxLevelValue = 0;
while (ptr < srcData.length)
{
int h = 0xFF & srcData[ptr];
histData[h] ++;
if (histData[h] > maxLevelValue) maxLevelValue = histData[h];
ptr ++;
}
// Total number of pixels
int total = srcData.length; //48*57 image, total=48*57
float sum = 0;
for (int t=0 ; t<256 ; t++) sum += t * histData[t];
float sumB = 0;
int wB = 0;
int wF = 0;
float varMax = 0;
threshold = 0;
for (int t=0 ; t<256 ; t++)
{
wB += histData[t]; // Weight Background
if (wB == 0) continue; // 'cause divide the number of pixle of background
wF = total - wB; // Weight Foreground
if (wF == 0) break;
sumB += (float) (t * histData[t]);
float mB = sumB / wB; // Mean Background
float mF = (sum - sumB) / wF; // Mean Foreground
// Calculate Between Class Variance
float varBetween = (float)wB * (float)wF * (mB - mF) * (mB - mF);
// Check if new maximum found
if (varBetween > varMax) {
varMax = varBetween;
threshold = t;
}
}
// Apply threshold to create binary image
if (monoData != null)
{
ptr = 0;
while (ptr < srcData.length) // 48*57
{
monoData[ptr] = ((0xFF & srcData[ptr]) >= threshold) ? (byte) 255 : 0;
ptr ++;
}
}
return threshold;
}
}
package com.lili.otsu;
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import com.lili.otsu.GreyFrame;
import com.lili.otsu.OtsuThresholder;
public class OtsuDemo
{
public OtsuDemo(String filename)
{
// Load Source image
BufferedImage srcImage = null;
try
{
File imgFile = new File(filename);
srcImage = javax.imageio.ImageIO.read(imgFile);
}
catch (IOException ioE)
{
System.err.println(ioE);
System.exit(1);
}
int width = srcImage.getWidth();
int height = srcImage.getHeight();
// Get raw image data
Raster raster = srcImage.getData();
DataBuffer buffer = raster.getDataBuffer();
int type = buffer.getDataType();
if (type != DataBuffer.TYPE_BYTE)
{
System.err.println("Wrong image data type");
System.exit(1);
}
if (buffer.getNumBanks() != 1)
{
System.err.println("Wrong image data format");
System.exit(1);
}
DataBufferByte byteBuffer = (DataBufferByte) buffer;
byte[] srcData = byteBuffer.getData(0);
// Sanity check image
if (width * height != srcData.length) {
System.err.println("Unexpected image data size. Should be greyscale image");
System.exit(1);
}
// Output Image info
System.out.printf("Loaded image: '%s', width: %d, height: %d, num bytes: %d\n", filename, width, height, srcData.length);
byte[] dstData = new byte[srcData.length];
// Create Otsu Thresholder
OtsuThresholder thresholder = new OtsuThresholder();
int threshold = thresholder.doThreshold(srcData, dstData);
System.out.printf("Threshold: %d\n", threshold);
// Create GUI
GreyFrame srcFrame = new GreyFrame(width, height, srcData);
GreyFrame dstFrame = new GreyFrame(width, height, dstData);
GreyFrame histFrame = createHistogramFrame(thresholder);
JPanel infoPanel = new JPanel();
infoPanel.add(histFrame);
JPanel panel = new JPanel(new BorderLayout(5, 5));
panel.setBorder(new javax.swing.border.EmptyBorder(5, 5, 5, 5));
panel.add(infoPanel, BorderLayout.NORTH);
panel.add(srcFrame, BorderLayout.WEST);
panel.add(dstFrame, BorderLayout.EAST);
panel.add(new JLabel("QingrouShui", JLabel.CENTER), BorderLayout.SOUTH);
JFrame frame = new JFrame("QingrouShui");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
// Save Images
try
{
int dotPos = filename.lastIndexOf(".");
String basename = filename.substring(0,dotPos);
javax.imageio.ImageIO.write(dstFrame.getBufferImage(), "PNG", new File(basename+"_BW.png"));
javax.imageio.ImageIO.write(histFrame.getBufferImage(), "PNG", new File(basename+"_hist.png"));
}
catch (IOException ioE)
{
System.err.println("Could not write image " + filename);
}
}
private GreyFrame createHistogramFrame(OtsuThresholder thresholder)
{
int numPixels = 256 * 100;
byte[] histPlotData = new byte[numPixels];
int[] histData = thresholder.getHistData();
int max = thresholder.getMaxLevelValue();
int threshold = thresholder.getThreshold();
for (int l=0 ; l<256 ; l++)
{
int ptr = (numPixels - 256) + l;
int val = (100 * histData[l]) / max;
if (l == threshold)
{
for (int i=0 ; i<100 ; i++, ptr-=256) histPlotData[ptr] = (byte) 128;
}
else
{
for (int i=0 ; i<100 ; i++, ptr-=256) histPlotData[ptr] = (val < i) ? (byte) 255 : 0;
}
}
return new GreyFrame(256, 100, histPlotData);
}
public static void main(String args[])
{
new OtsuDemo("lili_icmv12.png");
System.out.println("QingrouShui");
// if (args.length<1) {
// System.err.println("Provide image filename");
// System.exit(1);
// }
}
}
<pre name="code" class="java">package com.lili.otsu;
import java.awt.*;
import javax.swing.*;
import java.awt.image.*;
import java.awt.color.*;
public class GreyFrame extends JComponent
{
private int width;
private int height;
private Dimension size;
private BufferedImage image;
private String title;
public GreyFrame(int width, int height, byte[] data)
{
this(width, height, data, null);
}
public GreyFrame(int width, int height, byte[] data, String title)
{
this.width = width;
this.height = height;
this.title = title;
size = new Dimension(width, height);
DataBufferByte dataBuffer = new DataBufferByte(data, data.length, 0);
PixelInterleavedSampleModel sampleModel = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, width, height, 1, width, new int[] {0});
ColorSpace colourSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ComponentColorModel colourModel = new ComponentColorModel(colourSpace, new int[] {8}, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
WritableRaster raster = Raster.createWritableRaster(sampleModel, dataBuffer, null);
image = new BufferedImage(colourModel, raster, false, null);
}
public Graphics2D getBufferImageGraphics()
{
return image.createGraphics();
}
public BufferedImage getBufferImage()
{
return image;
}
public Dimension getSize()
{
return size;
}
public Dimension getPreferredSize()
{
return size;
}
public void paint(Graphics g)
{
super.paint(g);
if (image != null) g.drawImage(image, 0, 0, this);
if (title != null) {
g.setColor(Color.RED);
g.drawString(title, 5, height - 5);
}
}
}
<img src="https://img-blog.csdn.net/20150401123028357" alt="" /><img src="https://img-blog.csdn.net/20150401123001341" alt="" />