private static Mat generateBoardProjection(Mat boardThreshold, boolean ui)
			throws VisionException {

		// Find the polygon enclosing the blue connect four board
		LinkedList<MatOfPoint> contours = new LinkedList<MatOfPoint>();
		Mat hierarchy = new Mat();
		Imgproc.findContours(boardThreshold, contours, hierarchy,
				Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));
		int maxContourIndex = 0;
		double maxArea = 0;
		for (int i = 0; i < contours.size(); i++) {
			double area = Imgproc.contourArea(contours.get(i));
			if (area > maxArea) {
				maxArea = area;
				maxContourIndex = i;
			}
		}

		// Calculate the percent of the image the detected blue area makes up
		double percentSpace = maxArea
				/ (boardThreshold.width() * boardThreshold.height()) * 100;
		System.out.println("The board occupies " + Math.round(percentSpace)
				+ "% of the image.");

		// Throw an exception if the detected blue area is too small
		if (percentSpace < 20) {
			throw new VisionException(
					"A sufficiently large board could not be detected.");
		}

		// Find possible border lines of the enclosing polygon
		Mat newImage = Mat.zeros(boardThreshold.size(), boardThreshold.type());
		Imgproc.drawContours(newImage, contours, maxContourIndex, new Scalar(
				255));
		Mat lines = new Mat();
		Imgproc.HoughLines(newImage, lines, 1, Math.PI / 180, 75);
		LinkedList<Line> detectedLines = new LinkedList<Line>();
		for (int i = 0; i < lines.cols(); i++) {
			double[] info = lines.get(0, i);
			Line line = new Line(info[0], info[1]);
			Core.clipLine(
					new Rect(0, 0, boardThreshold.width(), boardThreshold
							.height()), line.getPt1(), line.getPt2());
			detectedLines.push(line);
		}
		System.out.println("There are " + detectedLines.size()
				+ " lines that were detected.");
		if (ui) {
			Mat debugImage = originalBoardImage.clone();
			Imgproc.drawContours(debugImage, contours, maxContourIndex, new Scalar(
				0, 0, 255), 3);
			for (Line line : detectedLines) {
				Core.line(debugImage, line.getPt1(), line.getPt2(), new Scalar(0,
						255, 0), 3);
			}
			showResult(debugImage);
		}

		// Get the corners of the polygon and apply the transform
		Mat corners1 = calculateBorderFromLines(detectedLines);
		Size resultSize = originalBoardImage.size();
		double[] data1 = { 0, 0 };
		double[] data2 = { resultSize.width, 0 };
		double[] data3 = { resultSize.width, resultSize.height };
		double[] data4 = { 0, resultSize.height };
		Mat corners2 = new Mat(new Size(4, 1), CvType.CV_32FC2);
		corners2.put(0, 0, data1);
		corners2.put(0, 1, data2);
		corners2.put(0, 2, data3);
		corners2.put(0, 3, data4);
		Mat transform = Imgproc.getPerspectiveTransform(corners1, corners2);
		Mat dst = new Mat(resultSize, originalBoardImage.type());
		Imgproc.warpPerspective(originalBoardImage, dst, transform, dst.size());
		return dst;

	}