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));
    }
    
}