[Java] GRIP Vision help?!

I generated a GRIP pipeline to use for gear auton in an off season competition, and I wrote what I think should work. Long story short, it didn’t work the way I wanted it to. The robot’s movements were jittery and it was starting and stopping. My thought was that there was a delay between when it was getting values from the calculations to when it was setting the motor speeds, so I rewrote the command. I dded in a thread to calculate the values I’d need, and then it would just set the motor speeds in the execute function, hopefully removing that delay. After working through tons of errors, I finally got it to a point where it builds and uploads; and then does nothing. It does, however, give an exception. I’ll put the pipeline, the command, and the exception down below, but I am very very confused!! (Also, ignore the while(true), one weird problem was that it was just skipping the inside of the run function, so that’s just a placeholder for now.) If you need me to explain any of the code so you can understand why we did some really odd stuff to throw it together, just let me know! Whatever I can do to help you help me :slight_smile:

Command:

package org.usfirst.frc.team4750.robot.commands;

import org.opencv.core.Mat;
import org.usfirst.frc.team4750.robot.Robot;
import org.usfirst.frc.team4750.robot.GripPipeline;

import edu.wpi.cscore.CvSink;
import edu.wpi.cscore.UsbCamera;
import edu.wpi.first.wpilibj.CameraServer;
import edu.wpi.first.wpilibj.command.Command;
import edu.wpi.first.wpilibj.networktables.NetworkTable;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;

/**
 *
 */
public class TrackGear extends Command {

	// Creates double arrays
	double] defaultValue = new double[0];

	boolean isFinished = false;
	
	// Network Tables
	NetworkTable table;
	
	
	public VisionThread calculateSpeed;
	
	
	// Create variables
	double xOffset;
	double offsetMotorSpeed;

	// Vision Thread
	class VisionThread extends Thread {
		boolean keepgoing = true;

		double] area;
		double] centerX;
		double] centerY;
		// Declare pipeline
		public GripPipeline gearPipe;
		double goalCenterX;
		double goalCenterY;
		// Create instance of camera, cvsink, mat
		public UsbCamera camera;
		public CvSink cvsink;
		public Mat mat;
		
		public VisionThread() {
			keepgoing=true;
			camera = CameraServer.getInstance().startAutomaticCapture();
			cvsink = CameraServer.getInstance().getVideo(camera);
			mat = new Mat();
			gearPipe = new GripPipeline();
			table = NetworkTable.getTable("GRIP/gearReport");
		}
		@Override
		public void run() {
			System.out.println("VisionThread Started...");
			while (true) {
				System.out.println("In Visionthread run!!!");
				// Process mat
				gearPipe.process(mat);
				// Set double arrays to NetworkTable values
				area = table.getNumberArray("area", defaultValue);
				centerX = table.getNumberArray("centerX", defaultValue);
				centerY = table.getNumberArray("centerY", defaultValue);
				// If 2 or more contours are shown
				if (area.length >= 2) {
					// Average of centerX and centerY (average of goal)
					goalCenterX = (centerX[0] + centerX[1]) / 2;
					goalCenterY = (centerY[0] + centerY[1]) / 2;
					xOffset = 320 - goalCenterX;
					offsetMotorSpeed = (Math.abs(xOffset) * 1.64) + 60;
					SmartDashboard.putNumber("ThreadxOffset", xOffset);
					SmartDashboard.putNumber("Thread offsetMotorSpeed", offsetMotorSpeed);
				} else { // If less than 2 contours
					// Stop drive
					offsetMotorSpeed = 0;
				}
				if(keepgoing==false)
					break;
			}
			
			System.out.println("VisionThread Done!!!");
		}

		public void stopThread() {
			keepgoing = false;
		}
	}

	public TrackGear() {
		// Use requires() here to declare subsystem dependencies
		// eg. requires(chassis);
	}

	// Called just before this Command runs the first time
	protected void initialize() {
		System.out.println("TrackGear initialize()");
		calculateSpeed = new VisionThread();
		// Start vision thread
		calculateSpeed.start();
		System.out.println("TrackGear initialize done!");
	}

	// Called repeatedly when this Command is scheduled to run
	protected void execute() {
		//Create new local variables
		double offset = xOffset;
		double motorSpeed = offsetMotorSpeed;
		SmartDashboard.putNumber("Local xoffset", offset);
		SmartDashboard.putNumber("motorSpeed", motorSpeed);
		// If the offset is less than 5px
		if (Math.abs(offset) < 5) {
			// Stop motors
			Robot.driveTrain.visionDrive(0);
			// Stop command
			isFinished = true;
		} else {
			// If the offset is positive
			if (offset > 0) {
				// Strafe left
				Robot.driveTrain.visionDrive(-motorSpeed);
			} else { // If the offset is negative
				// Strafe right
				Robot.driveTrain.visionDrive(motorSpeed);
			}
		}
	}

	// Make this return true when this Command no longer needs to run execute()
	protected boolean isFinished() {
		if (isFinished = false) {
			return false;
		} else {
			calculateSpeed.stopThread();
			return true;
		}
	}

	// Called once after isFinished returns true
	protected void end() {
	}

	// Called when another command which requires one or more of the same
	// subsystems is scheduled to run
	protected void interrupted() {
	}
}

