List of usage examples for org.opencv.core Mat size
public Size size()
From source file:org.ar.rubik.MonoChromatic.java
License:Open Source License
/** * Computer Mono Chromatic Filter using GPU and OpenCL * @param image/*from ww w .ja va 2 s. c o m*/ * @return */ private static Mat monochromaticMedianImageFilterOpenCL(Mat image) { Log.i(Constants.TAG, "Mono Arg Image: " + image); // 720*1280 CV_8UC3 // Imgproc.resize(image, image, new Size(image.size().height/2, image.size().width/2)); Size size = image.size(); // Create OpenCL Output Bit Map Bitmap outputBitmap = Bitmap.createBitmap((int) size.width, (int) size.height, Bitmap.Config.ARGB_8888); // Create OpenCL Input Bit Map Bitmap inputBitmap = Bitmap.createBitmap((int) size.width, (int) size.height, Bitmap.Config.ARGB_8888); // Convert to Hue, Luminance, and Saturation // long startHLS = System.currentTimeMillis(); Mat hls_image = new Mat(); Imgproc.cvtColor(image, hls_image, Imgproc.COLOR_BGR2YUV); // Log.i(Constants.TAG, "Mono HLS Image: " + hls_image); // 720*1280 CV_8UC3 // long endHLS = System.currentTimeMillis(); // Log.i(Constants.TAG, "HLS Conversion Time: " + (endHLS - startHLS) + "mS"); // Convert image Mat to Bit Map. Utils.matToBitmap(hls_image, inputBitmap); // Call C++ code. nativeStepOpenCL((int) 7, (int) 5, 0, 0, true, inputBitmap, outputBitmap); Mat result = new Mat(); Utils.bitmapToMat(outputBitmap, result); // long startChannel = System.currentTimeMillis(); List<Mat> channels = new LinkedList<Mat>(); Core.split(result, channels); Mat channel0 = channels.get(0); // long endChannel = System.currentTimeMillis(); // Log.i(Constants.TAG, "Channel Conversion Time: " + (endChannel - startChannel) + "mS"); return channel0; // return result; }
From source file:org.ar.rubik.MonoChromatic.java
License:Open Source License
/** * Create submatrix using bytearray, then Mat.minmax(). * This solution consumes about 10 seconds per frame. * /*from w w w. j ava 2 s . c om*/ * @param original_image * @return */ private static Mat monochromaticMedianImageFilterUtilizingOpenCv3(Mat original_image) { final Size imageSize = original_image.size(); Mat monochromatic_image = new Mat(imageSize, CvType.CV_8UC1); Mat hsv_image = new Mat(imageSize, CvType.CV_8UC3); Imgproc.cvtColor(original_image, hsv_image, Imgproc.COLOR_RGB2HLS); // Log.i(Constants.TAG, "HSV Image: " + hsv_image); // CV_8UC3 // Try RGB below // hsv_image = result; // Get hue channel into simple byte array for speed efficiency. final int numColumns = (int) original_image.size().width; final int numRows = (int) original_image.size().height; final int span = (int) 7; final int accuracy = (int) 5; List<Mat> channels = new LinkedList<Mat>(); Core.split(hsv_image, channels); Mat hueMat = channels.get(0); Mat lumMat = channels.get(1); Mat satMat = channels.get(2); final int bufferSize = numColumns * numRows; byte[] hueByteArray = new byte[bufferSize]; byte[] lumByteArray = new byte[bufferSize]; byte[] satByteArray = new byte[bufferSize]; hueMat.get(0, 0, hueByteArray); // get all the pixels lumMat.get(0, 0, lumByteArray); // get all the pixels satMat.get(0, 0, satByteArray); // get all the pixels // Output byte array for speed efficiency byte[] monochromaticByteArray = new byte[bufferSize]; Mat subimageMat = new Mat(span, span, CvType.CV_8UC1); byte[] subimageByteArray = new byte[span * span]; for (int row = 0; row < numRows; row++) { byte result_pixel = 0; for (int col = 0; col < numColumns; col++) { if (col < span || (col >= numColumns - span)) result_pixel = 0; // Just put in black else if (row < span || (row >= numRows - span)) result_pixel = 0; // Just put in black else { // Copy a row (or column) for (int i = 0; i < span; i++) { // copy span bytes from (row + i) * numCol + col int srcPos = (row + i) * numColumns + col; int dstPos = i * span; System.arraycopy(hueByteArray, srcPos, subimageByteArray, dstPos, span); } subimageMat.put(0, 0, subimageByteArray); Core.MinMaxLocResult minMaxResult = Core.minMaxLoc(subimageMat); if (((minMaxResult.maxVal - minMaxResult.maxVal) < accuracy)) //&& (lum_max - lum_min < accuracy) && (sat_max - sat_min < accuracy) ) result_pixel = (byte) 128; else result_pixel = (byte) 0; // Log.i(Constants.TAG, String.format("Lum %d %d", lum_min, lum_max)); } // End of else if ((col >= span / 2) && (row >= span / 2)) monochromaticByteArray[(row - span / 2) * numColumns + (col - span / 2)] = result_pixel; // int test = (int)(satByteArray[row * numColumns + col]) & 0xFF; // monochromaticByteArray[row * numColumns + (col - span/2)] = (byte) test; } // End of column sweep } // End of row sweep Log.i(Constants.TAG, "Completed MonoChromatic CV"); monochromatic_image.put(0, 0, monochromaticByteArray); return monochromatic_image; }
From source file:org.ar.rubik.MonoChromatic.java
License:Open Source License
/** * Use mask operation and then min max.//from w w w. j a v a2 s.c o m * This solution consumes about 20 minutes per frame! * * @param original_image * @return */ @SuppressWarnings("unused") private static Mat monochromaticMedianImageFilterUtilizingOpenCv2(Mat original_image) { final Size imageSize = original_image.size(); final int numColumns = (int) original_image.size().width; final int numRows = (int) original_image.size().height; final int bufferSize = numColumns * numRows; final int span = (int) 7; final int accuracy = (int) 5; Mat hsv_image = new Mat(imageSize, CvType.CV_8UC3); Imgproc.cvtColor(original_image, hsv_image, Imgproc.COLOR_RGB2HLS); List<Mat> channels = new LinkedList<Mat>(); Core.split(hsv_image, channels); Mat hueMat = channels.get(0); Mat lumMat = channels.get(1); Mat satMat = channels.get(2); // Output byte array for speed efficiency Mat monochromatic_image = new Mat(imageSize, CvType.CV_8UC1); byte[] monochromaticByteArray = new byte[bufferSize]; Mat mask = Mat.zeros(numRows, numColumns, CvType.CV_8UC1); Log.i(Constants.TAG, "Begin MonoChromatic CV"); for (int row = 0; row < numRows; row++) { byte result_pixel = 0; for (int col = 0; col < numColumns; col++) { if (col < span || (col >= numColumns - span)) result_pixel = 0; // Just put in black else if (row < span || (row >= numRows - span)) result_pixel = 0; // Just put in black else { // Log.i(Constants.TAG, "Creating Mask at " + row +"," + col); Core.rectangle(mask, new Point(row, col), new Point(row + span, col + span), new Scalar(1, 1, 1)); // Core.MinMaxLocResult minMaxResult = Core.minMaxLoc(hueMat, mask); Mat subset = new Mat(); hueMat.copyTo(subset, mask); Core.MinMaxLocResult minMaxResult = Core.minMaxLoc(subset); if (((minMaxResult.maxVal - minMaxResult.maxVal) < accuracy)) //&& (lum_max - lum_min < accuracy) && (sat_max - sat_min < accuracy) ) result_pixel = (byte) 128; else result_pixel = (byte) 0; // Log.i(Constants.TAG, "Completed Mask at " + row +"," + col); Core.rectangle(mask, new Point(row, col), new Point(row + span, col + span), new Scalar(0, 0, 0)); } if ((col >= span / 2) && (row >= span / 2)) monochromaticByteArray[(row - span / 2) * numColumns + (col - span / 2)] = result_pixel; } Log.i(Constants.TAG, "Completed Row: " + row); } monochromatic_image.put(0, 0, monochromaticByteArray); Log.i(Constants.TAG, "Completed MonoChromatic CV"); // System.exit(0); return monochromatic_image; }
From source file:org.ar.rubik.MonoChromatic.java
License:Open Source License
/** * Use OpenCV minMax./*from w w w .j a v a2s .c om*/ * * However, this is enormously slow, taking 10 minutes per frame! Why? * I think because it is effective O(O^4) in computation. * * @param original_image * @return */ @SuppressWarnings("unused") private static Mat monochromaticMedianImageFilterUtilizingOpenCv(Mat original_image) { final Size imageSize = original_image.size(); final int numColumns = (int) original_image.size().width; final int numRows = (int) original_image.size().height; final int bufferSize = numColumns * numRows; final int span = (int) 7; final int accuracy = (int) 5; Mat hsv_image = new Mat(imageSize, CvType.CV_8UC3); Imgproc.cvtColor(original_image, hsv_image, Imgproc.COLOR_RGB2HLS); List<Mat> channels = new LinkedList<Mat>(); Core.split(hsv_image, channels); Mat hueMat = channels.get(0); Mat lumMat = channels.get(1); Mat satMat = channels.get(2); // Output byte array for speed efficiency Mat monochromatic_image = new Mat(imageSize, CvType.CV_8UC1); byte[] monochromaticByteArray = new byte[bufferSize]; Mat mask = Mat.zeros(numRows, numColumns, CvType.CV_8UC1); Log.i(Constants.TAG, "Begin MonoChromatic CV"); for (int row = 0; row < numRows; row++) { byte result_pixel = 0; for (int col = 0; col < numColumns; col++) { if (col < span || (col >= numColumns - span)) result_pixel = 0; // Just put in black else if (row < span || (row >= numRows - span)) result_pixel = 0; // Just put in black else { // Log.i(Constants.TAG, "Creating Mask at " + row +"," + col); Core.rectangle(mask, new Point(row, col), new Point(row + span, col + span), new Scalar(1, 1, 1)); Core.MinMaxLocResult minMaxResult = Core.minMaxLoc(hueMat, mask); if (((minMaxResult.maxVal - minMaxResult.maxVal) < accuracy)) //&& (lum_max - lum_min < accuracy) && (sat_max - sat_min < accuracy) ) result_pixel = (byte) 128; else result_pixel = (byte) 0; // Log.i(Constants.TAG, "Completed Mask at " + row +"," + col); Core.rectangle(mask, new Point(row, col), new Point(row + span, col + span), new Scalar(0, 0, 0)); } if ((col >= span / 2) && (row >= span / 2)) monochromaticByteArray[(row - span / 2) * numColumns + (col - span / 2)] = result_pixel; } Log.i(Constants.TAG, "Completed Row: " + row); } monochromatic_image.put(0, 0, monochromaticByteArray); Log.i(Constants.TAG, "Completed MonoChromatic CV"); // System.exit(0); return monochromatic_image; }
From source file:org.ar.rubik.MonoChromatic.java
License:Open Source License
/** * Simple algorithm in Java. Java byte arrays of the original image * are obtain and operated on to then produce a resulting Java byte * array./*from w w w. j a va2 s . c o m*/ * * * @param original_image * @return */ private static Mat monochromaticMedianImageFilterBruteForceInJava(Mat original_image) { final Size imageSize = original_image.size(); Mat monochromatic_image = new Mat(imageSize, CvType.CV_8UC1); Mat hsv_image = new Mat(imageSize, CvType.CV_8UC3); Imgproc.cvtColor(original_image, hsv_image, Imgproc.COLOR_RGB2HLS); // Log.i(Constants.TAG, "HSV Image: " + hsv_image); // CV_8UC3 // Try RGB below // hsv_image = result; // Get hue channel into simple byte array for speed efficiency. final int numColumns = (int) original_image.size().width; final int numRows = (int) original_image.size().height; List<Mat> channels = new LinkedList<Mat>(); Core.split(hsv_image, channels); Mat hueMat = channels.get(0); Mat lumMat = channels.get(1); Mat satMat = channels.get(2); final int bufferSize = numColumns * numRows; byte[] hueByteArray = new byte[bufferSize]; byte[] lumByteArray = new byte[bufferSize]; byte[] satByteArray = new byte[bufferSize]; hueMat.get(0, 0, hueByteArray); // get all the pixels lumMat.get(0, 0, lumByteArray); // get all the pixels satMat.get(0, 0, satByteArray); // get all the pixels // Output byte array for speed efficiency byte[] monochromaticByteArray = new byte[bufferSize]; for (int row = 0; row < numRows; row++) { final int span = (int) 7; final int accuracy = (int) 5; byte result_pixel = 0; for (int col = 0; col < numColumns; col++) { if (col < span) result_pixel = 0; // Just put in black else if (row < span) result_pixel = 0; // Just put in black else { int hue_min = 255; int hue_max = 0; int lum_min = 255; int lum_max = 0; // int sat_min = 255; // int sat_max = 0; for (int i = 0; i < span; i++) { for (int j = 0; j < span; j++) { int hue = (int) hueByteArray[(row - j) * numColumns + (col - i)] & 0xFF; if (hue > hue_max) hue_max = hue; if (hue < hue_min) hue_min = hue; int lum = (int) lumByteArray[(row - j) * numColumns + (col - i)] & 0xFF; if (lum > lum_max) lum_max = lum; if (lum < lum_min) lum_min = lum; // =+= Saturation does not look correct when veiw as gray scale image. Not sure what is going on. // int sat = (int)satByteArray[row * numColumns + (col - i) ] & 0xFF; // if(sat > sat_max) // sat_max = sat; // if(sat < sat_min) // sat_min = sat; } // End of row min/max sweep } // End of column min/max sweep if ((hue_max - hue_min < accuracy)) //&& (lum_max - lum_min < accuracy) && (sat_max - sat_min < accuracy) ) result_pixel = (byte) 128; else result_pixel = (byte) 0; // Eliminate all black areas from consideration even if they are very flat. // For some reason, keying off minimum lumosity works best. if (lum_min < 30) result_pixel = 0; // Log.i(Constants.TAG, String.format("Lum %d %d", lum_min, lum_max)); } // End of else if ((col >= span / 2) && (row >= span / 2)) monochromaticByteArray[(row - span / 2) * numColumns + (col - span / 2)] = result_pixel; // int test = (int)(satByteArray[row * numColumns + col]) & 0xFF; // monochromaticByteArray[row * numColumns + (col - span/2)] = (byte) test; } // End of column sweep } // End of row sweep monochromatic_image.put(0, 0, monochromaticByteArray); return monochromatic_image; }
From source file:org.lasarobotics.vision.ftc.resq.Beacon.java
License:Open Source License
/** * Analyze the current frame using the selected analysis method, using custom color blob detectors * * @param redDetector Red color blob detector * @param blueDetector Blue color blob detector * @param img Image to analyze// ww w. j a va 2s.c o m * @param gray Grayscale image to analyze * @param orientation Screen orientation compensation, given by the android.Sensors class * @return Beacon analysis class */ public BeaconAnalysis analyzeFrame(ColorBlobDetector redDetector, ColorBlobDetector blueDetector, Mat img, Mat gray, ScreenOrientation orientation) { if (this.bounds == null) this.bounds = new Rectangle(img.size()); switch (method) { case REALTIME: blueDetector.process(img); redDetector.process(img); return BeaconAnalyzer.analyze_REALTIME(redDetector.getContours(), blueDetector.getContours(), img, orientation, this.debug); case FAST: case DEFAULT: default: return BeaconAnalyzer.analyze_FAST(redDetector, blueDetector, img, gray, orientation, this.bounds, this.debug); case COMPLEX: blueDetector.process(img); redDetector.process(img); return BeaconAnalyzer.analyze_COMPLEX(redDetector.getContours(), blueDetector.getContours(), img, gray, orientation, this.bounds, this.debug); } }
From source file:org.lasarobotics.vision.ftc.resq.BeaconAnalyzer.java
License:Open Source License
static Beacon.BeaconAnalysis analyze_FAST(ColorBlobDetector detectorRed, ColorBlobDetector detectorBlue, Mat img, Mat gray, ScreenOrientation orientation, Rectangle bounds, boolean debug) { //Figure out which way to read the image double orientationAngle = orientation.getAngle(); boolean swapLeftRight = orientationAngle >= 180; //swap if LANDSCAPE_WEST or PORTRAIT_REVERSE boolean readOppositeAxis = orientation == ScreenOrientation.PORTRAIT || orientation == ScreenOrientation.PORTRAIT_REVERSE; //read other axis if any kind of portrait //Bound the image if (readOppositeAxis) //Force the analysis box to transpose inself in place //noinspection SuspiciousNameCombination bounds = new Rectangle(new Point(bounds.center().y / img.height() * img.width(), bounds.center().x / img.width() * img.height()), bounds.height(), bounds.width()) .clip(new Rectangle(img.size())); if (!swapLeftRight && readOppositeAxis) //Force the analysis box to flip across its primary axis bounds = new Rectangle( new Point((img.size().width / 2) + Math.abs(bounds.center().x - (img.size().width / 2)), bounds.center().y), bounds.width(), bounds.height()); else if (swapLeftRight && !readOppositeAxis) //Force the analysis box to flip across its primary axis bounds = new Rectangle(new Point(bounds.center().x, img.size().height - bounds.center().y), bounds.width(), bounds.height()); bounds = bounds.clip(new Rectangle(img.size())); //Get contours within the bounds detectorRed.process(img);/*from ww w.java 2 s. c o m*/ detectorBlue.process(img); List<Contour> contoursRed = detectorRed.getContours(); List<Contour> contoursBlue = detectorBlue.getContours(); //DEBUG Draw contours before filtering if (debug) Drawing.drawContours(img, contoursRed, new ColorRGBA("#FF0000"), 2); if (debug) Drawing.drawContours(img, contoursBlue, new ColorRGBA("#0000FF"), 2); if (debug) Drawing.drawRectangle(img, bounds, new ColorRGBA("#aaaaaa"), 4); //Get the largest contour in each - we're calling this one the main light int largestIndexRed = findLargestIndexInBounds(contoursRed, bounds); int largestIndexBlue = findLargestIndexInBounds(contoursBlue, bounds); Contour largestRed = (largestIndexRed != -1) ? contoursRed.get(largestIndexRed) : null; Contour largestBlue = (largestIndexBlue != -1) ? contoursBlue.get(largestIndexBlue) : null; //If we don't have a main light for one of the colors, we know both colors are the same //TODO we should re-filter the contours by size to ensure that we get at least a decent size if (largestRed == null && largestBlue == null) return new Beacon.BeaconAnalysis(); //INFO The best contour from each color (if available) is selected as red and blue //INFO The two best contours are then used to calculate the location of the beacon //If we don't have a main light for one of the colors, we know both colors are the same //TODO we should re-filter the contours by size to ensure that we get at least a decent size //If the largest part of the non-null color is wider than a certain distance, then both are bright //Otherwise, only one may be lit //If only one is lit, and is wider than a certain distance, it is bright //TODO We are currently assuming that the beacon cannot be in a "bright" state if (largestRed == null) return new Beacon.BeaconAnalysis(); else if (largestBlue == null) return new Beacon.BeaconAnalysis(); //The height of the beacon on screen is the height of the best contour Contour largestHeight = ((largestRed.size().height) > (largestBlue.size().height)) ? largestRed : largestBlue; double beaconHeight = largestHeight.size().height; //Get beacon width on screen by extrapolating from height final double beaconActualHeight = Constants.BEACON_HEIGHT; //cm, only the lit up portion - 14.0 for entire final double beaconActualWidth = Constants.BEACON_WIDTH; //cm final double beaconWidthHeightRatio = Constants.BEACON_WH_RATIO; double beaconWidth = beaconHeight * beaconWidthHeightRatio; Size beaconSize = new Size(beaconWidth, beaconHeight); //Look at the locations of the largest contours //Check to see if the largest red contour is more left-most than the largest right contour //If it is, then we know that the left beacon is red and the other blue, and vice versa Point bestRedCenter = largestRed.centroid(); Point bestBlueCenter = largestBlue.centroid(); //DEBUG R/B text if (debug) Drawing.drawText(img, "R", bestRedCenter, 1.0f, new ColorRGBA(255, 0, 0)); if (debug) Drawing.drawText(img, "B", bestBlueCenter, 1.0f, new ColorRGBA(0, 0, 255)); //Test which side is red and blue //If the distance between the sides is smaller than a value, then return unknown final int minDistance = (int) (Constants.DETECTION_MIN_DISTANCE * beaconSize.width); //percent of beacon width boolean leftIsRed; Contour leftMostContour, rightMostContour; if (readOppositeAxis) { if (bestRedCenter.y + minDistance < bestBlueCenter.y) { leftIsRed = true; } else if (bestBlueCenter.y + minDistance < bestRedCenter.y) { leftIsRed = false; } else { return new Beacon.BeaconAnalysis(); } } else { if (bestRedCenter.x + minDistance < bestBlueCenter.x) { leftIsRed = true; } else if (bestBlueCenter.x + minDistance < bestRedCenter.x) { leftIsRed = false; } else { return new Beacon.BeaconAnalysis(); } } //Swap left and right if necessary leftIsRed = swapLeftRight != leftIsRed; //Get the left-most best contour (or top-most if axis swapped) (or right-most if L/R swapped) if (readOppositeAxis) { //Get top-most best contour leftMostContour = ((largestRed.topLeft().y) < (largestBlue.topLeft().y)) ? largestRed : largestBlue; //Get bottom-most best contour rightMostContour = ((largestRed.topLeft().y) < (largestBlue.topLeft().y)) ? largestBlue : largestRed; } else { //Get left-most best contour leftMostContour = ((largestRed.topLeft().x) < (largestBlue.topLeft().x)) ? largestRed : largestBlue; //Get the right-most best contour rightMostContour = ((largestRed.topLeft().x) < (largestBlue.topLeft().x)) ? largestBlue : largestRed; } //DEBUG Logging if (debug) Log.d("Beacon", "Orientation: " + orientation + "Angle: " + orientationAngle + " Swap Axis: " + readOppositeAxis + " Swap Direction: " + swapLeftRight); //Swap left and right if necessary //BUGFIX: invert when we swap if (!swapLeftRight) { Contour temp = leftMostContour; leftMostContour = rightMostContour; rightMostContour = temp; } //Now that we have the two contours, let's find ellipses that match //Draw the box surrounding both contours //Get the width of the contours double widthBeacon = rightMostContour.right() - leftMostContour.left(); //Center of contours is the average of centroids of the contours Point center = new Point((leftMostContour.centroid().x + rightMostContour.centroid().x) / 2, (leftMostContour.centroid().y + rightMostContour.centroid().y) / 2); //Get the combined height of the contours double heightContours = Math.max(leftMostContour.bottom(), rightMostContour.bottom()) - Math.min(leftMostContour.top(), rightMostContour.top()); //The largest size ratio of tested over actual is the scale ratio double scale = Math.max(widthBeacon / beaconActualWidth, heightContours / beaconActualHeight); //Define size of bounding box by scaling the actual beacon size Size beaconSizeFinal = new Size(beaconActualWidth * scale, beaconActualHeight * scale); //Swap x and y if we rotated the view if (readOppositeAxis) { //noinspection SuspiciousNameCombination beaconSizeFinal = new Size(beaconSizeFinal.height, beaconSizeFinal.width); } //Get points of the bounding box Point beaconTopLeft = new Point(center.x - (beaconSizeFinal.width / 2), center.y - (beaconSizeFinal.height / 2)); Point beaconBottomRight = new Point(center.x + (beaconSizeFinal.width / 2), center.y + (beaconSizeFinal.height / 2)); Rectangle boundingBox = new Rectangle(new Rect(beaconTopLeft, beaconBottomRight)); //Get ellipses in region of interest //Make sure the rectangles don't leave the image size Rectangle leftRect = leftMostContour.getBoundingRectangle().clip(new Rectangle(img.size())); Rectangle rightRect = rightMostContour.getBoundingRectangle().clip(new Rectangle(img.size())); Mat leftContourImg = gray.submat((int) leftRect.top(), (int) leftRect.bottom(), (int) leftRect.left(), (int) leftRect.right()); Mat rightContourImg = gray.submat((int) rightRect.top(), (int) rightRect.bottom(), (int) rightRect.left(), (int) rightRect.right()); //Locate ellipses in the image to process contours against List<Ellipse> ellipsesLeft = PrimitiveDetection.locateEllipses(leftContourImg).getEllipses(); Detectable.offset(ellipsesLeft, new Point(leftRect.left(), leftRect.top())); List<Ellipse> ellipsesRight = PrimitiveDetection.locateEllipses(rightContourImg).getEllipses(); Detectable.offset(ellipsesRight, new Point(rightRect.left(), rightRect.top())); //Score ellipses BeaconScoringCOMPLEX scorer = new BeaconScoringCOMPLEX(img.size()); List<BeaconScoringCOMPLEX.ScoredEllipse> scoredEllipsesLeft = scorer.scoreEllipses(ellipsesLeft, null, null, gray); scoredEllipsesLeft = filterEllipses(scoredEllipsesLeft); ellipsesLeft = BeaconScoringCOMPLEX.ScoredEllipse.getList(scoredEllipsesLeft); if (debug) Drawing.drawEllipses(img, ellipsesLeft, new ColorRGBA("#00ff00"), 3); List<BeaconScoringCOMPLEX.ScoredEllipse> scoredEllipsesRight = scorer.scoreEllipses(ellipsesRight, null, null, gray); scoredEllipsesRight = filterEllipses(scoredEllipsesRight); ellipsesRight = BeaconScoringCOMPLEX.ScoredEllipse.getList(scoredEllipsesRight); if (debug) Drawing.drawEllipses(img, ellipsesRight, new ColorRGBA("#00ff00"), 3); //Calculate ellipse center if present Point centerLeft; Point centerRight; boolean done = false; do { centerLeft = null; centerRight = null; if (scoredEllipsesLeft.size() > 0) centerLeft = scoredEllipsesLeft.get(0).ellipse.center(); if (scoredEllipsesRight.size() > 0) centerRight = scoredEllipsesRight.get(0).ellipse.center(); //Flip axis if necesary if (centerLeft != null && readOppositeAxis) { centerLeft.set(new double[] { centerLeft.y, centerLeft.x }); } if (centerRight != null && readOppositeAxis) { centerRight.set(new double[] { centerRight.y, centerRight.x }); } //Make very, very sure that we didn't just find the same ellipse if (centerLeft != null && centerRight != null) { if (Math.abs(centerLeft.x - centerRight.x) < Constants.ELLIPSE_MIN_DISTANCE * beaconSize.width) { //Are both ellipses on the left or right side of the beacon? - remove the opposite side's ellipse if (Math.abs(centerLeft.x - leftRect.center().x) < Constants.ELLIPSE_MIN_DISTANCE * beaconSize.width) scoredEllipsesRight.remove(0); else scoredEllipsesLeft.remove(0); } else done = true; } else done = true; } while (!done); //Improve the beacon center if both ellipses present byte ellipseExtrapolated = 0; if (centerLeft != null && centerRight != null) { if (readOppositeAxis) center.y = (centerLeft.x + centerRight.x) / 2; else center.x = (centerLeft.x + centerRight.x) / 2; } //Extrapolate other ellipse location if one present //FIXME: This method of extrapolation may not work when readOppositeAxis is true else if (centerLeft != null) { ellipseExtrapolated = 2; if (readOppositeAxis) centerRight = new Point(centerLeft.x - 2 * Math.abs(center.x - centerLeft.x), (centerLeft.y + center.y) / 2); else centerRight = new Point(centerLeft.x + 2 * Math.abs(center.x - centerLeft.x), (centerLeft.y + center.y) / 2); if (readOppositeAxis) centerRight.set(new double[] { centerRight.y, centerRight.x }); } else if (centerRight != null) { ellipseExtrapolated = 1; if (readOppositeAxis) centerLeft = new Point(centerRight.x + 2 * Math.abs(center.x - centerRight.x), (centerRight.y + center.y) / 2); else centerLeft = new Point(centerRight.x - 2 * Math.abs(center.x - centerRight.x), (centerRight.y + center.y) / 2); if (readOppositeAxis) centerLeft.set(new double[] { centerLeft.y, centerLeft.x }); } //Draw center locations if (debug) Drawing.drawCross(img, center, new ColorRGBA("#ffffff"), 10, 4); if (debug && centerLeft != null) { ColorRGBA c = ellipseExtrapolated != 1 ? new ColorRGBA("#ffff00") : new ColorRGBA("#ff00ff"); //noinspection SuspiciousNameCombination Drawing.drawCross(img, !readOppositeAxis ? centerLeft : new Point(centerLeft.y, centerLeft.x), c, 8, 3); } if (debug && centerRight != null) { ColorRGBA c = ellipseExtrapolated != 2 ? new ColorRGBA("#ffff00") : new ColorRGBA("#ff00ff"); //noinspection SuspiciousNameCombination Drawing.drawCross(img, !readOppositeAxis ? centerRight : new Point(centerRight.y, centerRight.x), c, 8, 3); } //Draw the rectangle containing the beacon if (debug) Drawing.drawRectangle(img, boundingBox, new ColorRGBA(0, 255, 0), 4); //Tell us the height of the beacon //TODO later we can get the distance away from the beacon based on its height and position //Remove the largest index and look for the next largest //If the next largest is (mostly) within the area of the box, then merge it in with the largest //Check if the size of the largest contour(s) is about twice the size of the other //This would indicate one is brightly lit and the other is not //If this is not true, then neither part of the beacon is highly lit //Get confidence approximation double WH_ratio = widthBeacon / beaconHeight; double ratioError = Math.abs((Constants.BEACON_WH_RATIO - WH_ratio)) / Constants.BEACON_WH_RATIO; // perfect value = 0; double averageHeight = (leftMostContour.height() + rightMostContour.height()) / 2.0; double dy = Math.abs((leftMostContour.centroid().y - rightMostContour.centroid().y) / averageHeight * Constants.FAST_HEIGHT_DELTA_FACTOR); double dArea = Math.sqrt(leftMostContour.area() / rightMostContour.area()); double buttonsdy = (centerLeft != null && centerRight != null) ? (Math.abs(centerLeft.y - centerRight.y) / averageHeight * Constants.FAST_HEIGHT_DELTA_FACTOR) : Constants.ELLIPSE_PRESENCE_BIAS; double confidence = MathUtil.normalPDFNormalized( MathUtil.distance(MathUtil.distance(MathUtil.distance(ratioError, dy), dArea), buttonsdy) / Constants.FAST_CONFIDENCE_ROUNDNESS, Constants.FAST_CONFIDENCE_NORM, 0.0); //Get button ellipses Ellipse leftEllipse = scoredEllipsesLeft.size() > 0 ? scoredEllipsesLeft.get(0).ellipse : null; Ellipse rightEllipse = scoredEllipsesRight.size() > 0 ? scoredEllipsesRight.get(0).ellipse : null; //Test for color switching if (leftEllipse != null && rightEllipse != null && leftEllipse.center().x > rightEllipse.center().x) { Ellipse tE = leftEllipse; leftEllipse = rightEllipse; rightEllipse = tE; } else if ((leftEllipse != null && leftEllipse.center().x > center.x) || (rightEllipse != null && rightEllipse.center().x < center.x)) { Ellipse tE = leftEllipse; leftEllipse = rightEllipse; rightEllipse = tE; } //Axis correction for ellipses if (swapLeftRight) { if (leftEllipse != null) leftEllipse = new Ellipse( new RotatedRect(new Point(img.width() - leftEllipse.center().x, leftEllipse.center().y), leftEllipse.size(), leftEllipse.angle())); if (rightEllipse != null) rightEllipse = new Ellipse( new RotatedRect(new Point(img.width() - rightEllipse.center().x, rightEllipse.center().y), rightEllipse.size(), rightEllipse.angle())); //Swap again after correcting axis to ensure left is left and right is right Ellipse tE = leftEllipse; leftEllipse = rightEllipse; rightEllipse = tE; } //Switch axis if necessary if (readOppositeAxis) boundingBox = boundingBox.transpose(); if (leftIsRed) return new Beacon.BeaconAnalysis(Beacon.BeaconColor.RED, Beacon.BeaconColor.BLUE, boundingBox, confidence, leftEllipse, rightEllipse); else return new Beacon.BeaconAnalysis(Beacon.BeaconColor.BLUE, Beacon.BeaconColor.RED, boundingBox, confidence, leftEllipse, rightEllipse); }
From source file:org.lasarobotics.vision.ftc.resq.BeaconAnalyzer.java
License:Open Source License
static Beacon.BeaconAnalysis analyze_COMPLEX(List<Contour> contoursRed, List<Contour> contoursBlue, Mat img, Mat gray, ScreenOrientation orientation, Rectangle bounds, boolean debug) { //The idea behind the SmartScoring algorithm is that the largest score in each contour/ellipse set will become the best //DONE First, ellipses and contours are are detected and pre-filtered to remove eccentricities //Second, ellipses, and contours are scored independently based on size and color ... higher score is better //Third, comparative analysis is used on each ellipse and contour to create a score for the contours //Ellipses within rectangles strongly increase in value //Ellipses without nearby/contained contours are removed //Ellipses with nearby/contained contours associate themselves with the contour //Pairs of ellipses (those with similar size and x-position) greatly increase the associated contours' value //Contours without nearby/contained ellipses lose value //Contours near another contour of the opposite color increase in value //Contours and ellipses near the expected area (if any expected area) increase in value //Finally, a fraction of the ellipse value is added to the value of the contour //The best ellipse is found first, then only this ellipse adds to the value //The best contour from each color (if available) is selected as red and blue //The two best contours are then used to calculate the location of the beacon //TODO Filter out bad contours - filtering currently ignored //DEBUG Draw contours before filtering if (debug)/*from w ww . j av a2 s. co m*/ Drawing.drawContours(img, contoursRed, new ColorRGBA("#FFCDD2"), 2); if (debug) Drawing.drawContours(img, contoursBlue, new ColorRGBA("#BBDEFB"), 2); //Score contours BeaconScoringCOMPLEX scorer = new BeaconScoringCOMPLEX(img.size()); List<BeaconScoringCOMPLEX.ScoredContour> scoredContoursRed = scorer.scoreContours(contoursRed, null, null, img, gray); List<BeaconScoringCOMPLEX.ScoredContour> scoredContoursBlue = scorer.scoreContours(contoursBlue, null, null, img, gray); //DEBUG Draw red and blue contours after filtering if (debug) Drawing.drawContours(img, BeaconScoringCOMPLEX.ScoredContour.getList(scoredContoursRed), new ColorRGBA(255, 0, 0), 2); if (debug) Drawing.drawContours(img, BeaconScoringCOMPLEX.ScoredContour.getList(scoredContoursBlue), new ColorRGBA(0, 0, 255), 2); //Locate ellipses in the image to process contours against //Each contour must have an ellipse of correct specification PrimitiveDetection primitiveDetection = new PrimitiveDetection(); PrimitiveDetection.EllipseLocationResult ellipseLocationResult = PrimitiveDetection.locateEllipses(gray); //Filter out bad ellipses - TODO filtering currently ignored List<Ellipse> ellipses = ellipseLocationResult.getEllipses(); //DEBUG Ellipse data before filtering //Drawing.drawEllipses(img, ellipses, new ColorRGBA("#ff0745"), 1); //Score ellipses List<BeaconScoringCOMPLEX.ScoredEllipse> scoredEllipses = scorer.scoreEllipses(ellipses, null, null, gray); //DEBUG Ellipse data after filtering if (debug) Drawing.drawEllipses(img, BeaconScoringCOMPLEX.ScoredEllipse.getList(scoredEllipses), new ColorRGBA("#FFC107"), 2); //DEBUG draw top 5 ellipses if (scoredEllipses.size() > 0 && debug) { Drawing.drawEllipses(img, BeaconScoringCOMPLEX.ScoredEllipse.getList( scoredEllipses.subList(0, scoredEllipses.size() > 5 ? 5 : scoredEllipses.size())), new ColorRGBA("#d0ff00"), 3); Drawing.drawEllipses(img, BeaconScoringCOMPLEX.ScoredEllipse.getList( scoredEllipses.subList(0, scoredEllipses.size() > 3 ? 3 : scoredEllipses.size())), new ColorRGBA("#00ff00"), 3); } //Third, comparative analysis is used on each ellipse and contour to create a score for the contours BeaconScoringCOMPLEX.MultiAssociatedContours associations = scorer.scoreAssociations(scoredContoursRed, scoredContoursBlue, scoredEllipses); double score = (associations.blueContours.size() > 0 ? associations.blueContours.get(0).score : 0) + (associations.redContours.size() > 0 ? associations.redContours.get(0).score : 0); double confidence = score / Constants.CONFIDENCE_DIVISOR; //INFO The best contour from each color (if available) is selected as red and blue //INFO The two best contours are then used to calculate the location of the beacon //Get the best contour in each (starting with the largest) if any contours exist //We're calling this one the main light Contour bestRed = (associations.redContours.size() > 0) ? associations.redContours.get(0).contour.contour : null; Contour bestBlue = (associations.blueContours.size() > 0) ? associations.blueContours.get(0).contour.contour : null; Ellipse bestRedEllipse = (associations.redContours.size() > 0 && associations.redContours.get(0).ellipses.size() > 0) ? associations.redContours.get(0).ellipses.get(0).ellipse : null; Ellipse bestBlueEllipse = (associations.blueContours.size() > 0 && associations.blueContours.get(0).ellipses.size() > 0) ? associations.blueContours.get(0).ellipses.get(0).ellipse : null; //If we don't have a main light for one of the colors, we know both colors are the same //TODO we should re-filter the contours by size to ensure that we get at least a decent size if (bestRed == null && bestBlue == null) return new Beacon.BeaconAnalysis(Beacon.BeaconColor.UNKNOWN, Beacon.BeaconColor.UNKNOWN, new Rectangle(), 0.0f); //TODO rotate image based on camera rotation here //The height of the beacon on screen is the height of the best contour Contour largestHeight = ((bestRed != null ? bestRed.size().height : 0) > (bestBlue != null ? bestBlue.size().height : 0)) ? bestRed : bestBlue; assert largestHeight != null; double beaconHeight = largestHeight.size().height; //Get beacon width on screen by extrapolating from height final double beaconActualHeight = Constants.BEACON_HEIGHT; //cm, only the lit up portion - 14.0 for entire final double beaconActualWidth = Constants.BEACON_WIDTH; //cm final double beaconWidthHeightRatio = Constants.BEACON_WH_RATIO; double beaconWidth = beaconHeight * beaconWidthHeightRatio; Size beaconSize = new Size(beaconWidth, beaconHeight); //If the largest part of the non-null color is wider than a certain distance, then both are bright //Otherwise, only one may be lit //If only one is lit, and is wider than a certain distance, it is bright //TODO We are currently assuming that the beacon cannot be in a "bright" state if (bestRed == null) return new Beacon.BeaconAnalysis(); else if (bestBlue == null) return new Beacon.BeaconAnalysis(); //Look at the locations of the largest contours //Check to see if the largest red contour is more left-most than the largest right contour //If it is, then we know that the left beacon is red and the other blue, and vice versa Point bestRedCenter = bestRed.centroid(); Point bestBlueCenter = bestBlue.centroid(); //DEBUG R/B text if (debug) Drawing.drawText(img, "R", bestRedCenter, 1.0f, new ColorRGBA(255, 0, 0)); if (debug) Drawing.drawText(img, "B", bestBlueCenter, 1.0f, new ColorRGBA(0, 0, 255)); //Test which side is red and blue //If the distance between the sides is smaller than a value, then return unknown final int minDistance = (int) (Constants.DETECTION_MIN_DISTANCE * beaconSize.width); //percent of beacon width //Figure out which way to read the image double orientationAngle = orientation.getAngle(); boolean swapLeftRight = orientationAngle >= 180; //swap if LANDSCAPE_WEST or PORTRAIT_REVERSE boolean readOppositeAxis = orientation == ScreenOrientation.PORTRAIT || orientation == ScreenOrientation.PORTRAIT_REVERSE; //read other axis if any kind of portrait boolean leftIsRed; Contour leftMostContour, rightMostContour; Ellipse leftEllipse, rightEllipse; if (readOppositeAxis) { if (bestRedCenter.y + minDistance < bestBlueCenter.y) { leftIsRed = true; } else if (bestBlueCenter.y + minDistance < bestRedCenter.y) { leftIsRed = false; } else { return new Beacon.BeaconAnalysis(); } } else { if (bestRedCenter.x + minDistance < bestBlueCenter.x) { leftIsRed = true; } else if (bestBlueCenter.x + minDistance < bestRedCenter.x) { leftIsRed = false; } else { return new Beacon.BeaconAnalysis(); } } //Swap left and right if necessary leftIsRed = swapLeftRight != leftIsRed; //Get the left-most best contour (or top-most if axis swapped) (or right-most if L/R swapped) if (readOppositeAxis) { //Get top-most best contour leftMostContour = ((bestRed.topLeft().y) < (bestBlue.topLeft().y)) ? bestRed : bestBlue; leftEllipse = ((bestRed.topLeft().y) < (bestBlue.topLeft().y)) ? bestRedEllipse : bestBlueEllipse; //Get bottom-most best contour rightMostContour = ((bestRed.topLeft().y) < (bestBlue.topLeft().y)) ? bestBlue : bestRed; rightEllipse = ((bestRed.topLeft().y) < (bestBlue.topLeft().y)) ? bestBlueEllipse : bestRedEllipse; } else { //Get left-most best contour leftMostContour = ((bestRed.topLeft().y) < (bestBlue.topLeft().y)) ? bestRed : bestBlue; leftEllipse = ((bestRed.topLeft().y) < (bestBlue.topLeft().y)) ? bestRedEllipse : bestBlueEllipse; //Get the right-most best contour rightMostContour = ((bestRed.topLeft().y) < (bestBlue.topLeft().y)) ? bestBlue : bestRed; rightEllipse = ((bestRed.topLeft().y) < (bestBlue.topLeft().y)) ? bestBlueEllipse : bestRedEllipse; } //Swap left and right if necessary //BUGFIX: invert when we swap if (swapLeftRight) { Contour temp = leftMostContour; leftMostContour = rightMostContour; rightMostContour = temp; Ellipse tE = leftEllipse; leftEllipse = rightEllipse; rightEllipse = tE; } //Draw the box surrounding both contours //Get the width of the contours double widthBeacon = rightMostContour.right() - leftMostContour.left(); //Center of contours is the average of centers of the contours Point center = new Point((leftMostContour.centroid().x + rightMostContour.centroid().x) / 2, (leftMostContour.centroid().y + rightMostContour.centroid().y) / 2); //Get the combined height of the contours double heightContours = Math.max(leftMostContour.bottom(), rightMostContour.bottom()) - Math.min(leftMostContour.top(), rightMostContour.top()); //The largest size ratio of tested over actual is the scale ratio double scale = Math.max(widthBeacon / beaconActualWidth, heightContours / beaconActualHeight); //Define size of bounding box by scaling the actual beacon size Size beaconSizeFinal = new Size(beaconActualWidth * scale, beaconActualHeight * scale); //Swap x and y if we rotated the view if (readOppositeAxis) { //noinspection SuspiciousNameCombination beaconSizeFinal = new Size(beaconSizeFinal.height, beaconSizeFinal.width); } //Get points of the bounding box Point beaconTopLeft = new Point(center.x - (beaconSizeFinal.width / 2), center.y - (beaconSizeFinal.height / 2)); Point beaconBottomRight = new Point(center.x + (beaconSizeFinal.width / 2), center.y + (beaconSizeFinal.height / 2)); Rectangle boundingBox = new Rectangle(new Rect(beaconTopLeft, beaconBottomRight)); //Draw the rectangle containing the beacon if (debug) Drawing.drawRectangle(img, boundingBox, new ColorRGBA(0, 255, 0), 4); //Tell us the height of the beacon //TODO later we can get the distance away from the beacon based on its height and position //Remove the largest index and look for the next largest //If the next largest is (mostly) within the area of the box, then merge it in with the largest //Check if the size of the largest contour(s) is about twice the size of the other //This would indicate one is brightly lit and the other is not //Draw some more debug info if (debug) Drawing.drawCross(img, center, new ColorRGBA("#ffffff"), 10, 4); if (debug && leftEllipse != null) Drawing.drawCross(img, leftEllipse.center(), new ColorRGBA("#ffff00"), 8, 3); if (debug && rightEllipse != null) Drawing.drawCross(img, rightEllipse.center(), new ColorRGBA("#ffff00"), 8, 3); //Test for color switching if (leftEllipse != null && rightEllipse != null && leftEllipse.center().x > rightEllipse.center().x) { Ellipse tE = leftEllipse; leftEllipse = rightEllipse; rightEllipse = tE; } else if ((leftEllipse != null && leftEllipse.center().x > center.x) || (rightEllipse != null && rightEllipse.center().x < center.x)) { Ellipse tE = leftEllipse; leftEllipse = rightEllipse; rightEllipse = tE; } //Axis correction for ellipses if (swapLeftRight) { if (leftEllipse != null) leftEllipse = new Ellipse( new RotatedRect(new Point(img.width() - leftEllipse.center().x, leftEllipse.center().y), leftEllipse.size(), leftEllipse.angle())); if (rightEllipse != null) rightEllipse = new Ellipse( new RotatedRect(new Point(img.width() - rightEllipse.center().x, rightEllipse.center().y), rightEllipse.size(), rightEllipse.angle())); //Swap again after correcting axis to ensure left is left and right is right Ellipse tE = leftEllipse; leftEllipse = rightEllipse; rightEllipse = tE; } //Make very, very sure that we didn't just find the same ellipse if (leftEllipse != null && rightEllipse != null) { if (Math.abs(leftEllipse.center().x - rightEllipse.center().x) < Constants.ELLIPSE_MIN_DISTANCE * beaconSize.width) { //Are both ellipses on the left or right side of the beacon? - remove the opposite side's ellipse if (Math.abs(leftEllipse.center().x - leftMostContour.center().x) < Constants.ELLIPSE_MIN_DISTANCE * beaconSize.width) rightEllipse = null; else leftEllipse = null; } } //Switch axis if necessary if (readOppositeAxis) boundingBox = boundingBox.transpose(); //If this is not true, then neither part of the beacon is highly lit if (leftIsRed) return new Beacon.BeaconAnalysis(Beacon.BeaconColor.RED, Beacon.BeaconColor.BLUE, boundingBox, confidence, leftEllipse, rightEllipse); else return new Beacon.BeaconAnalysis(Beacon.BeaconColor.BLUE, Beacon.BeaconColor.RED, boundingBox, confidence, leftEllipse, rightEllipse); }
From source file:org.lasarobotics.vision.ftc.resq.BeaconScoringCOMPLEX.java
License:Open Source License
@SuppressWarnings("unchecked") List<ScoredEllipse> scoreEllipses(List<Ellipse> ellipses, Point estimateLocation, Double estimateDistance, Mat gray) { List<ScoredEllipse> scores = new ArrayList<>(); for (Ellipse ellipse : ellipses) { double score = 1; //Find the eccentricity - the closer it is to 0, the better double eccentricity = ellipse.eccentricity(); double eccentricitySubscore = createSubscore(eccentricity, Constants.ELLIPSE_ECCENTRICITY_BEST, Constants.ELLIPSE_ECCENTRICITY_NORM, Constants.ELLIPSE_ECCENTRICITY_BIAS, false); score *= eccentricitySubscore;/* ww w . j av a2 s . c o m*/ //f(0.3) = 5, f(0.75) = 0 //Find the area - the closer to a certain range, the better double area = ellipse.area() / gray.size().area(); //area as a percentage of the area of the screen //Best value is the root mean squared of the min and max areas final double areaBestValue = Math.sqrt(Constants.ELLIPSE_AREA_MIN * Constants.ELLIPSE_AREA_MIN + Constants.ELLIPSE_AREA_MAX * Constants.ELLIPSE_AREA_MAX) / 2; double areaSubscore = createSubscore(area, areaBestValue, Constants.ELLIPSE_AREA_NORM, Constants.ELLIPSE_AREA_BIAS, true); score *= areaSubscore; //TODO Find the on-screen location - the closer it is to the estimate, the better //Find the color - the more black, the better (significantly) Ellipse e = ellipse.scale(0.5); //get the center 50% of data double averageColor = e.averageColor(gray, ColorSpace.GRAY).getScalar().val[0]; double colorSubscore = createSubscore(averageColor, Constants.ELLIPSE_CONTRAST_THRESHOLD, Constants.ELLIPSE_CONTRAST_NORM, Constants.ELLIPSE_CONTRAST_BIAS, false); score *= colorSubscore; //If score is above a value, keep the ellipse if (score >= Constants.ELLIPSE_SCORE_MIN) scores.add(new ScoredEllipse(ellipse, score)); } return (List<ScoredEllipse>) Scorable.sort(scores); }
From source file:org.lasarobotics.vision.image.Transform.java
License:Open Source License
/** * Rotate an image by an angle (counterclockwise) * * @param image Transform matrix//from w w w.j ava 2 s . c om * @param angle Angle to rotate by (counterclockwise) from -360 to 360 */ public static void rotate(Mat image, double angle) { //Calculate size of new matrix double radians = Math.toRadians(angle); double sin = Math.abs(Math.sin(radians)); double cos = Math.abs(Math.cos(radians)); int newWidth = (int) (image.width() * cos + image.height() * sin); int newHeight = (int) (image.width() * sin + image.height() * cos); // rotating image Point center = new Point(newWidth / 2, newHeight / 2); Mat rotMatrix = Imgproc.getRotationMatrix2D(center, angle, 1.0); //1.0 means 100 % scale Size size = new Size(newWidth, newHeight); Imgproc.warpAffine(image, image, rotMatrix, image.size()); }