Translate Python function to apply mask to image into Java - java

I'm trying to translate the following Python function, that applies a mask to an image, into Java:
# Applies an image mask.
def region_of_interest(img, vertices):
#defining a blank mask to start with
mask = np.zeros_like(img)
#defining a 3 channel or 1 channel color to fill the mask with depending on the input image
if len(img.shape) > 2:
channel_count = img.shape[2] # i.e. 3 or 4 depending on your image
ignore_mask_color = (255,) * channel_count
else:
ignore_mask_color = 255
#filling pixels inside the polygon defined by "vertices" with the fill color
cv2.fillPoly(mask, vertices, ignore_mask_color)
#returning the image only where mask pixels are nonzero
masked_image = cv2.bitwise_and(img, mask)
return masked_image
So far, this is what I've got:
public static opencv_core.Mat applyMask(opencv_core.Mat image, opencv_core.MatVector vertices) {
opencv_core.Mat mask = opencv_core.Mat.zeros(image.size(), opencv_core.CV_8U).asMat();
opencv_core.Scalar color = new opencv_core.Scalar(image.channels()); // 3
double[] colors = new double[] {
255.0, 255.0, 255.0, 255.0,
255.0, 255.0, 255.0, 255.0,
255.0, 255.0, 255.0, 255.0};
color.put(colors, 0, colors.length);
opencv_imgproc.fillPoly(mask, vertices, color);
opencv_core.Mat dst = new opencv_core.Mat();
opencv_core.bitwise_and(image, mask, dst);
return dst;
}
But, it isn't working. When I try invoking this method like in the following example:
opencv_core.MatVector points = new opencv_core.MatVector(
new opencv_core.Mat(2, 3, opencv_core.CV_32F, new IntPointer(1, 2, 3, 4, 5, 6))
);
opencv_core.MatVector vertices = new opencv_core.MatVector(points);
opencv_core.Mat masked = LaneManager.applyMask(src, vertices);
(I'm assuming this is the right way to build a 2x3 matrix of three points with two coordinates each (1,2), (3, 4) and (5,6))
I get an exception:
java.lang.RuntimeException: std::bad_alloc
at org.bytedeco.javacpp.opencv_imgproc.fillPoly(Native Method)
I'm using OpenCV as provided by org.bytedeco.javacpp-presets:opencv-platform:3.2.0-1.3 via Maven Central.
I must admit that I'm at a loss here: What's the idiomatic Java way of doing the same thing as the Python function above?

Alright, I finally figured it out. If you define your coordinates with:
int[] points = new int[] {x1, y1, x2, y2, ...};
Then you can simply apply a mask with the following code:
opencv_core.Mat mask = new opencv_core.Mat(image.size(), image.type());
// Array of polygons where each polygon is represented as an array of points
opencv_core.Point polygon = new opencv_core.Point();
polygon.put(points, 0, points.length);
opencv_imgproc.fillPoly(mask, polygon, new int[] {points.length / 2}, 1, new opencv_core.Scalar(255, 255, 255, 0));
opencv_core.Mat masked = new opencv_core.Mat(image.size(), image.type());
opencv_core.bitwise_and(image, mask, masked);
Where image is the original image, and masked is the masked result.
The problem was that the original list of points wasn't defined properly.

Maybe some whiz has the complete answers. Here is food for thought:
The Java API is a direct copy of the CPP API: http://opencv.org/opencv-java-api.html
The error std::bad_alloc occurs when you fail to allocate required storage space. (http://answers.opencv.org/question/28959/cascade-training-killed-and-bad_alloc/)
There are two CPP methods:
C++: void fillPoly(Mat& img, const Point** pts, const int* npts, int ncontours, const Scalar& color, int lineType=LINE_8, int shift=0, Point offset=Point() ), and
C++: void fillPoly(InputOutputArray img, InputArrayOfArrays pts, const Scalar& color, int lineType=LINE_8, int shift=0, Point offset=Point() )
You don't need to convert from Mat to InputArray, but you can (and should) just pass a Mat object where an InputArray is requested (https://stackoverflow.com/a/32976883/1587329)

Related

How to remove the background of the image of interest using opencv on android? [duplicate]

I am developing an application to detect the lesion area, for this I am using the grabcut to detect the ROI and remove the background from the image. However in some images it is not working well. He ends up not identifying the borders of the region of interest well. The watershed can better identify the edges for this type of work, however I am having difficulties making this transition from grabcut to watershed. Before processing the grabcut, the user uses touchevent to mark a rectangle around the image of interest (wound area) to facilitate the work of the algorithm. As the image below.
However, using other wound images, segmentation is not good, showing flaws in ROI detection.
Image using grabcut in app
Image using watershed in desktop
this is the code:
private fun extractForegroundFromBackground(coordinates: Coordinates, currentPhotoPath: String): String {
// TODO: Provide complex object that has both path and extension
val width = bitmap?.getWidth()!!
val height = bitmap?.getHeight()!!
val rgba = Mat()
val gray_mat = Mat()
val threeChannel = Mat()
Utils.bitmapToMat(bitmap, gray_mat)
cvtColor(gray_mat, rgba, COLOR_RGBA2RGB)
cvtColor(rgba, threeChannel, COLOR_RGB2GRAY)
threshold(threeChannel, threeChannel, 100.0, 255.0, THRESH_OTSU)
val rect = Rect(coordinates.first, coordinates.second)
val fg = Mat(rect.size(), CvType.CV_8U)
erode(threeChannel, fg, Mat(), Point(-1.0, -1.0), 10)
val bg = Mat(rect.size(), CvType.CV_8U)
dilate(threeChannel, bg, Mat(), Point(-1.0, -1.0), 5)
threshold(bg, bg, 1.0, 128.0, THRESH_BINARY_INV)
val markers = Mat(rgba.size(), CvType.CV_8U, Scalar(0.0))
Core.add(fg, bg, markers)
val marker_tempo = Mat()
markers.convertTo(marker_tempo, CvType.CV_32S)
watershed(rgba, marker_tempo)
marker_tempo.convertTo(markers, CvType.CV_8U)
val imgBmpExit = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
Utils.matToBitmap(markers, imgBmpExit)
image.setImageBitmap(imgBmpExit)
// Run the grab cut algorithm with a rectangle (for subsequent iterations with touch-up strokes,
// flag should be Imgproc.GC_INIT_WITH_MASK)
//Imgproc.grabCut(srcImage, firstMask, rect, bg, fg, iterations, Imgproc.GC_INIT_WITH_RECT)
// Create a matrix of 0s and 1s, indicating whether individual pixels are equal
// or different between "firstMask" and "source" objects
// Result is stored back to "firstMask"
//Core.compare(mark, source, mark, Core.CMP_EQ)
// Create a matrix to represent the foreground, filled with white color
val foreground = Mat(srcImage.size(), CvType.CV_8UC3, Scalar(255.0, 255.0, 255.0))
// Copy the foreground matrix to the first mask
srcImage.copyTo(foreground, mark)
// Create a red color
val color = Scalar(255.0, 0.0, 0.0, 255.0)
// Draw a rectangle using the coordinates of the bounding box that surrounds the foreground
rectangle(srcImage, coordinates.first, coordinates.second, color)
// Create a new matrix to represent the background, filled with black color
val background = Mat(srcImage.size(), CvType.CV_8UC3, Scalar(0.0, 0.0, 0.0))
val mask = Mat(foreground.size(), CvType.CV_8UC1, Scalar(255.0, 255.0, 255.0))
// Convert the foreground's color space from BGR to gray scale
cvtColor(foreground, mask, Imgproc.COLOR_BGR2GRAY)
// Separate out regions of the mask by comparing the pixel intensity with respect to a threshold value
threshold(mask, mask, 254.0, 255.0, Imgproc.THRESH_BINARY_INV)
// Create a matrix to hold the final image
val dst = Mat()
// copy the background matrix onto the matrix that represents the final result
background.copyTo(dst)
val vals = Mat(1, 1, CvType.CV_8UC3, Scalar(0.0))
// Replace all 0 values in the background matrix given the foreground mask
background.setTo(vals, mask)
// Add the sum of the background and foreground matrices by applying the mask
Core.add(background, foreground, dst, mask)
// Save the final image to storage
Imgcodecs.imwrite(currentPhotoPath + "_tmp.png", dst)
// Clean up used resources
firstMask.release()
source.release()
//bg.release()
//fg.release()
vals.release()
dst.release()
return currentPhotoPath
}
Exit:
How do I update the code to use watershed instead of grabcut?
A description of how to apply the watershed algorithm in OpenCV is here, although it is in Python. The documentation also contains some potentially useful examples. Since you already have a binary image, all that's left is to apply the Euclidean Distance Transform (EDT) and the watershed function. So instead of Imgproc.grabCut(srcImage, firstMask, rect, bg, fg, iterations, Imgproc.GC_INIT_WITH_RECT), you would have:
Mat dist = new Mat();
Imgproc.distanceTransform(srcImage, dist, Imgproc.DIST_L2, Imgproc.DIST_MASK_3); // use L2 for Euclidean Distance
Mat markers = Mat.zeros(dist.size(), CvType.CV_32S);
Imgproc.watershed(dist, markers); # apply watershed to resultant image from EDT
Mat mark = Mat.zeros(markers.size(), CvType.CV_8U);
markers.convertTo(mark, CvType.CV_8UC1);
Imgproc.threshold(mark, firstMask, 0, 255, Imgproc.THRESH_BINARY + Imgproc.THRESH_OTSU); # threshold results to get binary image
The thresholding step is described here. Also, optionally, before you apply Imgproc.watershed, you may want to apply some morphological operations to the result of EDT i.e; dilation, erosion:
Imgproc.dilate(dist, dist, Mat.ones(3, 3, CvType.CV_8U));
If you're not familiar with morphological operations when it comes to processing binary images, the OpenCV documentation contains some good, quick examples.
Hope this helps!

Detect three nested objects of similar shape in an image

I want to detect an archery target by its colors and made it that far:
Original image and analyzed colors:
I'm getting the circles using color range on a RGB image. As you can see, there are also many other blobs on the images which I want to get rid of.
I tried to compare all shapes in all three images with each other to find the best match by matchShape, but it doesn't result in those three circles I want.
Any ideas how I can find out which contours/shapes/objects on the image are actually the three circles?
The input image might have an angle/perspective, so I can't use hough to find circles.
What I need as a result is a contour of the three circles (outer and inner contour), as I need to do further processing with that data.
This is actually a follow-up of that question: find archery target in image of different perspectives
As it is a more specific question, I created a new one. I'm new here
A follow-up to Miki's answer.
One of the results looks like this:
Contour of blue circle and fitting ellipse
The contour in the binary image and the resulting ellipse in the original image are different.
I would still like to solve that problem. I'm thinking of an algorithm that goes along the contour of the binary image and wherever the contour is broken the algorithm keeps going using the last known radius until it finds the next pixel of the contour. Is there such an algorithm? Or maybe a method to stretch the fitting ellipse at specific points until all parts of the contour are covered?
You can look a Circle Hough Transform algorithm to find all circular objects in Red, Green and Blue channels and then match them.
You can find implementation here or use OpenCV realization.
Finding directly the ellipses in that image can be quite tricky. You can, however, have a look here for a few info and references to code.
In this case, it's much easier to segment the 3 color: blue, red and yellow, find the outer contours, and fit an ellipse to them.
So, on this input image:
So, you first convert your image to HSV, and then apply some thresholds to recover the masks. Using morphological close operations will get rid of some holes, and link nearby blobs.
blue mask:
red mask:
yellow mask:
Then, you can retrieve the external contours from this masks, and keep only the largest (in case you find some smaller blob not belonging to the target).
Now you just need to fit an ellipse to these contours:
Please note that I tried also on the image in your other question. Blue target is deformed and so it's not an ellipse, so fitting an ellipse is not a good choice here:
In this case may be better to use the convex hull of the contours, which will be more robust than the contour itself, if the mask is not perfect (code is given below):
Code:
#include <opencv2/opencv.hpp>
#include <vector>
#include <string>
using namespace std;
using namespace cv;
int main()
{
// Load image
Mat3b img = imread("path_to_image");
// Convert to hsv
Mat3b hsv;
cvtColor(img, hsv, COLOR_BGR2HSV);
// Find masks for different colors
Mat1b blue_mask;
inRange(hsv, Scalar(90, 150, 150), Scalar(110, 255, 255), blue_mask);
Mat1b red_mask;
inRange(hsv, Scalar(160, 150, 100), Scalar(180, 255, 255), red_mask);
Mat1b yellow_mask;
inRange(hsv, Scalar(20, 150, 100), Scalar(30, 255, 255), yellow_mask);
// Apply morphological close
Mat1b kernel = getStructuringElement(MORPH_ELLIPSE, Size(11,11));
morphologyEx(blue_mask, blue_mask, MORPH_CLOSE, kernel);
morphologyEx(red_mask, red_mask, MORPH_CLOSE, kernel);
morphologyEx(yellow_mask, yellow_mask, MORPH_CLOSE, kernel);
// Find largest blob and draw it
vector<Point> blue_contour, red_contour, yellow_contour;
{
vector<vector<Point>> contours;
findContours(blue_mask.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
blue_contour = *max_element(contours.begin(), contours.end(), [](const vector<Point>& lhs, const vector<Point>& rhs){
return contourArea(lhs) < contourArea(rhs); });
}
{
vector<vector<Point>> contours;
findContours(red_mask.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
red_contour = *max_element(contours.begin(), contours.end(), [](const vector<Point>& lhs, const vector<Point>& rhs){
return contourArea(lhs) < contourArea(rhs); });
}
{
vector<vector<Point>> contours;
findContours(yellow_mask.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
yellow_contour = *max_element(contours.begin(), contours.end(), [](const vector<Point>& lhs, const vector<Point>& rhs){
return contourArea(lhs) < contourArea(rhs); });
}
// Fit ellipse
RotatedRect blue_ellipse = fitEllipse(blue_contour);
RotatedRect red_ellipse = fitEllipse(red_contour);
RotatedRect yellow_ellipse = fitEllipse(yellow_contour);
// Draw ellipses
ellipse(img, blue_ellipse, Scalar(255, 0, 0), 3);
ellipse(img, red_ellipse, Scalar(0, 0, 255), 3);
ellipse(img, yellow_ellipse, Scalar(0, 255, 255), 3);
imshow("Result", img);
waitKey();
return 0;
}
Code for convex hull:
// Get convex hulls
vector<Point> blue_hull, red_hull, yellow_hull;
convexHull(blue_contour, blue_hull);
convexHull(red_contour, red_hull);
convexHull(yellow_contour, yellow_hull);
// Draw convex hulls
drawContours(img, vector < vector<Point> > {blue_hull}, 0, Scalar(255,0,0), 3);
drawContours(img, vector < vector<Point> > {red_hull}, 0, Scalar(0, 0, 255), 3);
drawContours(img, vector < vector<Point> > {yellow_hull}, 0, Scalar(0, 255, 255), 3);

Draw grid on transparent Mat

I would like to create a Mat with a grid on a transparent background that I can lay on top of other Mats. I struggle with the transparent part and to laying on top
Mat image = imread("pic.jpg");
Mat grid = new Mat(image.size(), CV_8UC4, new Scalar(0, 0, 0, 0);
for (//times)
// draw grid with: line(grid, ... )
grid.copyTo(image);
First of all the grid Mat is not transparent at all it is black. Isn't scalar constructed like this?
new Scalar(Blue, Green, Red, Alpha)
Also how do I overlay an image with another one? This is just overwriting.
Here is sample program written in C++ but it should be very analogue in java:
cv::Mat input = cv::imread("../inputData/Lenna.png");
cv::Mat inputBGRA;
cv::cvtColor(input, inputBGRA, CV_BGR2BGRA);
cv::Mat gridSolid = cv::Mat(input.size(), inputBGRA.type(), cv::Scalar(0,0,0,0));
cv::Mat gridMask = cv::Mat(input.size(), CV_8UC1, cv::Scalar(0));
cv::Mat gridAlpha = cv::Mat(input.size(), inputBGRA.type(), cv::Scalar(0,0,0,0));
cv::line(gridSolid, cv::Point(0,0), cv::Point(512,512), cv::Scalar(0,255,0,255), 10);
cv::line(gridSolid, cv::Point(0,512), cv::Point(512,0), cv::Scalar(0,255,0,255), 10);
cv::line(gridMask, cv::Point(0,0), cv::Point(512,512), cv::Scalar(255), 10); // single channel
cv::line(gridMask, cv::Point(0,512), cv::Point(512,0), cv::Scalar(255), 10); // single channel
// copy and use the mask. copying eliminates the original values where the mask is set
cv::Mat outputCopy = inputBGRA.clone();
gridSolid.copyTo(outputCopy,gridMask);
// here set the scalar alpha value to less than 255
// both lines use different alpha values
cv::line(gridAlpha, cv::Point(0,0), cv::Point(512,512), cv::Scalar(0,255,0,120), 10);
cv::line(gridAlpha, cv::Point(0,512), cv::Point(512,0), cv::Scalar(0,255,0,180), 10);
cv::Mat outputWeightSum = inputBGRA.clone();
//cv::addWeighted(inputBGRA, 0.5, gridAlpha, 0.5, 0, outputWeightSum);
// manually add weighted sum PER ALPHA VALUE:
for(int y=0; y<outputWeightSum.rows; ++y)
for(int x=0; x<outputWeightSum.cols; ++x)
{
// the bigger the alpha value, the less of the original image is kept at that pixel
cv::Vec4b imgPix = outputWeightSum.at<cv::Vec4b>(y,x);
cv::Vec4b gridPix = gridAlpha.at<cv::Vec4b>(y,x);
// use alpha channel vor blending
float blendpart = (float)gridPix[3]/(float)255;
// set pixel value to blended value
outputWeightSum.at<cv::Vec4b>(y,x) = blendpart * gridPix + (1.0f-blendpart) * imgPix;
}
in fact you dont need the alpha channel in this example but if you have more complex "grids" with differing alpha values, it might be nice.
I get these results:
method: copy:
method: blend with alpha channel:

How to get co-ordinates of line with the help of OpenCv in android

I want to co-ordinates of lines with the help of OpenCV in android. I studied the tutorial and this is what my api call is
Mat ImageMat = new Mat(croppedImage.getHeight(), croppedImage.getWidth(), CvType.CV_8U, new Scalar(4));
int threshold = 50;
int minLineSize = 100;
int lineGap = 20;
Mat lines = new Mat();
Imgproc.HoughLinesP(ImageMat, lines, 1, Math.PI / 180, threshold, minLineSize, lineGap);
I provide a simple image with one line in it but in "lines" variable I get hundreds of co-ordinates. I just one co-ordinate of that single line. How to get co-ordinate of that single line only. Also what is the unit in which minLineSize is measured? My lines are the lines which are in front of FirstName, LastName etc.
Here's code in C++. Since mostly OpenCV functions are used, you might be able to port it do android CV easily:
int main()
{
// loading your image. you dont need theses parts
cv::Mat input = cv::imread("../inputData/FormularLineDetection.png");
// convert to grayscale: you will do something similar:
cv::Mat gray;
cv::cvtColor(input, gray, CV_BGR2GRAY);
// computation of binary thresholding so that dark areas of the image will bevcome "foreground pixel".
// If your image have bright features you'll have to choose different parameters.
// If you want to detect contour lines instead you'll compute gradient magnitude first.
cv::Mat mask;
cv::threshold(gray, mask, 0, 255, CV_THRESH_BINARY_INV | CV_THRESH_OTSU);
std::vector<cv::Vec4i> lines;
//cv::HoughLinesP(mask, lines, 1, CV_PI/180.0, 50, 50, 10 );
// I've changed the min-Size of a line to 1/3 of the images width. Maybe you'll have to adjust that parameter to your needs!
cv::HoughLinesP(mask, lines, 1, CV_PI/180.0, 50, input.cols/3, 10 );
// draw the lines to visualize: you might not do this at all
for( size_t i = 0; i < lines.size(); i++ )
{
cv::Vec4i l = lines[i];
cv::line( input, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0,0,255), 3, CV_AA);
}
// display and save to disk
cv::imshow("mask", mask); // you might not want to display the image here.
cv::imshow("output",input);
cv::imwrite("../outputData/FormularLineDetection.png", input);
cv::waitKey(0);
return 0;
}
with your input I get this output:
as you can see, your desired lines are detected, but in addition that big thick "line" is detected too. You might want to try to detect structures like that and filter them out!

JavaCV Warning sign detection?

I've looked at JavaCV wrapper for OpenCV library and I saw that it is possible to use that library in Java for face detection on an image, but I was wondering is it possible to use that library for detecting traffic warning signs on an image and how?
I have pictures taken from the road that look like this: http://www.4shared.com/photo/5QxoVDwd/041.html
and the result of detection should look sometning like this or similar: http://www.4shared.com/photo/z_pL0lSK/overlay-0.html
EDIT:
After I detect red color I get this image:
And I have a problem detecting just the warning sign triangle shape and ignore all other shapes. I tried changing the cvApproxPoly parameters but with no result. This is my code:
public void myFindContour(IplImage image)
{
IplImage grayImage = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1);
cvCvtColor(image, grayImage, CV_BGR2GRAY);
CvMemStorage mem;
CvSeq contours = new CvSeq();
CvSeq ptr = new CvSeq();
cvThreshold(grayImage, grayImage, 150, 255, CV_THRESH_BINARY);
mem = cvCreateMemStorage(0);
cvFindContours(grayImage, mem, contours, Loader.sizeof(CvContour.class) , CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
Random rand = new Random();
while (contours != null && !contours.isNull()) {
if (contours.elem_size() > 0) {
CvSeq points = cvApproxPoly(contours, Loader.sizeof(CvContour.class),
mem, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);
Color randomColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
CvScalar color = CV_RGB( randomColor.getRed(), randomColor.getGreen(), randomColor.getBlue());
cvDrawContours(image, points, color, CV_RGB(0,0,0), -1, CV_FILLED, 8, cvPoint(0,0));
}
contours = contours.h_next();
}
cvSaveImage("myfindcontour.png", image);
}
This is the output that i get (I used different colors for every shape, but in the final output i will use only white for detected warning sign and everything other left black):
You have to do the following:
Detect red color on image - you will get 1bit image where: 0=non-red, 1=red.
Detect triangles on created in previous step image. You can do that using approxPoly function.
see ,the first find the contour area.
compare it with the precalculated value and keep it with in a range
like
if(area>Mincontourarea && area<maxcontourare)
{
thats it now we have the signboard do
}
the value if calculated wouldnot be bigger than the car conotur,
to get the contoutr
up to my knowledge u need
Moments operator
code for the momnts operator:
Moments moment = moments((cv::Mat)contours[index]);
area = moment.m00; //m00 gives the area of the detected contour
put the above code before the if block discussed above
if you want the x and y coordinates put a post again..
take a look of my answer, it is in c++ but using opencv it is able to detect road signs, you can take it as a good example.
https://stackoverflow.com/a/52651521/8035924

Categories

Resources