Error:

 OpenCV Error: Assertion failed ((scn == 3 || scn == 4) && (depth == CV_8U || depth == CV_32F)) in cvtColor, file /var/lib/jenkins/workspace/OpenCV-roborio/modules/imgproc/src/color.cpp, line 8141 
 Exception in thread "Thread-0" CvException [org.opencv.core.CvException: cv::Exception: /var/lib/jenkins/workspace/OpenCV-roborio/modules/imgproc/src/color.cpp:8141: error: (-215) (scn == 3 || scn == 4) && (depth == CV_8U || depth == CV_32F) in function cvtColor 
 ] 
 	at org.opencv.imgproc.Imgproc.cvtColor_1(Native Method) 
 	at org.opencv.imgproc.Imgproc.cvtColor(Unknown Source) 
 	at org.usfirst.frc.team4750.robot.GripPipe.hslThreshold(GripPipe.java:106) 
 	at org.usfirst.frc.team4750.robot.GripPipe.process(GripPipe.java:46) 
 	at org.usfirst.frc.team4750.robot.commands.TrackGear$VisionThread.run(TrackGear.java:66) 
 NT: server: client CONNECTED: 10.47.50.46 port 57978

Pipeline:

package org.usfirst.frc.team4750.robot;

import java.util.ArrayList;
import java.util.List;

import org.opencv.core.*;
import org.opencv.imgproc.*;

/**
* GripPipeline class.
*
* <p>An OpenCV pipeline generated by GRIP.
*
* @author GRIP
*/
public class GripPipeline {

	//Outputs
	private Mat resizeImageOutput = new Mat();
	private Mat hslThresholdOutput = new Mat();
	private ArrayList<MatOfPoint> findContoursOutput = new ArrayList<MatOfPoint>();
	private ArrayList<MatOfPoint> filterContoursOutput = new ArrayList<MatOfPoint>();

	static {
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
	}

