Actually, i want to find out the dominant colour in the image, so i want to find the HSV histogram of the image and hence filter out the other colours. However, i dont know how to do this in java platform using opence. I only find the code in C++. Thank you.
Mat image = Highgui.imread("binary07.jpg");
//Mat src = new Mat(image.height(), image.width(), CvType.CV_8UC2);
Imgproc.cvtColor(image, image, Imgproc.COLOR_RGB2GRAY);
List<Mat> hsv_planes = new ArrayList<Mat>();
Core.split(image, hsv_planes);
MatOfInt histSize = new MatOfInt(256);
final MatOfFloat histRange = new MatOfFloat(0f, 256f);
boolean accumulate = false;
Mat h_hist = new Mat();
Mat s_hist = new Mat();
Mat v_hist = new Mat();
//error appear in the following sentences
Imgproc.calcHist((List<Mat>) hsv_planes.get(0), new MatOfInt(3), new Mat(), h_hist, histSize, histRange, accumulate);
Imgproc.calcHist((List<Mat>) hsv_planes.get(1), new MatOfInt(3), new Mat(), s_hist, histSize, histRange, accumulate);
Imgproc.calcHist((List<Mat>) hsv_planes.get(2), new MatOfInt(3), new Mat(), v_hist, histSize, histRange, accumulate);
int hist_w = 512;
int hist_h = 600;
long bin_w = Math.round((double) hist_w / 256);
//bin_w = Math.round((double) (hist_w / 256));
Mat histImage = new Mat(hist_h, hist_w, CvType.CV_8UC1);
Core.normalize(h_hist, h_hist, 3, histImage.rows(), Core.NORM_MINMAX);
Core.normalize(s_hist, s_hist, 3, histImage.rows(), Core.NORM_MINMAX);
Core.normalize(v_hist, v_hist, 3, histImage.rows(), Core.NORM_MINMAX);
for (int i = 1; i < 256; i++) {
Point p1 = new Point(bin_w * (i - 1), hist_h - Math.round(h_hist.get(i - 1, 0)[0]));
Point p2 = new Point(bin_w * (i), hist_h - Math.round(h_hist.get(i, 0)[0]));
Core.line(histImage, p1, p2, new Scalar(255, 0, 0), 2, 8, 0);
Point p3 = new Point(bin_w * (i - 1), hist_h - Math.round(s_hist.get(i - 1, 0)[0]));
Point p4 = new Point(bin_w * (i), hist_h - Math.round(s_hist.get(i, 0)[0]));
Core.line(histImage, p3, p4, new Scalar(0, 255, 0), 2, 8, 0);
Point p5 = new Point(bin_w * (i - 1), hist_h - Math.round(v_hist.get(i - 1, 0)[0]));
Point p6 = new Point(bin_w * (i), hist_h - Math.round(v_hist.get(i, 0)[0]));
Core.line(histImage, p5, p6, new Scalar(0, 0, 255), 2, 8, 0);
}
Highgui.imwrite("histogram.jpg", histImage);
I dont know how to get the output after the split function.
reference:
http://docs.opencv.org/java/
http://docs.opencv.org/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html
In the code, the color conversion:
Imgproc.cvtColor(image, image, Imgproc.COLOR_RGB2GRAY);
should be to HSV not gray:
Imgproc.cvtColor(image, image, Imgproc.COLOR_BGR2HSV);
In your example you will only have one (gray) plane instead of the 3 HSV channels. That will give errors when you access the 2nd and 3rd plane.
Here is the code for comparing the histogram of Source image to a reference image for OpenCV 2.4.11 Java (Android).
// Assume SourceImage is a Bitmap ARGB_8888
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap refImage = BitmapFactory.decodeFile(mBaseDir + "some_reference.jpg", options);
Mat hsvRef = new Mat();
Mat hsvSource = new Mat();
Mat srcRef = new Mat(refImage.getHeight(), refImage.getWidth(), CvType.CV_8U, new Scalar(4));
Utils.bitmapToMat(refImage, srcRef);
Mat srcSource = new Mat(SourceImage.getHeight(), SourceImage.getWidth(), CvType.CV_8U, new Scalar(4));
Utils.bitmapToMat(SourceImage, srcSource);
/// Convert to HSV
Imgproc.cvtColor(srcRef, hsvRef, Imgproc.COLOR_BGR2HSV);
Imgproc.cvtColor(srcSource, hsvSource, Imgproc.COLOR_BGR2HSV);
/// Using 50 bins for hue and 60 for saturation
int hBins = 50;
int sBins = 60;
MatOfInt histSize = new MatOfInt( hBins, sBins);
// hue varies from 0 to 179, saturation from 0 to 255
MatOfFloat ranges = new MatOfFloat( 0f,180f,0f,256f );
// we compute the histogram from the 0-th and 1-st channels
MatOfInt channels = new MatOfInt(0, 1);
Mat histRef = new Mat();
Mat histSource = new Mat();
ArrayList<Mat> histImages=new ArrayList<Mat>();
histImages.add(hsvRef);
Imgproc.calcHist(histImages,
channels,
new Mat(),
histRef,
histSize,
ranges,
false);
Core.normalize(histRef,
histRef,
0,
1,
Core.NORM_MINMAX,
-1,
new Mat());
histImages=new ArrayList<Mat>();
histImages.add(hsvSource);
Imgproc.calcHist(histImages,
channels,
new Mat(),
histSource,
histSize,
ranges,
false);
Core.normalize(histSource,
histSource,
0,
1,
Core.NORM_MINMAX,
-1,
new Mat());
double resp1 = Imgproc.compareHist(histRef, histSource, 0);
double resp2 = Imgproc.compareHist(histRef, histSource, 1);
double resp3 = Imgproc.compareHist(histRef, histSource, 2);
double resp4 = Imgproc.compareHist(histRef, histSource, 3);
The next code works fine for one depth channel. You have to do just a few modifications to add the other two channels
//Calculate histogram
java.util.List<Mat> matList = new LinkedList<Mat>();
matList.add(imageIR_gray);
Mat histogram = new Mat();
MatOfFloat ranges=new MatOfFloat(0,256);
MatOfInt histSize = new MatOfInt(255);
Imgproc.calcHist(
matList,
new MatOfInt(0),
new Mat(),
histogram ,
histSize ,
ranges);
// Create space for histogram image
Mat histImage = Mat.zeros( 100, (int)histSize.get(0, 0)[0], CvType.CV_8UC1);
// Normalize histogram
Core.normalize(histogram, histogram, 1, histImage.rows() , Core.NORM_MINMAX, -1, new Mat() );
// Draw lines for histogram points
for( int i = 0; i < (int)histSize.get(0, 0)[0]; i++ )
{
Core.line(
histImage,
new org.opencv.core.Point( i, histImage.rows() ),
new org.opencv.core.Point( i, histImage.rows()-Math.round( histogram.get(i,0)[0] )) ,
new Scalar( 255, 255, 255),
1, 8, 0 );
}
Related
I will like to detect the hand in the following images. I have tried grabCut algorithm from opencv using the foreground and background extraction. However, there is some inaccuracy in the detection. Could someone help me with it, please. Thank You.
The code is as follow:
public static void DetectionAlg() {
//convert from bitmap to mat
Mat mat = new Mat(myBitmap.getWidth(), myBitmap.getHeight(), CvType.CV_8UC3);
Utils.bitmapToMat(myBitmap, mat);
//covert to bgr
Mat bgrMat = new Mat();
Imgproc.cvtColor(mat, bgrMat, Imgproc.COLOR_BGRA2BGR);
Mat background = new Mat(bgrMat.size(), CvType.CV_8UC3, new Scalar(255, 255, 255));
Mat firstMask = new Mat();
Mat bgModel = new Mat();
Mat fgModel = new Mat();
Mat mask;
Mat source = new Mat(1, 1, CvType.CV_8U, new Scalar(Imgproc.GC_PR_FGD));
Mat dst = new Mat();
//using a user define area
Rect rect = new Rect(GlobalVar.tl, GlobalVar.br);
Imgproc.grabCut(bgrMat, firstMask, rect, bgModel, fgModel, 5, Imgproc.GC_INIT_WITH_RECT);
Core.compare(firstMask, source, firstMask, Core.CMP_EQ);
Mat foreground = new Mat(bgrMat.size(), CvType.CV_8UC3, new Scalar(255, 255, 255));
bgrMat.copyTo(foreground, firstMask);
Scalar color = new Scalar(255, 0, 0, 255);
Imgproc.rectangle(bgrMat, GlobalVar.tl, GlobalVar.br, color);
Mat tmp = new Mat();
Imgproc.resize(background, tmp, bgrMat.size());
background = tmp;
mask = new Mat(foreground.size(), CvType.CV_8UC1, new Scalar(255, 255, 255));
Imgproc.cvtColor(foreground, mask, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(mask, mask, 254, 255, Imgproc.THRESH_BINARY_INV);
Mat vals = new Mat(1, 1, CvType.CV_8UC3, new Scalar(0.0));
background.copyTo(dst);
background.setTo(vals, mask);
Core.add(background, foreground, dst, mask);
firstMask.release();
source.release();
bgModel.release();
fgModel.release();
vals.release();
Mat colorMat = new Mat();
Imgproc.cvtColor(dst, colorMat, Imgproc.COLOR_BGR2BGRA);
Bitmap outputBitmap = Bitmap.createBitmap(colorMat.cols(), colorMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(colorMat, outputBitmap);
}
Images are in the link:
This is the input image
This is another input image
This is the output using the above method, however, there are some other parts that does not belong to the hand
I am performing Canny edge detector using Android and Opencv on an image to detect the largest contour, extract it using warpPerspective method then find all objects inside that contour. Everything is working as expected but only for image that isn't rotated.
I am using boundingRect to get the contour and use its coordinates to extract it.
Here my code:
private Mat detectLargestContour(Mat origMat) {
// long e1 = Core.getTickCount();
Mat mGray = new Mat();
MatOfDouble mu = new MatOfDouble();
MatOfDouble stddev = new MatOfDouble();
Imgproc.cvtColor(origMat, mGray, Imgproc.COLOR_BGR2GRAY);
Core.meanStdDev(mGray, mu, stddev);
Imgproc.GaussianBlur(mGray, mGray, new Size(5, 5), 5);
//Imgproc.Canny(mGray, mGray, 30, 80, 3, false); //FOR HIGH BRIGHTNESS
//Imgproc.Canny(mGray, mGray, 50, 130, 3, false); // FOR LOW BRIGHTNESS
Imgproc.Canny(mGray, mGray, mu.get(0, 0)[0], stddev.get(0, 0)[0], 3, false);
Mat kernell = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(9,9));
Imgproc.morphologyEx(mGray, mGray, Imgproc.MORPH_CLOSE, kernell);
Imgproc.dilate(mGray, mGray, Imgproc.getStructuringElement(Imgproc.MORPH_CROSS, new Size(3, 3)));
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(mGray, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
//MatOfPoint2f approxCurve = new MatOfPoint2f();
double largest_area=0;
Rect rect = new Rect();
for (int idx = 0; idx < contours.size() ; idx++) {
double a = Imgproc.contourArea(contours.get(idx)); //Find the area of contour
if (a > largest_area) {
largest_area = a;
rect = Imgproc.boundingRect(contours.get(idx));
}
}
if (rect.area() > 100000) {
Imgproc.rectangle(origMat, rect.tl(), rect.br(), new Scalar(0, 255, 0));
p1 = new Point(rect.tl().x, rect.tl().y);
p2 = new Point(rect.tl().x + rect.width, rect.tl().y);
p3 = new Point(rect.tl().x, rect.tl().y + rect.height);
p4 = new Point(rect.tl().x + rect.width, rect.tl().y + rect.height);
card_corners = new ArrayList<>();
card_corners.add(p1);
card_corners.add(p3);
card_corners.add(p4);
card_corners.add(p2);
warpedCard = new Mat(origMat.rows(), origMat.cols(), CvType.CV_8UC3);
final Point p1 = new Point(warpedCard.cols() + marge, warpedCard.rows() + marge);
final Point p2 = new Point(0 - marge, warpedCard.rows() + marge);
final Point p3 = new Point(0 - marge, 0 - marge);
final Point p4 = new Point(warpedCard.cols() + marge, 0 - marge);
LinkedList<Point> sceneList = new LinkedList<Point>();
sceneList.addLast(p4);
sceneList.addLast(p3);
sceneList.addLast(p2);
sceneList.addLast(p1);
MatOfPoint2f scene = new MatOfPoint2f();
scene.fromList(sceneList);
MatOfPoint2f obj = new MatOfPoint2f();
obj.fromList(card_corners);
Mat homography = Calib3d.findHomography(obj, scene);
Imgproc.warpPerspective(origMat, warpedCard, homography, new Size(warpedCard.cols(), warpedCard.rows()));
return warpedCard;
}
return origMat;
}
It's weird but only boundingRect gave me a stable and performant result but the drawn rectangle doesn't rotate with the found contour.
How can I manage to resolve this issue? Any thoughts?
EDIT:
I changed boundingRect with minAreaRect.
Here is the code
int largest_idx = 0;
for (int idx = 0; idx < contours.size() ; idx++) {
double a = Imgproc.contourArea(contours.get(idx)); //Find the area of contour
if (a > largest_area) {
largest_area = a;
// rect = Imgproc.boundingRect(contours.get(idx));
largest_idx = idx;
}
}
MatOfPoint2f new_mat = new MatOfPoint2f( contours.get(largest_idx).toArray() );
RotatedRect rbox = Imgproc.minAreaRect(new_mat);
Log.d("rotatedrect_angle", "" + rbox.angle);
Point points[] = new Point[4];
rbox.points(points);
for(int i=0; i<4; ++i){
Imgproc.line(origMat, points[i], points[(i+1)%4], new Scalar(255,255,255));
}
And here is what I've got:
As you can see the detection isn't as accurate as when I used boundingRect.
A Python demo to find and draw a rotatedRect:
# 2019/03/01
# https://stackoverflow.com/a/54942835/3547485
import numpy as np
import cv2
gray = cv2.imread("tmp.png", cv2.IMREAD_GRAYSCALE)
th, threshed = cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY_INV)
cnts = cv2.findContours(threshed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]
cnt = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
rbox = cv2.minAreaRect(cnt)
pts = cv2.boxPoints(rbox).astype(np.int32)
cv2.drawContours(img, [pts], -1, (0, 255, 0), 1, cv2.LINE_AA)
cv2.imwrite("dst.png", img)
Useful OpenCV functions(in Python) : cv2.minAreaRect, cv2.boxPoints, cv.2drawContours. You can find corresponding functions in Java.
I'm doing a project using opencv in android studio.
My project is about comparing colour histogram of an input image captured by camera with the 6 images that is stored in the drawable. The opencv function that I used is comparHist(). The purpose of the comparison is to know if the input image has matched colour to any images in the drawable.
My problem is I need to calculate colour histogram using calcHist() for every 6 images every time there is an input image(captured by camera) so that I can compare them. I was thinking to store the colour histogram value of 6 images in an array. Can I do so?
I was searching for a solution but there is none. Here is my code that I have done.
public int resultRecog() {
drawImg = BitmapFactory.decodeResource(getResources(), R.drawable.onefront);
Mat hsv_base = new Mat();
Mat hsv_test = new Mat();
Utils.bitmapToMat(drawImg, hsv_base);
Utils.bitmapToMat(pic, hsv_test);
/// Convert to HSV
Imgproc.cvtColor(hsv_base, hsv_base, Imgproc.COLOR_BGR2HSV);
Imgproc.cvtColor(hsv_test, hsv_test, Imgproc.COLOR_BGR2HSV);
/// h s bins value
MatOfInt histSize = new MatOfInt(40, 40);
//// ranges h=0-180, s=0-256
MatOfFloat ranges = new MatOfFloat(0f, 180f, 0f, 256f);
/// two channel
MatOfInt channels = new MatOfInt(0, 1);
Mat hist_base = new Mat();
Mat hist_test = new Mat();
ArrayList<Mat> histImages = new ArrayList<Mat>();
histImages.add(hsv_base);
Imgproc.calcHist(histImages, channels, new Mat(), hist_base, histSize, ranges, false);
Core.normalize(hist_base, hist_base, 0, 1, Core.NORM_MINMAX, -1, new Mat());
histImages = new ArrayList<Mat>();
histImages.add(hsv_test);
Imgproc.calcHist(histImages, channels, new Mat(), hist_test, histSize, ranges, false);
Core.normalize(hist_test, hist_test, 0, 1, Core.NORM_MINMAX, -1, new Mat());
double result = Imgproc.compareHist(hist_base, hist_test, 0);
drawImg.recycle();
drawImg = null;
int r;
if (result >= 0.15) {
r = 1;
} else {
drawImg = BitmapFactory.decodeResource(getResources(), R.drawable.fivefront);
Mat hsv_base5 = new Mat();
Utils.bitmapToMat(drawImg, hsv_base5);
Imgproc.cvtColor(hsv_base5, hsv_base5, Imgproc.COLOR_BGR2HSV);
Mat hist_base5 = new Mat();
ArrayList<Mat> histImages5 = new ArrayList<Mat>();
histImages5.add(hsv_base5);
Imgproc.calcHist(histImages5, channels, new Mat(), hist_base5, histSize, ranges, false);
Core.normalize(hist_base5, hist_base5, 0, 1, Core.NORM_MINMAX, -1, new Mat());
result = Imgproc.compareHist(hist_base5, hist_test, 0);
drawImg.recycle();
drawImg = null;
if (result >= 0.1) {
r = 5;
} else {
drawImg = BitmapFactory.decodeResource(getResources(), R.drawable.f10);
Mat hsv_base10 = new Mat();
Utils.bitmapToMat(drawImg, hsv_base10);
Imgproc.cvtColor(hsv_base10, hsv_base10, Imgproc.COLOR_BGR2HSV);
Mat hist_base10 = new Mat();
ArrayList<Mat> histImages10 = new ArrayList<Mat>();
histImages10.add(hsv_base10);
Imgproc.calcHist(histImages10, channels, new Mat(), hist_base10, histSize, ranges, false);
Core.normalize(hist_base10, hist_base10, 0, 1, Core.NORM_MINMAX, -1, new Mat());
result = Imgproc.compareHist(hist_base10, hist_test, 0);
drawImg.recycle();
drawImg = null;
if (result >= 0.1) {
r = 10;
} else {
drawImg = BitmapFactory.decodeResource(getResources(), R.drawable.fiftyfront);
Mat hsv_base50 = new Mat();
Utils.bitmapToMat(drawImg, hsv_base50);
Imgproc.cvtColor(hsv_base50, hsv_base50, Imgproc.COLOR_BGR2HSV);
Mat hist_base50 = new Mat();
ArrayList<Mat> histImages50 = new ArrayList<Mat>();
histImages50.add(hsv_base50);
Imgproc.calcHist(histImages50, channels, new Mat(), hist_base50, histSize, ranges, false);
Core.normalize(hist_base50, hist_base50, 0, 1, Core.NORM_MINMAX, -1, new Mat());
result = Imgproc.compareHist(hist_base50, hist_test, 0);
drawImg.recycle();
drawImg = null;
if (result >= 0.1) {
r = 50;
} else {
r = 0;
}
}//rm10
}//rm5
}//rm1
return r;
}
So my question is, can I store the colour histogram value (of 6 images stored in the drawable) in an array so that the application doesn't need to re-calculate the colour histogram every time there is input image captured by camera
I try to dilate three image of characters in java opencv. i found out than ever it same character with same font and size, after dilate the result is different. so i try with same image the result is still different. Here is my test code.
for (int j = 0; j < 3; j++) {
Mat InputSrc = openFile("src\\myOpencv\\ocr\\crop1.png");
Mat tempImg =new Mat();
Imgproc.cvtColor(InputSrc, tempImg, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(tempImg, tempImg, 0, 255, Imgproc.THRESH_OTSU);
imageViewer.show(tempImg, "src");
Mat kernal5 = new Mat(5, 5, CV_8U);
Point midPoint = new Point(-1, -1);
Scalar scalarOne = new Scalar(1);
Mat binImg2 = new Mat();
Imgproc.dilate(tempImg, binImg2, kernal5, midPoint, 1, 1, scalarOne);
imageViewer.show(binImg2, "dilate");
}
thank
Is there a way in Java or OpenCv ; preferably Java, that i can have an HSV histogram give RGB image.
I tried exploring JAI but it creates histogram for RGB image.
Thanks
Harshit
firs use cv::cvtColor to convert RGB to HSV
then use cv::calcHist to compute the histogram
Here is the pseudocode for a simple RGB to HSV converter. It will give a H of UNDEFINED if the color is a shade of gray, otherwise H is between 0 and 6.
x = min(R, G, B);
V = max(R, G, B);
if (V == x) {
H = UNDEFINED
S = 0
}
else {
if( R == x ) {
f = G - B;
i = 3;
} else if( G == x ) {
f = B - R;
i = 5;
} else {
f = R - G;
i = 1;
}
H = i - f /(V - x);
S = (V - x)/V;
}
Now you can either convert all your pixels and bin them to construct your HSV histogram, or you can convert each bin of your RGB histogram to an HSV bin.
You can use the "JavaCV" library to access OpenCV functions directly from Java:
http://code.google.com/p/javacv/
Then you can use my code for RGB to HSV that is better than OpenCV's cvConvert function:
http://www.shervinemami.co.cc/colorConversion.html
Cheers,
Shervin Emami.
Here is a code to do this:
// Assume SourceImage is a Bitmap ARGB_8888
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap refImage = BitmapFactory.decodeFile(mBaseDir + "some_reference.jpg", options);
Mat hsvRef = new Mat();
Mat hsvSource = new Mat();
Mat srcRef = new Mat(refImage.getHeight(), refImage.getWidth(), CvType.CV_8U, new Scalar(4));
Utils.bitmapToMat(refImage, srcRef);
Mat srcSource = new Mat(SourceImage.getHeight(), SourceImage.getWidth(), CvType.CV_8U, new Scalar(4));
Utils.bitmapToMat(SourceImage, srcSource);
/// Convert to HSV
Imgproc.cvtColor(srcRef, hsvRef, Imgproc.COLOR_BGR2HSV);
Imgproc.cvtColor(srcSource, hsvSource, Imgproc.COLOR_BGR2HSV);
/// Using 50 bins for hue and 60 for saturation
int hBins = 50;
int sBins = 60;
MatOfInt histSize = new MatOfInt( hBins, sBins);
// hue varies from 0 to 179, saturation from 0 to 255
MatOfFloat ranges = new MatOfFloat( 0f,180f,0f,256f );
// we compute the histogram from the 0-th and 1-st channels
MatOfInt channels = new MatOfInt(0, 1);
Mat histRef = new Mat();
Mat histSource = new Mat();
ArrayList<Mat> histImages=new ArrayList<Mat>();
histImages.add(hsvRef);
Imgproc.calcHist(histImages,
channels,
new Mat(),
histRef,
histSize,
ranges,
false);
Core.normalize(histRef,
histRef,
0,
1,
Core.NORM_MINMAX,
-1,
new Mat());
histImages=new ArrayList<Mat>();
histImages.add(hsvSource);
Imgproc.calcHist(histImages,
channels,
new Mat(),
histSource,
histSize,
ranges,
false);
Core.normalize(histSource,
histSource,
0,
1,
Core.NORM_MINMAX,
-1,
new Mat());
double resp1 = Imgproc.compareHist(histRef, histSource, 0);
double resp2 = Imgproc.compareHist(histRef, histSource, 1);
double resp3 = Imgproc.compareHist(histRef, histSource, 2);
double resp4 = Imgproc.compareHist(histRef, histSource, 3);
First, you have to convert image to HSV using cv::cvtColor to convert RGB image into HSV image and then, you can use cv::calcHist to compute the HSV histogram.