I have created Mat with training images (150 images size of 144x33) so my Mat is 4752 width and 150 height. Another mat with labels is 1 width and 150 height. And now when I am trying svm.train() with these two Mat's, I am getting following error:
OpenCV Error: Bad argument (response #2 is not integral) in cvPreprocessCategoricalResponses, file ..\..\..\..\opencv\modules\ml\src\inner_functions.cpp, line 715
Exception in thread "main" CvException [org.opencv.core.CvException: cv::Exception: ..\..\..\..\opencv\modules\ml\src\inner_functions.cpp:715: error: (-5) response #2 is not integral in function cvPreprocessCategoricalResponses]
Here is piece of my code, can somebody tell me what could be wrong?
Mat trainingImages = new Mat(0, imageWidth * imageHeight, CvType.CV_32FC1);
Mat labels = new Mat(amountOfPlates + amountOfNoPlates, 1, CvType.CV_32FC1);
List<Integer> trainingLabels = new ArrayList<>();
for (int i = 0; i < amountOfPlates; i++) {
int index = i + 1;
String file = pathPlates + index + ".jpg";
Mat img = Highgui.imread(file, 0);
img.convertTo(img, CvType.CV_32FC1);
img = img.reshape(1, 1);
trainingImages.push_back(img);
trainingLabels.add(1);
}
for (int i = 0; i < amountOfNoPlates; i++) {
int index = i + 1;
String file = pathNoPlates + index + ".jpg";
Mat img = Highgui.imread(file, 0);
img.convertTo(img, CvType.CV_32FC1);
img = img.reshape(1, 1);
trainingImages.push_back(img);
trainingLabels.add(0);
}
Integer[] array = trainingLabels.toArray(new Integer[trainingLabels.size()]);
int[] trainLabels = new int[array.length];
for (int i = 0; i < array.length; i++) {
trainLabels[i] = array[i];
}
for (int i = 0; i < trainingLabels.size(); i++) {
labels.put(i, 1, trainLabels[i]);
}
CvSVMParams params = new CvSVMParams();
params.set_svm_type(CvSVM.C_SVC);
params.set_kernel_type(CvSVM.LINEAR);
params.set_degree(0);
params.set_gamma(1);
params.set_coef0(0);
params.set_C(1);
params.set_nu(0);
params.set_p(0);
TermCriteria tc = new TermCriteria(opencv_core.CV_TERMCRIT_ITER, 1000, 0.01);
params.set_term_crit(tc);
Size data = trainingImages.size();
Size label = labels.size();
CvSVM svmClassifier = new CvSVM();
svmClassifier.train(trainingImages, labels, new Mat(), new Mat(), params);
svmClassifier.save("test.xml");
Size data shows: width = 4752, height = 150
Size labels shows: width = 1, height = 150
What am I doing wrong?
Mat labels was defined as CV_32FC1, but you extend it with integers from int[] trainLabels.
You should use floating point trainLabels or CV_32SC1 type labels instead.
Related
I am trying to extract a table row containing a filled rectangle in an image file using openCV. I have used findcontours and boundingrect. Disclaimer: I am completely new to opencv and image processing, so this might not be an optimal solution.
This is what I have done so far, it is getting me all the tables in the image including the row i want. How can i filter to just that row?
Imgcodecs imageCodecs = new Imgcodecs();
Mat sourceMat = imageCodecs.imread("image.png");
Mat grayMat = imageCodecs.imread("image.png");
Mat threshold = imageCodecs.imread("image.png");
Mat threshold1 = imageCodecs.imread("image.png");
Imgproc.cvtColor(sourceMat, grayMat, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(grayMat, threshold, 70, 255, Imgproc.THRESH_BINARY_INV);
Imgproc.threshold(grayMat, threshold1, 270, 255, Imgproc.THRESH_BINARY);
Core.bitwise_not(grayMat, threshold);
Imgcodecs imgcodecs1 = new Imgcodecs();
imgcodecs1.imwrite("imagethreshold.png", threshold);
List<MatOfPoint> whiteContours = new ArrayList<>();
MatOfPoint heirarchy = new MatOfPoint();
Imgproc.findContours(threshold.clone(), whiteContours, heirarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_SIMPLE);
int count = 0;
// find appropriate bounding rectangles
for (int i = 0; i < whiteContours.size(); i++) {
RotatedRect boundingRect = Imgproc.minAreaRect(new MatOfPoint2f(whiteContours.get(i).toArray()));
Point rotated_rect_points[] = new Point[4];
boundingRect.points(rotated_rect_points);
Rect rect = Imgproc.boundingRect(new MatOfPoint(rotated_rect_points));
Mat roiMat = sourceMat.submat(rect);
if (rect.area()>15000 && heirarchy.get(0,i) != null) {
// checking if heirarchy has parent, next or previous contour is -1
if(heirarchy.get(0,i)[3]!=-1 && heirarchy.get(0,i)[0] !=-1 && heirarchy.get(0,i)[1] ==-1){
// write to image file
Imgcodecs imgcodecs = new Imgcodecs();
imgcodecs.imwrite("image" + count + ".png", roiMat);
count++;
}
}
}```
I have to do develop a similar algorithm as in Remove top section of image above border line to detect text document, but in Java 1.8 using JavaCV.
The method signature in Python is
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
However in Java it appears to be:
MatVector mt = new MatVector();
findContours(dst, mt, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
I'm stuck into finding the contours and sorting them from biggest to lowest. How do I go about sorting from biggest to lower contours?
My code:
Mat image = imread(imagePath);
Mat gray = new Mat();
cvtColor(mat, gray, COLOR_BGR2GRAY);
Mat grayImg = convertToGray(mat);
GaussianBlur(grayImg, grayImg, new Size(3, 3), 0);
Mat dst = new Mat();
threshold(grayImg, dst, 0, 255,THRESH_BINARY + THRESH_OTSU);
// Find contours and sort for largest contour
MatVector mt = new MatVector();
findContours(dst, mt, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
How to access contours suggestion from https://github.com/bytedeco/javacv/issues/1270:
// accessing contours
MatVector contours = ...
for (int i = 0; i < contours.size(); ++i) {
IntIndexer points = contours.get(i).createIndexer();
int size = (int) points.size(0); // points are stored in a Mat with a single column and multiple rows, since size(0), each element has two channels - for x and y - so the type is CV_32SC2 for integer points
for (int j = 0; j < size; ++j) {
int x = points.get(2 * j);
int y = points.get(2 * j + 1);
// do something with x and y
}
}
Thank you
As #fmw42 said, I refactored the code to look into the contourArea().
See below,
Mat mask = new Mat();
Mat gray = new Mat();
Mat denoised = new Mat();
Mat bin = new Mat();
Mat hierarchy = new Mat();
MatVector contours = new MatVector();
cvtColor(mat, gray, COLOR_BGR2GRAY);
//Normalize
GaussianBlur(gray, denoised, new Size(5, 5), 0);
threshold(denoised, mask, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
normalize(gray, gray, 0, 255, NORM_MINMAX, -1, mask);
// Convert image to binary
threshold(gray, bin, 150, 255, THRESH_BINARY);
// Find contours
findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);
long contourCount = contours.size();
System.out.println("Countour count " + contourCount);
double maxArea = 0;
int maxAreaId = 0;
for (int i = 0; i < contourCount; ++i) {
// Calculate the area of each contour
Mat contour = contours.get(i);
double area = contourArea(contour);
if(area > maxArea){
maxAreaId = i;
maxArea = area;
}
}
I want to have a faster way to apply k-mean to a image and display on the screen. I want to have a opencv for android solutions. My code has a 30s run time on smart phone. I want to run it around 1 or 2s.
I already had code for the K-mean and displayed on the screen using opencv. But I need it to be faster. I think the way it label the image and display took to much time.
public void k_Mean(){
Mat rgba = new Mat();
Mat mHSV = new Mat();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),images[current_image]);
Bitmap outputBitmap = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.RGB_565);
Utils.bitmapToMat(bitmap,rgba);
//must convert to 3 channel image
Imgproc.cvtColor(rgba, mHSV, Imgproc.COLOR_RGBA2RGB,3);
Imgproc.cvtColor(rgba, mHSV, Imgproc.COLOR_RGB2HSV,3);
Mat clusters = cluster(mHSV, 3).get(0);
Utils.matToBitmap(clusters,outputBitmap);
imageView.setImageBitmap(outputBitmap);
}
public List<Mat> cluster(Mat cutout, int k) {
Mat samples = cutout.reshape(1, cutout.cols() * cutout.rows());
Mat samples32f = new Mat();
samples.convertTo(samples32f, CvType.CV_32F, 1.0 / 255.0);
Mat labels = new Mat();
//criteria means the maximum loop
TermCriteria criteria = new TermCriteria(TermCriteria.COUNT, 20, 1);
Mat centers = new Mat();
Core.kmeans(samples32f, k, labels, criteria, 1, Core.KMEANS_PP_CENTERS, centers);
return showClusters(cutout, labels, centers);
}
private static List<Mat> showClusters (Mat cutout, Mat labels, Mat centers) {
centers.convertTo(centers, CvType.CV_8UC1, 255.0);
centers.reshape(3);
System.out.println(labels + "labels");
List<Mat> clusters = new ArrayList<Mat>();
for(int i = 0; i < centers.rows(); i++) {
clusters.add(Mat.zeros(cutout.size(), cutout.type()));
}
Map<Integer, Integer> counts = new HashMap<Integer, Integer>();
for(int i = 0; i < centers.rows(); i++) counts.put(i, 0);
int rows = 0;
for(int y = 0; y < cutout.rows(); y++) {
for(int x = 0; x < cutout.cols(); x++) {
int label = (int)labels.get(rows, 0)[0];
int r = (int)centers.get(label, 2)[0];
int g = (int)centers.get(label, 1)[0];
int b = (int)centers.get(label, 0)[0];
counts.put(label, counts.get(label) + 1);
clusters.get(label).put(y, x, b, g, r);
rows++;
}
}
System.out.println(counts);
return clusters;
}
My output is correct. I wander if there is any faster way to do this. My other image processing algorithm run time is less than 1s.
I want to move a sliding window (a Rect) by half of each window, but I can only get the first line:
My code:
int widthImg = 600;
int HeightImg = 500;
int wWin = 100;// weight window
int hWin = 100;// height window
int xWin = 0;
int yWin = 0;
int winSize = ((widthImg/wWin)*2) * ((HeightImg/hWin)*2);// slide half of window(50)
for(int i=0;i<winSize;i++){
Mat ROIMat = new Mat();
if(i < winSize){
xWin = xWin + wWin/2;
if(xWin == widthImg){
xWin = 0;
yWin = yWin + hWin/2;
}
}
ROIMat = croppMat(Highgui.imread(fileImageName), new Rect(xWin , yWin , wWin , hWin) );
Highgui.imwrite(pathROI+"\\"+i+".jpg", ROIMat); //save ROI image
}
ERROR:
OpenCV Error: Assertion failed (0 <= _colRange.start && _colRange.start <= _colRange.end && _colRange.end <= m.cols) in cv::Mat::Mat, file......\opencv\modules\core\src\matrix.cpp, line 292 Exception in thread "AWT-EventQueue-0" CvException [org.opencv.core.CvException: cv::Exception: ........\opencv\modules\core\src\matrix.cpp:292: error: (-215) 0 <= _colRange.start && _colRange.start <= _colRange.end && _colRange.end <= m.cols in function cv::Mat::Mat]
Where am I doing wrong?
If I understand correctly your question, you should correct your for loop.
Take a look at this code, and check if it's the expected result. The code is in C++, but it's be very close to Java, and I added as comments the equivalent Java calls (but I didn't test them).
#include <opencv2/opencv.hpp>
#include <string>
using namespace cv;
int main()
{
// Load image
Mat3b img = imread(fileImageName);
// JAVA: Mat img = Highgui.imread(fileImageName);
int widthImg = img.cols; // JAVA: img.cols();
int heightImg = img.rows; // JAVA: img.rows();
int wWin = 100; // weight window
int hWin = 100; // height window
int counter = 0;
for (int yWin = 0; yWin <= heightImg - hWin; yWin += hWin/2)
{
for (int xWin = 0; xWin <= widthImg - wWin; xWin += wWin/2)
{
Mat ROIMat(img(Rect(xWin, yWin, wWin, hWin)));
// JAVA: Mat ROIMat = new Mat();
// JAVA: ROIMat = croppMat(img, new Rect(xWin, yWin, wWin, hWin));
imwrite(pathROI + std::to_string(counter) + ".jpg", ROIMat);
//JAVA: Highgui.imwrite(pathROI + "\\" + counter + ".jpg", ROIMat); //save ROI image
++counter;
}
}
return 0;
}
I am using OpenCV4Android to process my images. I wanted to preform Illumination Normalization which was linked to me with this work:
http://lear.inrialpes.fr/pubs/2007/TT07/Tan-amfg07a.pdf
furthermore I was given COMPLETE IMPLEMENTATION in C++ (OpenCV):
https://github.com/bytefish/opencv/blob/master/misc/tan_triggs.cpp
I tried to rewrite this code do Java, but I think there might be mistake somewhere. So, what I get from this alghorithm is close but not good enough. Check the expected results on the PDF above on page for example 12. And this is what i get:
https://dl.dropboxusercontent.com/u/108321090/a1.png
https://dl.dropboxusercontent.com/u/108321090/Screenshot_2013-12-31-14-09-25.png
So there is still too much noise between background and face features, but I think it's my fault here. This is my code:
//GET IMAGE URI
Uri selectedImage = imageReturnedIntent.getData();
//CREATE BITMAP FROM IT
BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
bmpFactoryOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bmp = BitmapFactory.decodeStream(getContentResolver().openInputStream(selectedImage),
null, bmpFactoryOptions);
//CREATE OPENCV MAT OBJECT
Mat imageMat = new Mat();
Utils.bitmapToMat(bmp, imageMat);
//CONVERT TO GRAYSCALE
Mat grayMat = new Mat();
Imgproc.cvtColor(imageMat, grayMat, Imgproc.COLOR_BGR2GRAY);
//CUT OUT FACE FROM WHOLE IMAGE
(...) face detection cascades localize face and writes the region where face is located
in array, then I create mat with only face in it:
Mat cleanFaceMatGRAY = new Mat();
cleanFaceMatGRAY = new Mat(faceDetectMatGRAY, facesArray[0]);
//PROCESSING OF MAT WITH FACE (alghorithm from PDF & .cpp file)
Mat I = tan_triggs_preprocessing(cleanFaceMatGRAY);
Core.normalize(I, I,0, 255, Core.NORM_MINMAX, CvType.CV_8UC1);
//DISPLAY MAT IN IMAGEVIEW
ivPickedPhoto.setImageBitmap(AppTools.createBitmapFromMat(I, Bitmap.Config.ARGB_8888));
And method with algorithm (as u can see its total copy-paste from .cpp file with edited/rewrited methods to OpenCV4Android):
private Mat tan_triggs_preprocessing(Mat image) {
float alpha = 0.1f;
float tau = 10.0f;
float gamma = 0.2f;
int sigma0 = 1;
int sigma1 = 2;
// Convert to floating point:
Mat X = image;
X.convertTo(X, CvType.CV_32FC1);
// Start preprocessing:
Mat I = new Mat();
Core.pow(X, gamma, I);
// Calculate the DOG Image:
{
Mat gaussian0 = new Mat();
Mat gaussian1 = new Mat();
// Kernel Size:
int kernel_sz0 = (3*sigma0);
int kernel_sz1 = (3*sigma1);
// Make them odd for OpenCV:
kernel_sz0 += ((kernel_sz0 % 2) == 0) ? 1 : 0;
kernel_sz1 += ((kernel_sz1 % 2) == 0) ? 1 : 0;
Size ksize1 = new Size(kernel_sz0,kernel_sz0);
Size ksize2 = new Size(kernel_sz1,kernel_sz1);
Imgproc.GaussianBlur(I, gaussian0, ksize1, sigma0, sigma0, Imgproc.BORDER_CONSTANT);
Imgproc.GaussianBlur(I, gaussian1, ksize2, sigma1, sigma1, Imgproc.BORDER_CONSTANT);
Core.subtract(gaussian0, gaussian1, I);
}
{
double meanI = 0.0;
{
Mat tmp = new Mat();
Mat abstmp = new Mat();
Core.absdiff(I, new Scalar(0), abstmp);
Core.pow(abstmp, alpha, tmp);
meanI = Core.mean(tmp).val[0];
}
Core.divide( Math.pow(meanI, 1.0/alpha), I, I);
}
{
double meanI = 0.0;
{
Mat tmp = new Mat();
Mat abstmp = new Mat();
Mat mintmp = new Mat();
Core.absdiff(I, new Scalar(0), abstmp);
Core.min(abstmp, new Scalar(tau), mintmp);
Core.pow(mintmp, alpha, tmp);
meanI = Core.mean(tmp).val[0];
}
Core.divide( Math.pow(meanI, 1.0/alpha), I, I);
}
// Squash into the tanh:
{
for(int r = 0; r < I.rows(); r++) {
for(int c = 0; c < I.cols(); c++) {
I.get(r,c)[0] = Math.tanh(I.get(r,c)[0]) / tau;
}
}
Core.multiply(I,new Scalar(tau), I);
}
return I;
}
And what I didn't understand while I was rewriting this code was the iteration over the matrix. In .cpp there was
I.at<float>(r,c)
Where I have replaced it with just:
I.get(r,c)[0]
Do you think I might have lost some data here so thats why image is shady?