	/**
	 * This is the primary method that runs the entire pipeline and updates the outputs.
	 */
	public void process(Mat source0) {
		// Step Resize_Image0:
		Mat resizeImageInput = source0;
		double resizeImageWidth = 640.0;
		double resizeImageHeight = 480.0;
		int resizeImageInterpolation = Imgproc.INTER_CUBIC;
		resizeImage(resizeImageInput, resizeImageWidth, resizeImageHeight, resizeImageInterpolation, resizeImageOutput);

		// Step HSL_Threshold0:
		Mat hslThresholdInput = resizeImageOutput;
		double] hslThresholdHue = {38.84892086330935, 98.3059261557555};
		double] hslThresholdSaturation = {36.954348510643435, 255.0};
		double] hslThresholdLuminance = {80.26079136690647, 255.0};
		hslThreshold(hslThresholdInput, hslThresholdHue, hslThresholdSaturation, hslThresholdLuminance, hslThresholdOutput);

		// Step Find_Contours0:
		Mat findContoursInput = hslThresholdOutput;
		boolean findContoursExternalOnly = false;
		findContours(findContoursInput, findContoursExternalOnly, findContoursOutput);

		// Step Filter_Contours0:
		ArrayList<MatOfPoint> filterContoursContours = findContoursOutput;
		double filterContoursMinArea = 200.0;
		double filterContoursMinPerimeter = 0.0;
		double filterContoursMinWidth = 0.0;
		double filterContoursMaxWidth = 1000.0;
		double filterContoursMinHeight = 0.0;
		double filterContoursMaxHeight = 1000.0;
		double] filterContoursSolidity = {0, 100};
		double filterContoursMaxVertices = 1000000.0;
		double filterContoursMinVertices = 0.0;
		double filterContoursMinRatio = 0.0;
		double filterContoursMaxRatio = 1000.0;
		filterContours(filterContoursContours, filterContoursMinArea, filterContoursMinPerimeter, filterContoursMinWidth, filterContoursMaxWidth, filterContoursMinHeight, filterContoursMaxHeight, filterContoursSolidity, filterContoursMaxVertices, filterContoursMinVertices, filterContoursMinRatio, filterContoursMaxRatio, filterContoursOutput);

	}

	/**
	 * This method is a generated getter for the output of a Resize_Image.
	 * @return Mat output from Resize_Image.
	 */
	public Mat resizeImageOutput() {
		return resizeImageOutput;
	}

	/**
	 * This method is a generated getter for the output of a HSL_Threshold.
	 * @return Mat output from HSL_Threshold.
	 */
	public Mat hslThresholdOutput() {
		return hslThresholdOutput;
	}

	/**
	 * This method is a generated getter for the output of a Find_Contours.
	 * @return ArrayList<MatOfPoint> output from Find_Contours.
	 */
	public ArrayList<MatOfPoint> findContoursOutput() {
		return findContoursOutput;
	}

	/**
	 * This method is a generated getter for the output of a Filter_Contours.
	 * @return ArrayList<MatOfPoint> output from Filter_Contours.
	 */
	public ArrayList<MatOfPoint> filterContoursOutput() {
		return filterContoursOutput;
	}


	/**
	 * Scales and image to an exact size.
	 * @param input The image on which to perform the Resize.
	 * @param width The width of the output in pixels.
	 * @param height The height of the output in pixels.
	 * @param interpolation The type of interpolation.
	 * @param output The image in which to store the output.
	 */
	private void resizeImage(Mat input, double width, double height,
		int interpolation, Mat output) {
		Imgproc.resize(input, output, new Size(width, height), 0.0, 0.0, interpolation);
	}

	/**
	 * Segment an image based on hue, saturation, and luminance 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 lum The min and max luminance
	 * @param output The image in which to store the output.
	 */
	private void hslThreshold(Mat input, double] hue, double] sat, double] lum,
		Mat out) {
		Imgproc.cvtColor(input, out, Imgproc.COLOR_BGR2HLS);
		Core.inRange(out, new Scalar(hue[0], lum[0], sat[0]),
			new Scalar(hue[1], lum[1], sat[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);
		}
	}




}


It’s the offseason so we’ll try the show instead of tell approach.

Ctrl + F your code for the phrase “mat”. Follow this variable through the expected code flow and I believe you will see what might be happening.

Spoiler:
What is in this variable when you call gearpipe.process on it?

So do you mean I have to do something like

cvSink.grabFrame(mat);

inside the thread?

Yeah, something like that…:smiley: Otherwise you are processing an empty image.

You probably want to put in something like

long frameTime = cvSink.grabFrame(mat);
if(frameTime == 0) continue;

This will prevent errors and save cpu time when the imagesink returns an empty image for 1 frame.

Okay – Thank you so much to you both! I was able to get it to a point where the vision thread would run and continue to loop; but now I think it’s an issue with the pipeline. No values are outputting to the network table, but the vision thread and the pipeline are both running. I used NTPublishContours in GRIPS but I can’t see where in the pipeline it outputs any values to the network table. I’ve been looking around on the forums but I can’t find anything that lets me know if I have to add in some outputs in the pipeline. Here’s what I have right now:

Command:

package org.usfirst.frc.team4750.robot.commands;

import org.opencv.core.Mat;
import org.usfirst.frc.team4750.robot.Robot;
import org.usfirst.frc.team4750.robot.GripPipeline;

import edu.wpi.cscore.CvSink;
import edu.wpi.cscore.UsbCamera;
import edu.wpi.first.wpilibj.CameraServer;
import edu.wpi.first.wpilibj.command.Command;
import edu.wpi.first.wpilibj.networktables.NetworkTable;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;

/**
 *
 */
public class TrackGear extends Command {

