Permalink
Find file
fe52039 Jan 13, 2017
346 lines (304 sloc) 11.9 KB
package org.usfirst.frc.team3238.robot;
import edu.wpi.cscore.CvSink;
import edu.wpi.cscore.CvSource;
import edu.wpi.cscore.UsbCamera;
import edu.wpi.first.wpilibj.CameraServer;
import edu.wpi.first.wpilibj.Preferences;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class ImageProcessor implements Runnable
{
private static List<MatOfPoint> contours, preFilteredContours;
private static double[] hThresh = new double[2];
private static double[] sThresh = new double[2];
private static double[] vThresh = new double[2];
private static boolean external;
private static double minArea = 0;
private static double minPerimeter = 0;
private static double minWidth = 0;
private static double maxWidth = 1000;
private static double minHeight = 0;
private static double maxHeight = 1000;
private static double[] solidity = { 0, 100 };
private static double maxVertices = 1000000;
private static double minVertices = 0;
private static double minRatio = 0;
private static double maxRatio = 1000;
static Preferences prefs;
static UsbCamera camera;
static CvSink cvSink;
static CvSource outputStream;
static Mat source, output;
public static void init()
{
prefs = Preferences.getInstance();
hThresh[0] = prefs.getDouble("hThreshMin", 0.0);
hThresh[1] = prefs.getDouble("hThreshmax", 255.0);
sThresh[0] = prefs.getDouble("sThreshMin", 0.0);
sThresh[1] = prefs.getDouble("sThreshMax", 255.0);
vThresh[0] = prefs.getDouble("vThreshMin", 0.0);
vThresh[1] = prefs.getDouble("vThreshMax", 255.0);
external = prefs.getBoolean("external", true);
minArea = prefs.getDouble("minArea", 0.0);
minPerimeter = prefs.getDouble("minPerimeter", 0.0);
minWidth = prefs.getDouble("minWidth", 0.0);
maxWidth = prefs.getDouble("maxWidth", 1000);
minHeight = prefs.getDouble("minHeight", 0.0);
maxHeight = prefs.getDouble("maxHeight", 1000);
solidity[1] = prefs.getDouble("soliditymax", 100);
solidity[0] = prefs.getDouble("soliditymin", 0.0);
maxVertices = prefs.getDouble("maxVertices", 1000000);
minVertices = prefs.getDouble("minVertices", 0.0);
minRatio = prefs.getDouble("minRatio", 0.0);
maxRatio = prefs.getDouble("maxRatio", 1000);
CameraServer.getInstance().startAutomaticCapture();
camera.setResolution(640, 480);
cvSink = CameraServer.getInstance().getVideo();
outputStream = CameraServer.getInstance().putVideo("somethingThatIsActuallyGood", 640, 480);
source = new Mat();
output = new Mat();
}
public static List<MatOfPoint> getContours()
{
if(contours != null)
{
return contours;
} else
{
return new List<MatOfPoint>()
{
@Override public int size()
{
return 0;
}
@Override public boolean isEmpty()
{
return false;
}
@Override public boolean contains(Object o)
{
return false;
}
@Override public Iterator<MatOfPoint> iterator()
{
return null;
}
@Override public Object[] toArray()
{
return new Object[0];
}
@Override public <T> T[] toArray(T[] a)
{
return null;
}
@Override public boolean add(MatOfPoint matOfPoint)
{
return false;
}
@Override public boolean remove(Object o)
{
return false;
}
@Override public boolean containsAll(Collection<?> c)
{
return false;
}
@Override public boolean addAll(
Collection<? extends MatOfPoint> c)
{
return false;
}
@Override public boolean addAll(int index,
Collection<? extends MatOfPoint> c)
{
return false;
}
@Override public boolean removeAll(Collection<?> c)
{
return false;
}
@Override public boolean retainAll(Collection<?> c)
{
return false;
}
@Override public void clear()
{
}
@Override public MatOfPoint get(int index)
{
return null;
}
@Override public MatOfPoint set(int index, MatOfPoint element)
{
return null;
}
@Override public void add(int index, MatOfPoint element)
{
}
@Override public MatOfPoint remove(int index)
{
return null;
}
@Override public int indexOf(Object o)
{
return 0;
}
@Override public int lastIndexOf(Object o)
{
return 0;
}
@Override public ListIterator<MatOfPoint> listIterator()
{
return null;
}
@Override public ListIterator<MatOfPoint> listIterator(
int index)
{
return null;
}
@Override public List<MatOfPoint> subList(int fromIndex,
int toIndex)
{
return null;
}
};
}
}
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see Thread#run()
*/
@Override public void run()
{
while(true)
{
cvSink.grabFrame(source);
hsvThreshold(source, hThresh, sThresh, vThresh, output );
findContours(output, external, preFilteredContours );
filterContours(preFilteredContours, minArea,
minPerimeter, minWidth,
maxWidth, minHeight,
maxHeight, solidity,
maxVertices, minVertices,
minRatio, maxRatio,
contours);
Imgproc.drawContours(source, contours, -1, new Scalar(255, 0,0));
outputStream.putFrame(output);
}
}
/**
* Segment an image based on hue, saturation, and value ranges.
*
* @param input The image on which to perform the HSL threshold.
* @param hue The min and max hue
* @param sat The min and max saturation
* @param val The min and max value
* @param out The image in which to store the output.
*/
private void hsvThreshold(Mat input, double[] hue, double[] sat,
double[] val, Mat out)
{
Imgproc.cvtColor(input, out, Imgproc.COLOR_BGR2HSV);
Core.inRange(out, new Scalar(hue[0], sat[0], val[0]),
new Scalar(hue[1], sat[1], val[1]), out);
}
/**
* Sets the values of pixels in a binary image to their distance to the
* nearest black pixel.
*
* @param input The image on which to perform the Distance Transform.
// * @param type The Transform.
// * @param maskSize the size of the mask.
// * @param output The image in which to store the output.
*/
private void findContours(Mat input, boolean externalOnly,
List<MatOfPoint> contours)
{
Mat hierarchy = new Mat();
contours.clear();
int mode;
if(externalOnly)
{
mode = Imgproc.RETR_EXTERNAL;
} else
{
mode = Imgproc.RETR_LIST;
}
int method = Imgproc.CHAIN_APPROX_SIMPLE;
Imgproc.findContours(input, contours, hierarchy, mode, method);
}
/**
* Filters out contours that do not meet certain criteria.
*
* @param inputContours is the input list of contours
* @param output is the the output list of contours
* @param minArea is the minimum area of a contour that will be kept
* @param minPerimeter is the minimum perimeter of a contour that will be
* kept
* @param minWidth minimum width of a contour
* @param maxWidth maximum width
* @param minHeight minimum height
* @param maxHeight maximimum height
// * @param Solidity the minimum and maximum solidity of a contour
* @param minVertexCount minimum vertex Count of the contours
* @param maxVertexCount maximum vertex Count
* @param minRatio minimum ratio of width to height
* @param maxRatio maximum ratio of width to height
*/
private void filterContours(List<MatOfPoint> inputContours, double minArea,
double minPerimeter, double minWidth, double maxWidth,
double minHeight, double maxHeight, double[] solidity,
double maxVertexCount, double minVertexCount, double minRatio,
double maxRatio, List<MatOfPoint> output)
{
final MatOfInt hull = new MatOfInt();
output.clear();
//operation
for(int i = 0; i < inputContours.size(); i++)
{
final MatOfPoint contour = inputContours.get(i);
final Rect bb = Imgproc.boundingRect(contour);
if(bb.width < minWidth || bb.width > maxWidth)
continue;
if(bb.height < minHeight || bb.height > maxHeight)
continue;
final double area = Imgproc.contourArea(contour);
if(area < minArea)
continue;
if(Imgproc.arcLength(new MatOfPoint2f(contour.toArray()), true)
< minPerimeter)
continue;
Imgproc.convexHull(contour, hull);
MatOfPoint mopHull = new MatOfPoint();
mopHull.create((int) hull.size().height, 1, CvType.CV_32SC2);
for(int j = 0; j < hull.size().height; j++)
{
int index = (int) hull.get(j, 0)[0];
double[] point = new double[] { contour.get(index, 0)[0],
contour.get(index, 0)[1] };
mopHull.put(j, 0, point);
}
final double solid = 100 * area / Imgproc.contourArea(mopHull);
if(solid < solidity[0] || solid > solidity[1])
continue;
if(contour.rows() < minVertexCount
|| contour.rows() > maxVertexCount)
continue;
final double ratio = bb.width / (double) bb.height;
if(ratio < minRatio || ratio > maxRatio)
continue;
output.add(contour);
}
}
}