I'm trying to make a program that takes a picture from a webcam, and afterwards resizes it, converts it to HSV, and makes some thresholding on it, to find a specific color. After this is done, I use the thresholded image to find contours, and print the x,y coords of the different contours. This is repeated over and over, to make the processing from the webcam realtime.
It all works quite well, except for the fact that I am using up about 100 mb of RAM every 2 second it runs.
So far I have discovered that if I use a static picture, instead of the live images from the webcam, I can minimize the memory leak significantly, although still there is memory being consumed.
Below my code is:
public class Application {
private CaptureImage ci;
private ImageUtils iu;
private CanvasFrame canvasContours;
IplImage grabbedFrame;
IplImage resizedFrame;
IplImage thresholdedFrame;
IplImage clonedImage;
public Application(){
ci = new CaptureImage();
iu = new ImageUtils();
canvasContours = new CanvasFrame("contours");
}
public void frameProcessing(){
grabbedFrame = ci.grabImage();
//below call used for testing purposes
//grabbedFrame = (IplImage) opencv_highgui.cvLoadImage("testingImage.jpg");
//cloning image due to highgui guidelines.
clonedImage = opencv_core.cvCloneImage(grabbedFrame);
resizedFrame = iu.resizeImage(clonedImage);
opencv_core.cvReleaseImage(clonedImage);
thresholdedFrame = iu.thresholdImage(resizedFrame);
IplImage contoursFrame = iu.findContours(thresholdedFrame, resizedFrame);
canvasContours.showImage(contoursFrame);
}
}
The grabImage is just the standard frameGrabber from javacv, which looks like this:
public class CaptureImage {
private final OpenCVFrameGrabber grabber;
private IplImage img = null;
public CaptureImage(){
// 0-default camera, 1 - next...so on
grabber = new OpenCVFrameGrabber(0);
try {
grabber.start();
} catch (Exception e) {
System.err.print("Failed to initialize camera");
e.printStackTrace();
}
}
public IplImage grabImage(){
try {
//A grabbed image from Logitech webcam is in following resolution: 1200x800px
img = grabber.grab();
} catch (Exception e) {
e.printStackTrace();
}
return img;
}
I appreciate any help you can give me, and if you need any more information, please just ask!
/Jesper
From your heap dump, the used memory is all byte and int arrays that are referenced from native code. Looking at your code I see that you only call cvReleaseImage for the cloned image and not for the original image.
Related
I am implementing the MLKit face detection library with a simple application. The application is a facial monitoring system so i am setting up a preview feed from the front camera and attempting to detect a face. I am using camera2Api. At my ImageReader.onImageAvailableListener, I want to implement the firebase face detection on each read in the image. After creating my FirebaseVisionImage and running the FirebaseVisionFaceDetector I am getting an empty faces list, this should contain detected faces but I always get a face of size 0 even though a face is in the image.
I have tried other forms of creating my FirebaseVisionImage. Currently, I am creating it through the use of a byteArray which I created following the MlKit docs. I have also tried to create a FirebaseVisionImage using the media Image object.
private final ImageReader.OnImageAvailableListener onPreviewImageAvailableListener = new ImageReader.OnImageAvailableListener() {
/**Get Image convert to Byte Array **/
#Override
public void onImageAvailable(ImageReader reader) {
//Get latest image
Image mImage = reader.acquireNextImage();
if(mImage == null){
return;
}
else {
byte[] newImg = convertYUV420888ToNV21(mImage);
FirebaseApp.initializeApp(MonitoringFeedActivity.this);
FirebaseVisionFaceDetectorOptions highAccuracyOpts =
new FirebaseVisionFaceDetectorOptions.Builder()
.setPerformanceMode(FirebaseVisionFaceDetectorOptions.ACCURATE)
.setLandmarkMode(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS)
.setClassificationMode(FirebaseVisionFaceDetectorOptions.ALL_CLASSIFICATIONS)
.build();
int rotation = getRotationCompensation(frontCameraId,MonitoringFeedActivity.this, getApplicationContext() );
FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
.setWidth(480) // 480x360 is typically sufficient for
.setHeight(360) // image recognition
.setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
.setRotation(rotation)
.build();
FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(newImg, metadata);
FirebaseVisionFaceDetector detector = FirebaseVision.getInstance()
.getVisionFaceDetector(highAccuracyOpts);
Task<List<FirebaseVisionFace>> result =
detector.detectInImage(image)
.addOnSuccessListener(
new OnSuccessListener<List<FirebaseVisionFace>>() {
#Override
public void onSuccess(List<FirebaseVisionFace> faces) {
// Task completed successfully
if (faces.size() != 0) {
Log.i(TAG, String.valueOf(faces.get(0).getSmilingProbability()));
}
}
})
.addOnFailureListener(
new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// Task failed with an exception
// ...
}
});
mImage.close();
The aim is to have the resulting faces list contain the detected faces in each processed image.
byte[] newImg = convertYUV420888ToNV21(mImage);
FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(newImg, metadata);
These two lines are important. make sure Its creating proper VisionImage.
Checkout my project for all functionality
MLKIT demo
I'm trying to create a game from scratch, and my question is directed towards the background image for that said game. This project is probably a precursor for me creating games on a greater scale. In this situation, I'm wanting to change the image it loads for the background depending on what room the game says its in. right now I have two room images, but I can't seem to get it to change the image after it has first been brought up. I don't want to load too big of a file and then use it like a sprite sheet, mainly because I don't want to have framerate issues in the future when the rooms get larger and there is a lot to load as well as the background. My question is, how do I change the image that the background will load in?
I have found several people with the same problem, and there may be answers to this question out there, but I am new-ish to programming so some of the language that is used might be foreign to me.
package com.game.main;
import java.awt.image.BufferedImage;
public class Texture {
SpriteSheet es, ps, ws, bg;
private BufferedImage enemy_sheet = null;
private BufferedImage player_sheet = null;
private BufferedImage weapon_sheet = null;
private BufferedImage background = null;
public BufferedImage[] enemy = new BufferedImage[2];
public BufferedImage[] player = new BufferedImage[9];
public BufferedImage[] weapon = new BufferedImage[9];
public String[] roomim = new String[9];
public BufferedImage room;
public Texture()
{
BuffImLoad loader = new BuffImLoad();
try{
enemy_sheet = loader.loadImage("/SpriteSheetEn1.png");
player_sheet = loader.loadImage("/SpriteSheet.png");
weapon_sheet = loader.loadImage("/SSWeapon.png");
int roomnum = Values.getRoomnum();
background = loader.loadImage(roomloader(roomnum));
}catch(Exception e){
e.printStackTrace();
}
es = new SpriteSheet(enemy_sheet);
ps = new SpriteSheet(player_sheet);
ws = new SpriteSheet(weapon_sheet);
bg = new SpriteSheet(background);
getTextures();
getRoom();
}
private void getTextures(){
enemy[0] = es.grabImage(1,1,56, 53);//idol en1
player[0] = ps.grabImage(1, 1, 34, 50);//temp player image
weapon[0] = ws.grabImage(1, 1, 7, 47);//temp player image
}
private void getRoom(){
room = bg.grabBG(0, 0, 3840 ,2160);
}
private String roomloader(int x){
roomim[0] = ("/Bgtemp.png");
roomim[1] = ("/Bgtemp2.png");
//roomim[1] =
return roomim[x];
}
}
i am learning to write code in java and recently started to write a game as per my assignment.
i have completed almost entire game but stuck with the animation part of the game.
here is what i have done so far,
this is the class that load the image ti display,
public class dpmImage {
private BufferedImage dpm1;
private BufferedImage setDpm1;
public dpmImage() { //this is a constructor
try {
dpm1= ImageIO.read(new File("dpm1Load.png"));
} catch (IOException e) {
e.printStackTrace();
}
setDpm1 = dpm1;
}
private BufferedImage dpm1ImageGet() {
return setDpm1;
}
}
the following code is from main class (Main.java)
private Graphics cGraphcs;
cGraphcs.drawImage(dpmImageInstance.dpm1ImageGet(), 0, 0, null);
The code is working fine and displays the image.
Now, I am allowed to modify anything in dpmImage class but not allowed to modify anything in Main.java and still make this image animate. So I create an array of BufferedImage in dpmImage class and add a second image in the array as follows,
public class dpmImage {
private BufferedImage [] dpm1 = new BufferedImage[2];
private BufferedImage setDpm1;
public dpmImage() { //this is a constructor
try {
dpm1[0]= ImageIO.read(new File("dpm1Load.png"));
dpm1[1]= ImageIO.read(new File("dpm1Load1.png"));
} catch (IOException e) {
e.printStackTrace();
}
setDpm1 = dpm1[0];
setDpm1 = dpm1[1];
}
private BufferedImage dpm1ImageGet() {
return setDpm1;
}
}
But i couldn't get to animate it from first image to 2nd. Can someone please give me any hints on that ? i am not allowed to change the Main.java class
You always return the same BufferedImage from the dpm1ImageGet method. You need to get the instance from the array. Based on the frequency of the update, you can simply use an index like
private int indexImage = 0;
private BufferedImage dpm1ImageGet() {
indexImage = ( indexImage + 1 ) % dpm1.length; //increment and use % to prevent any ArrayOutOfBoundsException
return dpm1[indexImage];
}
Each call will return the next image. Of course, this depends on when you want to get the other image. It could be anything.
I'm trying to implement a simple application to show my webcamera, which works good, until I try to flip the output stream. After I'm flipping the stream the program starts to leak. The strange its only leak when I flip the stream and I have no idea why, since I try to release everything. When I dont flip, the leak is gone. (Its still leaks when I comment out all the release lines)
I'm getting my image source from GrabCamera through these methods:
private void StartWebcamera(){
cameraThread = new Thread(){
public void run(){
while ((grabbedImage = opencv_highgui.cvQueryFrame(capture)) != null) {
grabbedImage.release();
}
}
};
cameraThread.start();
}
public IplImage GetGrabbedStream(){
return grabbedImage;
}
In my main i pass the image to my GUI class like these:
while (true) {
gui.SetVideoStream(grabCamera.GetGrabbedStream(), true);//if true its leaking if false its not leaking
}
And I show my image in the GUI class:
public void SetVideoStream(IplImage img,boolean flip){
if (flip) {
IplImage mirrorImage = img.clone();
cvFlip(img, mirrorImage, 1);
cameraFeed.setImage(mirrorImage.getBufferedImage());
videoPanel.setIcon(cameraFeed);
videoPanel.repaint();
mirrorImage.release();
img.release();
}
else {
cameraFeed.setImage(img.getBufferedImage());
videoPanel.setIcon(cameraFeed);
videoPanel.repaint();
img.release();
}
}
Here are the complete classes:
Classes
suppose we have this URL called
"mysite.com"
in this site we have a directory called images that has about 200 .PNG pictures
I want to make a program that scans through these pictures one by one(you can predict the picture's URL if you know the previous picture's URL) and then I want to display this image on my JFrame
what I initially thought of doing was, since I know the URL, why don't I just scan through all the different image urls, and then do this?
Image image = ImageIO.read(url); hasImage.setIcon(new
ImageIcon(image));
now
hasImage
is a JLabel where I use the image that I just downloaded from the URL
and
url
is an object of class URL
so, everytime in a loop I find the new URL, and I call the function that has the 2 lines of code that I just posted above, in order to update the image on my label
note that these 2 lines are inside a button ActionListener, so that everytime I click on the button, the next image will be displayed
there is 1 major problem here.
when I want to display the next image, it takes some time to create the new url object, download the image, and then display it on my label, which is kind of annoying especially if you're in a hurry and want to display the images really fast...
now, I thought of another implementation, why not just download all the images, save them somewhere locally and then scan through the directory where you stored the images and display them each time the button is clicked?
ok I did this, but the problem is that it takes more than a minute to download all the images
after that it works smoothly, really fast
so here the big problem is that it takes so much time to download the images, so it's basically the previous implementation, but in this one instead of waiting a little bit when I press the button, I kind of wait for everything to get downloaded, which takes the same time...
my question is, how can I make it be faster? if it would download all the images in less than 5 seconds I would be satisfied
here is the function I'm using in order to save the images
private void saveImages() {
Image image;
int ID = 1;
String destFile = "destFolder" + ID + ".png";
try {
image = ImageIO.read(url);
ImageIO.write((RenderedImage) image, "png", new File(destFile));
} catch (IOException ex) {
}
while (ID < 200) {
try {
String path = url.getPath();
String[] token = path.split("-");
String[] finalToken = token[2].split("\\.");
ID = Integer.parseInt(finalToken[0]);
url = new URL("http://somesite/images/" + (ID + 1) + ".png");
destFile = "C:\\...\\destFolder\\" + ID + ".png";
image = ImageIO.read(url);
ImageIO.write((RenderedImage) image, "png", new File(destFile));
} catch (MalformedURLException ex) {
JOptionPane.showMessageDialog(null,
"URL is not in the correct form", "Malformed URL",
JOptionPane.ERROR_MESSAGE);
} catch (IOException ex) {
}
}
JOptionPane.showMessageDialog(null, "Images were loaded successfully",
"Success", JOptionPane.INFORMATION_MESSAGE);
}
EDIT: Btw I'm really sorry about the code, it's kind of messy, it's just the first thing I typed.... I will change it later for the better but I hope you get the idea of what problem I'm facing right now :)
Neither Java or the implementation is the issue, it's the speed of your connection. Either you download all images that the application requires (which takes some time, and is pointless if the images aren't viewed) or you load them as they're clicked.
If you want to make it seem a little quicker, you can start loading the images into a local database or the filesystem (like a cache). That obviously has its drawbacks as it will only make loading times faster once a picture has been loaded once, and it's often ideal to not have a very large cache.
You can also load the five or so next and previous images when just viewing one image, which will make it seem faster to the user.
Downloading 200 images from the web is always going to take some time. What you need to do to speed this up is
To avoid downloading the images in the event dispatch thread: it blocks the UI while downloading. The images should be downloaded in a separate, background thread.
To have two threads downloading images simultaneously. You could download more in parallel, but most of the web servers refuse more than 2 concurrent connections from the same host.
I'd suggest loading images in the background and having them appear as soon as they are loaded, that way your UI will be responsive. When you go to a particular image you can automatically load the next ones in anticipation as well.
Here's a rather green dry-coded (not actually tried to compile) attempt at this, that should demonstrate the idea. (Forgive the tight coupling and rather mashed up responsibilities in the classes. Also, it rather cavalier about the unboundedness of the imageBuffer, and there is no offload to disk).
class ImageManager {
private ExecutorService asyncLoader = Executors.newFixedThreadPool(2);
private HashMap<Integer, Image> imageBuffer = new HashMap<Integer, Image>();
private final int PRELOAD = 2;
private String baseUrl;
public ImageManager(String baseUrl) {
this.baseUrl = baseUrl;
}
public void loadImageAndUpdateLabel(final int idx, final JLabel label) {
asyncLoader.execute(new Runnable() {
public void run() {
loadSync(idx);
SwingUtilities.invokeLater(new Runnable() {
label.setIcon(new ImageIcon(imageBuffer.get(idx)));
label.setText(null);
});
}
});
triggerAsyncPreload(idx);
}
public void triggerAsyncPreload(int idx) {
for (int i=idx-PRELOAD; i<idx+PRELOAD; i++) {
if (i>0) {
loadAsync(i);
}
}
}
public synchronized void loadSync(int idx) {
if (imageBuffer.get(idx) == null) {
imageBuffer.put(idx, ImageIO.read(new URL(baseUrl, idx+".png")));
}
}
public void loadAsync(final int idx) {
asyncLoader.execute(new Runnable() {
public void run() {
loadSync(idx);
}
});
}
}
class ... {
...
private int currentImageIdx = 0;
private ImageManager imageManager = new ImageManager("http://site.com/images/");
private JLabel label = new JLabel();
private JButton nextImageButton = new JButton(new AbstractAction("Next image") {
public void actionPerformed(ActionEvent e) {
currentImageIdx++;
label.setIcon(null);
label.setText("Loading image "+currentImageIdx);
imageManager.loadImageAndUpdateLabel(currentImageIdx, label);
}
});
...
}