File source
import georegression.struct.shapes.EllipseRotated_F64;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import boofcv.alg.color.ColorHsv;
import boofcv.alg.feature.detect.edge.CannyEdge;
import boofcv.alg.feature.shapes.FitData;
import boofcv.alg.feature.shapes.ShapeFittingOps;
import boofcv.alg.filter.binary.BinaryImageOps;
import boofcv.alg.filter.binary.Contour;
import boofcv.core.image.ConvertBufferedImage;
import boofcv.factory.feature.detect.edge.FactoryEdgeDetectors;
import boofcv.gui.feature.VisualizeShapes;
import boofcv.struct.image.ImageFloat32;
import boofcv.struct.image.ImageUInt8;
import boofcv.struct.image.MultiSpectral;
public class ColorReader {
public static Color main(BufferedImage image, BufferedImage output) {
ImageFloat32 input = ConvertBufferedImage.convertFromSingle(image, null, ImageFloat32.class);
ImageUInt8 binary = new ImageUInt8(input.width,input.height);
// detect edges using canny edge detector
CannyEdge<ImageFloat32,ImageFloat32> canny = FactoryEdgeDetectors.canny(4,true, true, ImageFloat32.class, ImageFloat32.class);
canny.process(input,0.1f,0.4f,binary);
ImageUInt8 filtered = binary;
// find the contour around the shapes
List<Contour> contours = BinaryImageOps.contour(filtered,8,null);
// fit an ellipse to each external contour and draw the results
Graphics2D g2 = image.createGraphics();
g2.setStroke(new BasicStroke(3));
g2.setColor(Color.RED);
EllipseRotated_F64 target = null;
for( Contour c : contours ) {
FitData<EllipseRotated_F64> ellipse = ShapeFittingOps.fitEllipse_I32(c.external,0,false,null);
// find and ellipse that is round enough and has wanted size
// if more than one exists, select the larger
if(Math.max(ellipse.shape.a, ellipse.shape.b)/Math.min(ellipse.shape.a, ellipse.shape.b) < 1.5 ){
if( ellipse.shape.a < 120 && (target == null || ellipse.shape.a > target.a)){
target = ellipse.shape;
}
}
}
if(target != null){
Color color = getColor(image, target.center.x, target.center.y, target.a, output);
g2.setColor(color);
VisualizeShapes.drawEllipse(target, g2);
return color;
}
return Color.black;
}
/** Calculate average color from given region */
public static Color getColor(BufferedImage image, double center_x, double center_y, double radius, BufferedImage output){
double hue = 0.0;
int N = 0;
long red = 0;
long green = 0;
long blue = 0;
MultiSpectral<ImageFloat32> input = ConvertBufferedImage.convertFromMulti(image,null,true,ImageFloat32.class);
MultiSpectral<ImageFloat32> hsv = new MultiSpectral<ImageFloat32>(ImageFloat32.class,input.width,input.height,3);
ColorHsv.rgbToHsv_F32(input,hsv);
ImageFloat32 H = hsv.getBand(0);
ImageFloat32 S = hsv.getBand(1);
ImageFloat32 V = hsv.getBand(2);
List<Double> hueList = new Vector<Double>();
/** find sample points in region of interest
* use only points with saturation > 10%
* use only points with value > 50
* discard center region of radius 25px
*/
for( int y = Math.max(0,(int)Math.floor(center_y-radius)); y < Math.min(center_y+radius, hsv.height); y++ ) {
for( int x = Math.max(0,(int)Math.floor(center_x-radius)); x < Math.min(center_x+radius,hsv.width); x++ ) {
double centerDiff = ((x-center_x)*(x-center_x)+(y-center_y)*(y-center_y));
if(S.get(x,y) > 0.1 && V.get(x,y) > 50 && centerDiff < radius*radius && centerDiff > 25*25){
output.setRGB(x,y,image.getRGB(x,y));
hue += H.get(x, y);
int color = image.getRGB(x, y);
// only for debug (average color)
red += ((color >> 16) & 0xFF);
green += ((color >> 8) & 0xFF);
blue += (color & 0xFF);
hueList.add((double) H.get(x,y));
N++;
}else{
output.setRGB(x,y,255);
}
}
}
/** Filtering - discard 10% of samples on the edges */
Collections.sort(hueList);
hue = 0.0;
double x=0.0;
double y=0.0;
N=0;
/** circular average **/
for(int i=hueList.size()/10; i < 9*hueList.size()/10; i++){
y+=Math.sin(hueList.get(i));
x+=Math.cos(hueList.get(i));
N++;
}
hue = Math.atan2(y/N, x/N);
/** return fully saturated color */
return new Color(Color.HSBtoRGB((float) (hue/Math.PI/2.0f), 1.0f, 1.0f));
}
}