	// Creates double arrays
	double] defaultValue = new double[0];

	boolean isFinished = false;
	
	// Network Tables
	NetworkTable table;
	
	
	public VisionThread calculateSpeed;
	
	
	// Create variables
	double xOffset;
	double offsetMotorSpeed;

	// Vision Thread
	class VisionThread extends Thread {
		boolean keepGoing = true;

		double] area;
		double] centerX;
		// Declare pipeline
		public GripPipeline gearPipe;
		double goalCenterX;
		double goalCenterY;
		// Create instance of camera, cvsink, mat
		public UsbCamera camera = new UsbCamera("camera", "cam1");
		public CvSink cvsink;
		public Mat mat;
		
		public VisionThread() {
			keepGoing=true;
			camera = CameraServer.getInstance().startAutomaticCapture();
			camera.setResolution(640, 480);
			cvsink = CameraServer.getInstance().getVideo(camera);
			mat = new Mat();
			gearPipe = new GripPipeline();
			table = NetworkTable.getTable("GRIP/gearReport");
			System.out.print("NetworkTable init");
		}
		@Override
		public void run() {
			System.out.println("VisionThread Started...");
			while (true) {
				long frameTime = cvsink.grabFrame(mat);
				if(frameTime != 0);
				System.out.println("In Visionthread run!!!");
				// Process mat
				gearPipe.process(mat);
				// Set double arrays to NetworkTable values
				area = table.getNumberArray("area", defaultValue);
				centerX = table.getNumberArray("centerX", defaultValue);
				// If 2 or more contours are shown
				if (area.length >= 2) {
					// Average of centerX (average of goal)
					goalCenterX = (centerX[0] + centerX[1]) / 2;
					xOffset = 320 - goalCenterX;
					offsetMotorSpeed = (Math.abs(xOffset) * 1.64) + 60;
					SmartDashboard.putNumber("ThreadxOffset", xOffset);
					SmartDashboard.putNumber("Thread offsetMotorSpeed", offsetMotorSpeed);
				} else { // If less than 2 contours
					// Stop drive
					offsetMotorSpeed = 0;
				}
				if(false)
					break;
			}
			
			System.out.println("VisionThread Done!!!");
		}

		public void stopThread() {
			keepGoing = false;
		}
	}

	public TrackGear() {
		// Use requires() here to declare subsystem dependencies
		// eg. requires(chassis);
	}

	// Called just before this Command runs the first time
	protected void initialize() {
		System.out.println("TrackGear initialize()");
		calculateSpeed = new VisionThread();
		// Start vision thread
		calculateSpeed.start();
		System.out.println("TrackGear initialize done!");
	}

	// Called repeatedly when this Command is scheduled to run
	protected void execute() {
		SmartDashboard.putNumber("is offset changing?", xOffset);
		//Create new local variables
		double offset = xOffset;
		double motorSpeed = offsetMotorSpeed;
		SmartDashboard.putNumber("Local xoffset", offset);
		SmartDashboard.putNumber("motorSpeed", motorSpeed);
		// If the offset is less than 5px
		if (Math.abs(offset) < 5) {
			// Stop motors
			Robot.driveTrain.visionDrive(0);
			// Stop command
			isFinished = true;
		} else {
			// If the offset is positive
			if (offset > 0) {
				// Strafe left
				Robot.driveTrain.visionDrive(-motorSpeed);
			} else { // If the offset is negative
				// Strafe right
				Robot.driveTrain.visionDrive(motorSpeed);
			}
		}
	}

	// Make this return true when this Command no longer needs to run execute()
	protected boolean isFinished() {
		if (isFinished = false) {
			return false;
		} else {
			calculateSpeed.stopThread();
			return true;
		}
	}

	// Called once after isFinished returns true
	protected void end() {
	}

	// Called when another command which requires one or more of the same
	// subsystems is scheduled to run
	protected void interrupted() {
	}
}

Pipeline:

package org.usfirst.frc.team4750.robot;

import java.util.ArrayList;
import java.util.List;

import org.opencv.core.*;
import org.opencv.imgproc.*;

import edu.wpi.first.wpilibj.networktables.NetworkTable;

/**
* GripPipeline class.
*
* <p>An OpenCV pipeline generated by GRIP.
*
* @author GRIP
*/
public class GripPipeline {

	//Outputs
	private Mat resizeImageOutput = new Mat();
	private Mat hslThresholdOutput = new Mat();
	private ArrayList<MatOfPoint> findContoursOutput = new ArrayList<MatOfPoint>();
	private ArrayList<MatOfPoint> filterContoursOutput = new ArrayList<MatOfPoint>();

	static {
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
	}

