一位網友給我發了幾張灰階圖像,說是他們機關的工業相機拍攝的,畫質非常的清楚,他們
機關是農業科研機關,特别想知道種子的數量,他想知道的是每次工業相機拍攝種子圖檔中
有多少顆粒種子,想到了用圖像處理的辦法解決他們的問題,看了他給我照片,以大米種子
為例。實作了一個簡單的算法流程,可以得到種子的數目。
大緻算法分為以下三個步驟:
1. 将灰階圖像二值化,二值化方法可以參考以前的文章,求取像素平均值,灰階直方圖都
可以
2. 去掉二值化以後的圖像中幹擾噪聲。
3. 得到種子數目,用彩色标記出來。

源圖像如下:
程式進行中間結果及最終效果如下:
大米計數與噪聲塊消去算法基于連通元件标記算法,源代碼如下:
package com.gloomyfish.rice.analysis;
import java.awt.image.bufferedimage;
import java.util.arraylist;
import java.util.arrays;
import java.util.hashmap;
import com.gloomyfish.face.detection.abstractbufferedimageop;
import com.gloomyfish.face.detection.fastconnectedcomponentlabelalg;
public class findricefilter extends abstractbufferedimageop {
private int sumrice;
public int getsumrice() {
return this.sumrice;
}
@override
public bufferedimage filter(bufferedimage src, bufferedimage dest) {
int width = src.getwidth();
int height = src.getheight();
if ( dest == null )
dest = createcompatibledestimage( src, null );
int[] inpixels = new int[width*height];
int[] outpixels = new int[width*height];
getrgb(src, 0, 0, width, height, inpixels );
fastconnectedcomponentlabelalg fccalg = new fastconnectedcomponentlabelalg();
fccalg.setbgcolor(0);
int[] outdata = fccalg.dolabel(inpixels, width, height);
// labels statistic
hashmap<integer, integer> labelmap = new hashmap<integer, integer>();
for(int d=0; d<outdata.length; d++) {
if(outdata[d] != 0) {
if(labelmap.containskey(outdata[d])) {
integer count = labelmap.get(outdata[d]);
count+=1;
labelmap.put(outdata[d], count);
} else {
labelmap.put(outdata[d], 1);
}
}
}
// try to find the max connected component
integer[] keys = labelmap.keyset().toarray(new integer[0]);
arrays.sort(keys);
int threshold = 10;
arraylist<integer> listkeys = new arraylist<integer>();
for(integer key : keys) {
if(labelmap.get(key) <=threshold){
listkeys.add(key);
system.out.println( "number of " + key + " = " + labelmap.get(key));
sumrice = keys.length - listkeys.size();
// calculate means of pixel
int index = 0;
for(int row=0; row<height; row++) {
int ta = 0, tr = 0, tg = 0, tb = 0;
for(int col=0; col<width; col++) {
index = row * width + col;
ta = (inpixels[index] >> 24) & 0xff;
tr = (inpixels[index] >> 16) & 0xff;
tg = (inpixels[index] >> 8) & 0xff;
tb = inpixels[index] & 0xff;
if(outdata[index] != 0 && validrice(outdata[index], listkeys)) {
tr = tg = tb = 255;
tr = tg = tb = 0;
outpixels[index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;
setrgb( dest, 0, 0, width, height, outpixels );
return dest;
private boolean validrice(int i, arraylist<integer> listkeys) {
for(integer key : listkeys) {
if(key == i) {
return false;
return true;
}
大米着色處理很簡單,隻是簡單rgb固定着色,源碼如下:
public class colorfulricefilter extends abstractbufferedimageop {
int index = 0, srcrgb;
int ta = 255, tr = 0, tg = 0, tb = 0;
for(int col=0; col<width; col++) {
// ta = (inpixels[index] >> 24) & 0xff;
// tr = (inpixels[index] >> 16) & 0xff;
// tg = (inpixels[index] >> 8) & 0xff;
// tb = inpixels[index] & 0xff;
srcrgb = inpixels[index] & 0x000000ff;
if(srcrgb > 0 && row < 140) {
tr = 0;
tg = 255;
tb = 0;
} else if(srcrgb > 0 && row >= 140 && row <=280) {
tg = 0;
tb = 255;
} else if(srcrgb > 0 && row >=280) {
tr = 255;
else {
測試程式ui代碼如下:
import java.awt.borderlayout;
import java.awt.color;
import java.awt.dimension;
import java.awt.flowlayout;
import java.awt.graphics;
import java.awt.graphics2d;
import java.awt.image;
import java.awt.mediatracker;
import java.awt.event.actionevent;
import java.awt.event.actionlistener;
import java.io.file;
import java.io.ioexception;
import javax.imageio.imageio;
import javax.swing.jbutton;
import javax.swing.jcomponent;
import javax.swing.jfilechooser;
import javax.swing.jframe;
import javax.swing.jpanel;
public class mainframe extends jcomponent implements actionlistener {
/**
*
*/
private static final long serialversionuid = 1518574788794973574l;
public final static string browse_cmd = "browse...";
public final static string noise_cmd = "remove noise";
public final static string fun_cmd = "colorful rice";
private bufferedimage rawimg;
private bufferedimage resultimage;
private mediatracker tracker;
private dimension mysize;
// jbuttons
private jbutton browsebtn;
private jbutton noisebtn;
private jbutton colorfulbtn;
// rice number....
private int ricenum = -1;
public mainframe() {
jpanel btnpanel = new jpanel();
btnpanel.setlayout(new flowlayout(flowlayout.left));
browsebtn = new jbutton("browse...");
noisebtn = new jbutton("remove noise");
colorfulbtn = new jbutton("colorful rice");
browsebtn.settooltiptext("please select image file...");
noisebtn.settooltiptext("find connected region and draw red rectangle");
colorfulbtn.settooltiptext("remove the minor noise region pixels...");
// buttons
btnpanel.add(browsebtn);
btnpanel.add(noisebtn);
btnpanel.add(colorfulbtn);
// setup listener...
browsebtn.addactionlistener(this);
noisebtn.addactionlistener(this);
colorfulbtn.addactionlistener(this);
browsebtn.setenabled(true);
noisebtn.setenabled(true);
colorfulbtn.setenabled(true);
// minx = miny = 10000;
// maxx = maxy = -1;
mysize = new dimension(500, 300);
jframe demoui = new jframe("rice detection demo");
demoui.getcontentpane().setlayout(new borderlayout());
demoui.getcontentpane().add(this, borderlayout.center);
demoui.getcontentpane().add(btnpanel, borderlayout.south);
demoui.setdefaultcloseoperation(jframe.exit_on_close);
demoui.pack();
demoui.setvisible(true);
public void paint(graphics g) {
graphics2d g2 = (graphics2d) g;
if(rawimg != null) {
image scaledimage = rawimg.getscaledinstance(200, 200, image.scale_fast);
g2.drawimage(scaledimage, 0, 0, 200, 200, null);
if(resultimage != null) {
image scaledimage = resultimage.getscaledinstance(200, 200, image.scale_fast);
g2.drawimage(scaledimage, 210, 0, 200, 200, null);
g2.setpaint(color.red);
if(ricenum > 0) {
g2.drawstring("number of rice : " + ricenum, 100, 300);
} else {
g2.drawstring("number of rice : unknown", 100, 300);
public dimension getpreferredsize() {
return mysize;
public dimension getminimumsize() {
public dimension getmaximumsize() {
public static void main(string[] args) {
new mainframe();
public void actionperformed(actionevent e) {
if(browse_cmd.equals(e.getactioncommand())) {
jfilechooser chooser = new jfilechooser();
chooser.showopendialog(null);
file f = chooser.getselectedfile();
bufferedimage bimage = null;
if(f == null) return;
try {
bimage = imageio.read(f);
} catch (ioexception e1) {
e1.printstacktrace();
tracker = new mediatracker(this);
tracker.addimage(bimage, 1);
// blocked 10 seconds to load the image data
if (!tracker.waitforid(1, 10000)) {
system.out.println("load error.");
system.exit(1);
}// end if
} catch (interruptedexception ine) {
ine.printstacktrace();
system.exit(1);
} // end catch
binaryfilter bfilter = new binaryfilter();
rawimg = bfilter.filter(bimage, null);
repaint();
} else if(noise_cmd.equals(e.getactioncommand())) {
findricefilter frfilter = new findricefilter();
resultimage = frfilter.filter(rawimg, null);
ricenum = frfilter.getsumrice();
} else if(fun_cmd.equals(e.getactioncommand())) {
colorfulricefilter cfilter = new colorfulricefilter();
resultimage = cfilter.filter(resultimage, null);
// do nothing...