一位网友给我发了几张灰度图像,说是他们单位的工业相机拍摄的,画质非常的清楚,他们
单位是农业科研单位,特别想知道种子的数量,他想知道的是每次工业相机拍摄种子图片中
有多少颗粒种子,想到了用图像处理的办法解决他们的问题,看了他给我照片,以大米种子
为例。实现了一个简单的算法流程,可以得到种子的数目。
大致算法分为以下三个步骤:
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...