	/**
	 * This is the primary method that runs the entire pipeline and updates the outputs.
	 */
	public void process(Mat source0) {
		System.out.println("pipeline run!!!");
		// Step Resize_Image0:
		Mat resizeImageInput = source0;
		double resizeImageWidth = 640.0;
		double resizeImageHeight = 480.0;
		int resizeImageInterpolation = Imgproc.INTER_CUBIC;
		resizeImage(resizeImageInput, resizeImageWidth, resizeImageHeight, resizeImageInterpolation, resizeImageOutput);

		// Step HSL_Threshold0:
		Mat hslThresholdInput = resizeImageOutput;
		double] hslThresholdHue = {38.84892086330935, 98.3059261557555};
		double] hslThresholdSaturation = {36.954348510643435, 255.0};
		double] hslThresholdLuminance = {80.26079136690647, 255.0};
		hslThreshold(hslThresholdInput, hslThresholdHue, hslThresholdSaturation, hslThresholdLuminance, hslThresholdOutput);

		// Step Find_Contours0:
		Mat findContoursInput = hslThresholdOutput;
		boolean findContoursExternalOnly = false;
		findContours(findContoursInput, findContoursExternalOnly, findContoursOutput);

		// Step Filter_Contours0:
		ArrayList<MatOfPoint> filterContoursContours = findContoursOutput;
		double filterContoursMinArea = 200.0;
		double filterContoursMinPerimeter = 0.0;
		double filterContoursMinWidth = 0.0;
		double filterContoursMaxWidth = 1000.0;
		double filterContoursMinHeight = 0.0;
		double filterContoursMaxHeight = 1000.0;
		double] filterContoursSolidity = {0, 100};
		double filterContoursMaxVertices = 1000000.0;
		double filterContoursMinVertices = 0.0;
		double filterContoursMinRatio = 0.0;
		double filterContoursMaxRatio = 1000.0;
		filterContours(filterContoursContours, filterContoursMinArea, filterContoursMinPerimeter, filterContoursMinWidth, filterContoursMaxWidth, filterContoursMinHeight, filterContoursMaxHeight, filterContoursSolidity, filterContoursMaxVertices, filterContoursMinVertices, filterContoursMinRatio, filterContoursMaxRatio, filterContoursOutput);
	}

	/**
	 * This method is a generated getter for the output of a Resize_Image.
	 * @return Mat output from Resize_Image.
	 */
	public Mat resizeImageOutput() {
		return resizeImageOutput;
	}

	/**
	 * This method is a generated getter for the output of a HSL_Threshold.
	 * @return Mat output from HSL_Threshold.
	 */
	public Mat hslThresholdOutput() {
		return hslThresholdOutput;
	}

	/**
	 * This method is a generated getter for the output of a Find_Contours.
	 * @return ArrayList<MatOfPoint> output from Find_Contours.
	 */
	public ArrayList<MatOfPoint> findContoursOutput() {
		return findContoursOutput;
	}

	/**
	 * This method is a generated getter for the output of a Filter_Contours.
	 * @return ArrayList<MatOfPoint> output from Filter_Contours.
	 */
	public ArrayList<MatOfPoint> filterContoursOutput() {
		return filterContoursOutput;
	}


	/**
	 * Scales and image to an exact size.
	 * @param input The image on which to perform the Resize.
	 * @param width The width of the output in pixels.
	 * @param height The height of the output in pixels.
	 * @param interpolation The type of interpolation.
	 * @param output The image in which to store the output.
	 */
	private void resizeImage(Mat input, double width, double height,
		int interpolation, Mat output) {
		Imgproc.resize(input, output, new Size(width, height), 0.0, 0.0, interpolation);
	}

	/**
	 * Segment an image based on hue, saturation, and luminance 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 lum The min and max luminance
	 * @param output The image in which to store the output.
	 */
	private void hslThreshold(Mat input, double] hue, double] sat, double] lum,
		Mat out) {
		Imgproc.cvtColor(input, out, Imgproc.COLOR_BGR2HLS);
		Core.inRange(out, new Scalar(hue[0], lum[0], sat[0]),
			new Scalar(hue[1], lum[1], sat[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);
		}
	}




}


The exported GRIP pipeline does not contain the Network Tables code. You have to do that yourself. I posted some sample code at https://www.chiefdelphi.com/forums/showpost.php?p=1648943&postcount=10 that we used on a co-processor, but the NT code should be pretty much the same.

Do you have the GripPipeline.java file I can see?

Here is a pipeline that was used at one point during development. Initially we were just dropping in the file created by a GRIP pipeline code export into the src/main/java folder of the JavaGradle project. Note that the last stage matches up with what this code expects, i.e. the last stage is filterContours and the main code consumes filterContoursOutput.

Later we played with the concept of having multiple named pipelines that the code could dynamically switch between. That required editing the GRIP pipeline file (to name the class) and creating a base pipeline class that each named pipeline could inherit.

GripPipeline.java (8.44 KB)