I am developing an OMR(Optical Mark Recognition) application using JavaCV,java interface for OpenCV.The application runs fine for 200 images but after that it fails to allocate memory for IplImage in my code.The allocation error comes when I try to clone imgx and assign it to imgxc1.Can you please suggest a programmatic fix for it? Increasing the heap size seems to be a temporary solution?
Here is the initialization code(where the exception occurs):
protected boolean init () throws UnableToLoadImage{
imgx = new IplImage();
imgxc1 = new IplImage();
imgxd1 = new IplImage();
imgx = cvLoadImage(path+DR+filename);
if(imgx == null)throw new UnableToLoadImage(path+DR+filename);
//cvSaveImage("debug/"+filename, imgx);
imgxc1 = cvCreateImage(cvGetSize(imgx), imgx.depth(), imgx.nChannels());
imgxc1 = imgx.clone();//error comes here
imgxd1 = cvCreateImage(cvGetSize(imgx), IPL_DEPTH_8U, 1);
cvCvtColor(imgxc1, imgxd1, CV_BGR2GRAY);
return (imgx != null && imgxc1 != null && imgxd1 != null)?true:false;
}
Here is the cleaning up code:
public void release() {
if(imgx != null){
imgx.release();
imgxc1.release();
imgxd1.release();
cvReleaseImage(imgx);
cvReleaseImage(imgxc1);
cvReleaseImage(imgxd1);
}
}
Stack trace:
OpenCV Error: Insufficient memory (Failed to allocate 6454368 bytes) in cv::OutOfMemoryError, file ..\..\..\..\opencv\modules\core\src\alloc.cpp, line 52
at org.bytedeco.javacpp.opencv_core.cvCloneImage(Native Method)
at org.bytedeco.javacpp.helper.opencv_core$AbstractIplImage.clone(opencv_core.java:1005)
at com.omr.app.OmrModel.init(OmrModel.java:200)
at com.omr.app.OmrController$2.doInBackground(OmrController.java:328)
at com.omr.app.OmrController$2.doInBackground(OmrController.java:1)
at javax.swing.SwingWorker$1.call(Unknown Source)
at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at javax.swing.SwingWorker.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Your code looks weird. You're assigning IplImage
objects to your variables, only to overwrite them instantly. You're also leaking at least one presumably native resource in the following case:
imgxc1 = cvCreateImage(cvGetSize(imgx), imgx.depth(), imgx.nChannels());
imgxc1 = imgx.clone();//error comes here
The original imgxc1
object that was created with cvCreateImage
has no longer references to it, but you didn't release the resources.
If how references work in Java is unclear to you, I'd suggest you do some work on that before continuing with your work. You'll get rid of your redundant double assignment lines and the code will be clearer too.
Edit: Since I've already wasted time on this, let's go through init()
line by line.
protected boolean init () throws UnableToLoadImage{
imgx = new IplImage(); // Create a new IplImage() even though we won't use it
imgxc1 = new IplImage(); // Same here. Just food for the garbage collector
imgxd1 = new IplImage(); // Third completely unnecessary object creation
imgx = cvLoadImage(path+DR+filename); // Load an image, the previously created object in imgx is now GC food
if(imgx == null)throw new UnableToLoadImage(path+DR+filename);
//cvSaveImage("debug/"+filename, imgx);
imgxc1 = cvCreateImage(cvGetSize(imgx), imgx.depth(), imgx.nChannels()); // Create an image with native resources, previous object is GC food
imgxc1 = imgx.clone(); // Third time assignment to imgxc1, previous object isn't GC food since it holds native resources
imgxd1 = cvCreateImage(cvGetSize(imgx), IPL_DEPTH_8U, 1); // The first proper use of cvCreateImage
cvCvtColor(imgxc1, imgxd1, CV_BGR2GRAY); // Presumably ok
return (imgx != null && imgxc1 != null && imgxd1 != null)?true:false; // Doesn't need the ternary operator at all
}
The code shows a huge misunderstanding of Java, but if there hadn't been native resources involved, it would only result in some extra object creations which would then be handled by the garbage collector.