I was trying to implement watershed function from OpenCV on Android. However my program always crashed at the place where watershed function is called. I can output the marker's result perfectly fine. But the watershed function always just crashes. Here is my code:
Mat threeChannel = new Mat();
Imgproc.cvtColor(mRgba, threeChannel, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(threeChannel, threeChannel, 100, 255, Imgproc.THRESH_BINARY);
Mat fg = new Mat(mRgba.size(),CvType.CV_8U);
Imgproc.erode(threeChannel,fg,new Mat(),new Point(-1,-1),2);
Mat bg = new Mat(mRgba.size(),CvType.CV_8U);
Imgproc.dilate(threeChannel,bg,new Mat(),new Point(-1,-1),3);
Imgproc.threshold(bg,bg,1, 128,Imgproc.THRESH_BINARY_INV);
Mat markers = new Mat(mRgba.size(),CvType.CV_8U, new Scalar(0));
Core.add(fg, bg, markers);
WatershedSegmenter segmenter = new WatershedSegmenter();
segmenter.setMarkers(markers);
Mat result = segmenter.process(mRgba);
return result;
WatershedSegmenter calss is as follows:
public class WatershedSegmenter{
public Mat markers;
public void setMarkers(Mat markerImage)
{
markerImage.convertTo(markers, CvType.CV_32S);
}
public Mat process(Mat image)
{
Imgproc.watershed(image, markers);
markers.convertTo(markers,CvType.CV_8U);
return markers;
}
}
Has anybody managed to get this working on Android before? I managed to get it to work in C++ with Qt before following this tutorial: link. However I haven't got any luck on Android at the moment.
I found out the reason of crash now. watershed is taking a 8 bit 3 channel format of data, and RGBA is a 4 channel data. I just convert it from RGBA to RGB, and it solved all the issues.
Your Mat doesn't match the correct .depth() and/or .channel().
The first step is to double-check each Mat has the type you think it does by using the myMat.depth() and myMat.channels() functions. The function watershed uses two Mat arguments. The first should be an 8-bit, 3-channel image, and the second should be a 32-bit single-channel image.
If they are not the right kind of image, use cvtColor to convert from what you have to what you need.
Try out this solution
BitmapFactory.Options o = new BitmapFactory.Options();
o.inDither = false;
o.inSampleSize=4;
int width , height ;
width = src_Bitmap.getWidth();
height = src_Bitmap.getHeight();
Mat rgba = new Mat();
Mat gray_mat= new Mat();
Mat threeChannel = new Mat();
Utils.bitmapToMat(src_Bitmap,gray_mat);
Imgproc.cvtColor(gray_mat,rgba , Imgproc.COLOR_RGBA2RGB);
Imgproc.cvtColor(rgba, threeChannel, Imgproc.COLOR_RGB2GRAY);
Imgproc.threshold(threeChannel, threeChannel, 100, 255, Imgproc.THRESH_OTSU);
Mat fg = new Mat(rgba.size(),CvType.CV_8U);
Imgproc.erode(threeChannel,fg,new Mat(),new Point(-1,-1),2);
Mat bg = new Mat(rgba.size(),CvType.CV_8U);
Imgproc.dilate(threeChannel,bg,new Mat(),new Point(-1,-1),3);
Imgproc.threshold(bg,bg,1, 128,Imgproc.THRESH_BINARY_INV);
Mat markers = new Mat(rgba.size(),CvType.CV_8U, new Scalar(0));
Core.add(fg, bg, markers);
// Start the WaterShed Segmentation :
Mat marker_tempo = new Mat();
markers.convertTo(marker_tempo, CvType.CV_32S);
Imgproc.watershed(rgba, marker_tempo);
marker_tempo.convertTo(markers,CvType.CV_8U);
result_Bitmap=Bitmap.createBitmap(width,height,Bitmap.Config.RGB_565);
Imgproc.applyColorMap( markers, markers,4 );
Utils.matToBitmap( markers,result_Bitmap);
myImageView.setImageBitmap(result_Bitmap);
Related
I'm trying to develop simple application (OpenCv, Tesseract and Java) where i need to get numbers from a photo of water meter. I am newbie to OpenCV and i am stuck on detection of numbers in rectangles.
So i want to achieve "00295" value as result.
Here is a example of water meter
But i am not able to achieve this result.
Steps:
Apply Gray filter
GaussianBlur filter 3x3
Sobel filter Threshold
And doing OCR with number characters allowed only
But in result i get bunch of random numbers from other labels.
Can you please give some suggestions and show way how to detect this 5 rectangles and get digits from them ?
Thanks in advance.
Here is code:
private static final int
CV_THRESH_OTSU = 8;
public static void main(String[] args) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat img = new Mat();
Mat imgGray = new Mat();
Mat imgGaussianBlur = new Mat();
Mat imgSobel = new Mat();
Mat imgThreshold = new Mat();
//Path to picture
String inputFilePath = "D:/OCR/test.jpg";
img = Imgcodecs.imread(inputFilePath);
Imgcodecs.imwrite("preprocess/1_True_Image.png", img);
Imgproc.cvtColor(img, imgGray, Imgproc.COLOR_BGR2GRAY);
Imgcodecs.imwrite("preprocess/2_imgGray.png", imgGray);
Imgproc.GaussianBlur(imgGray,imgGaussianBlur, new Size(3, 3),0);
Imgcodecs.imwrite("preprocess/3_imgGaussianBlur.png", imgGray);
Imgproc.Sobel(imgGaussianBlur, imgSobel, -1, 1, 0);
Imgcodecs.imwrite("preprocess/4_imgSobel.png", imgSobel);
Imgproc.threshold(imgSobel, imgThreshold, 0, 255, CV_THRESH_OTSU);
Imgcodecs.imwrite("preprocess/5_imgThreshold.png", imgThreshold);
File imageFile = new File("preprocess/5_imgThreshold.png");
Tesseract tesseract = new Tesseract();
//tessdata directory
tesseract.setDatapath("tessdata");
tesseract.setTessVariable("tessedit_char_whitelist", "0123456789");
try {
String result = tesseract.doOCR(imageFile);
System.out.println(result);
} catch (TesseractException e) {
System.err.println(e.getMessage());
}
}
}
If the position of the water filter won't change from image to image, could you just manually crop the image to your desired size? Also, after you blur the image, try using an adaptive threshold followed by canny edge detection. As a result, your image will only have the hard edges present. Then you could find contours on the image and filter through those contours till they fit the desired size that you want.
I'd like to create a black&white image relying on HSV filtering. However, after converting the image from BGR to HSV and applying the inRange() method, the matrix is reduced to a single channel matrix (with values either 0 or 255) and cannot be converted back to BGR.
Is there an easy way to work around this? Do I even need that step of back-conversion or can I somehow display the new image with the information I have? I'm pretty new to OpenCV and already found a very similar question but I'm still kinda confused on what to do.
Example:
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat img = Imgcodecs.imread(path);
Mat hsv = new Mat();
Mat img_new = new Mat();
Imgproc.cvtColor(img,hsv,Imgproc.COLOR_BGR2HSV);
Core.inRange(hsv, new Scalar(hue,saturation,value),new Scalar(hue,saturation,value),hsv);
Imgproc.cvtColor(hsv, img_new, Imgproc.COLOR_HSV2BGR); // This line doesn't work.
// display new image in JFrame
MatOfByte mob = new MatOfByte();
Imgcodecs.imencode(".tif", img_new, mob);
byte ba[] = mob.toArray();
BufferedImage bi = ImageIO.read(new ByteArrayInputStream(ba));
newImgLabel.setIcon(new ImageIcon (bi));
Thank you in advance!
inRange function gives you a mask(actually, a single channel image with values 0 and 255), you can use it to select which areas you want to select.
Mat mask = new Mat();
Imgproc.cvtColor(img,hsv,Imgproc.COLOR_BGR2HSV);
Core.inRange(hsv, new Scalar(hue,saturation,value),new Scalar(hue,saturation,value),mask);
img.copyTo(img_new, mask);
I am trying to extract contours objects from this:
Which is the S component of a HSV image obtained from this:
My code:
Mat hsvImage = new Mat();
Imgproc.cvtColor(image, hsvImage, Imgproc.COLOR_BGR2HSV);
List<Mat> images = new ArrayList<>();
Core.split(hsvImage, images);
Mat blur = new Mat();
Imgproc.medianBlur(images.get(1), blur, 1);
Mat thresh = new Mat();
Imgproc.threshold(blur, thresh, 40, 255, Imgproc.THRESH_BINARY);
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(thresh, contours, new Mat(), Imgproc.RETR_LIST,
Imgproc.CHAIN_APPROX_NONE);`
Results are not bad, but I need to improve detection.
I would like to remove image background, but I don't know if is possible and how. Any suggestion would be appreciated.
I am making a simple red color detector program using open cv but there is a problem with my JLabel it is not showing up the threshold image below is my code.
while(true)
{
try
{
cap.read(mat);// object of VideoCapture class
BufferedImage img= getImage(mat);// converts Mat to bufferedImage
lbl.setIcon(new ImageIcon(img));// this works fine
Mat hsv = new Mat();
Imgproc.cvtColor(mat, hsv, Imgproc.COLOR_BGR2HSV);// converts to hsv
Mat thresh = new Mat() ;
Core.inRange(hsv, new Scalar(0,100,100), new Scalar(10,255,255),thresh);
lbl2.setIcon(new ImageIcon(getImage(thresh)));// not showing up this
} catch(Exception e)
{}
}
Could anyone please tell what the issue is and how to solve it.Thanks.
After cropping an image how can I resize it?
Mat croppedimage = cropImage( image, rect );
Mat resizeimage = croppedimage.resize( any dimension ); //need to change this line
I think, you want this.
e.g.
Mat croppedimage = cropImage(image,rect);
Mat resizeimage = new Mat();
Size sz = new Size(100,100);
Imgproc.resize( croppedimage, resizeimage, sz );
If you want to scale an image using OpenCV java then do the following:
import static org.opencv.imgproc.Imgproc.*;
import static org.opencv.imgcodecs.Imgcodecs.imread;
Main code:
Mat src = imread("imageName.jpg");
Mat resizeimage = new Mat();
Size scaleSize = new Size(300,200);
resize(src, resizeimage, scaleSize , 0, 0, INTER_AREA);
For downscaling it is recommended to use: INTER_AREA and for upscaling use INTER_CUBIC
For more details: OpenCV Ref for Resize
Please read manual for c++ method Resize it's the same to java.
You can choose a type of interpolation. In some cases it's important for a best result.
Mat croppedImage = cropImage(image,rect);
Mat resizeImage = new Mat(anyHeight, anyWidth, croppedImage.type());
int interpolation = Imgproc.INTER_CUBIC;
Imgproc.resize(croppedImage, resizeImage, resizeImage.size(), 0, 0, interpolation );