Java JavaCV Out Of Memory Error bei Kameraaufnahme

XHotSniperX

Lt. Junior Grade
Registriert
Jan. 2008
Beiträge
474
Ich mache gerade ein kleines Projekt mit dem Lego Mindstorm. Als Programmiersprache habe ich Java mit OpenCV genommen (JavaCV) und die lejos library für den Mindstorm Legobaustein.

Das Programm soll einfach mit einer Kamera bestimmte Farben im Bild erkennen (bei mir Rot und Blau). Alles klappt eigentlich, nur kommt manchmal nach wenigen Minuten ein Out Of Memory Error. Weiss einer warum das Programm den Speicher leerfrisst?

Ich vermute, es hängt mit IplImage.create() zusammen. Wie kann ich den Speicher immer schön leeren?

Hier ist der komplette Code:

Code:
package com.mydomain;

/**
 * This is a Project in Computer Architecture and Operating Systems,
 * where a robot is remotely controlled by the computer.
 * 
 *
 */

import static com.googlecode.javacv.cpp.opencv_core.cvFlip;
import static com.googlecode.javacv.cpp.opencv_core.cvGetSize;
import static com.googlecode.javacv.cpp.opencv_core.cvInRangeS;
import static com.googlecode.javacv.cpp.opencv_core.cvScalar;
import static com.googlecode.javacv.cpp.opencv_imgproc.CV_MEDIAN;
import static com.googlecode.javacv.cpp.opencv_imgproc.cvGetCentralMoment;
import static com.googlecode.javacv.cpp.opencv_imgproc.cvGetSpatialMoment;
import static com.googlecode.javacv.cpp.opencv_imgproc.cvMoments;
import static com.googlecode.javacv.cpp.opencv_imgproc.cvSmooth;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
import lejos.nxt.Motor;
import lejos.nxt.remote.RemoteMotor;
import com.googlecode.javacv.CanvasFrame;
import com.googlecode.javacv.FrameGrabber;
import com.googlecode.javacv.VideoInputFrameGrabber;
import com.googlecode.javacv.cpp.opencv_core.CvScalar;
import com.googlecode.javacv.cpp.opencv_core.IplImage;
import com.googlecode.javacv.cpp.opencv_imgproc.CvMoments;

public class RoboPong implements Runnable {
    final int INTERVAL = 1000;// 1sec
    final int CAMERA_NUM = 0; // Default camera for this time

    /**
     * Correct the color range- it depends upon the object, camera quality,
     * environment.
     */
    static CvScalar rgba_min = cvScalar(0, 0, 100, 0);// RED
    static CvScalar rgba_max = cvScalar(30, 30, 255, 0);
    static CvScalar rgbb_min = cvScalar(100, 50, 0, 0);// BLUE
    static CvScalar rgbb_max = cvScalar(255, 190, 30, 0);
    
	//640 x 480 image resolution
	RemoteMotor motorA = Motor.A;
	RemoteMotor motorC = Motor.C;

    IplImage image;
    CanvasFrame canvas = new CanvasFrame("Web Cam Live");
    CanvasFrame path = new CanvasFrame("Detection");
    JPanel jp = new JPanel();

    public RoboPong() {
        canvas.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
        path.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
        path.setContentPane(jp);
        // set robot's speed
    	motorA.setSpeed(600);
    	motorC.setSpeed(600);
    }

    @Override
    public void run() {
        FrameGrabber grabber = new VideoInputFrameGrabber(CAMERA_NUM);
        try {
            grabber.start();
            IplImage img;
            int redX = 0;
            int redY = 0;
            int blueX = 0;
            int blueY = 0;
            while (true) {
                img = grabber.grab();
                if (img != null) {
                    // show image on window
                    cvFlip(img, img, 1);// l-r = 90_degrees_steps_anti_clockwise
                    canvas.showImage(img);
                    IplImage[] detectThrsArray = getThresholdImage(img);
                    IplImage detectThrsRed = detectThrsArray[0];
                    IplImage detectThrsBlue = detectThrsArray[1];

                    CvMoments moments = new CvMoments();
                    cvMoments(detectThrsRed, moments, 1);
                    double mom10a = cvGetSpatialMoment(moments, 1, 0);
                    double mom01a = cvGetSpatialMoment(moments, 0, 1);
                    double area1 = cvGetCentralMoment(moments, 0, 0);
                    redX = (int) (mom10a / area1);
                    redY = (int) (mom01a / area1);
                    cvMoments(detectThrsBlue, moments, 1);
                    double mom10b = cvGetSpatialMoment(moments, 1, 0);
                    double mom01b = cvGetSpatialMoment(moments, 0, 1);
                    double area2 = cvGetCentralMoment(moments, 0, 0);
                    blueX = (int) (mom10b / area2);
                    blueY = (int) (mom01b / area2);
                    // only if its a valid position
                    if (redX > 0 && redY > 0 && blueX > 0 && blueY > 0) {
                        paint1(img, redX, redY);
                        paint2(img, blueX, blueY);
                        compare(redX, redY, blueX, blueY);
                    }
                    else{
                    	stop();
                    }
                }
                //Thread.sleep(INTERVAL);
            }
        } catch (Exception e) {
        }
    }
    
    private void compare(int redX, int redY, int blueX, int blueY){
    	if(blueX > 600 && redX > blueX || blueX < 40 && redX < blueX){
    		stop();
    	}
    	else if((Math.abs(redX - blueX)) < 100){
    		stop();
    	}
    	else if(redX < blueX){
    		goLeft();
    	}
    	else if(redX > blueX){
    		goRight();
    	}
    }
    
    private void goRight(){
    	motorA.forward();
    	motorC.backward();
    }
    
    private void goLeft(){
    	motorA.backward();
    	motorC.forward();
    }
    
    private void stop(){
    	motorA.stop();
    	motorC.stop();
    }

    private void paint1(IplImage img, int posX, int posY) {
        Graphics g = jp.getGraphics();
        path.setSize(img.width(), img.height());
        g.setColor(Color.RED);
        g.drawOval(posX, posY, 20, 20);
        System.out.println(posX + " , " + posY);

    }
    
    private void paint2(IplImage img, int posX, int posY) {
        Graphics g = jp.getGraphics();
        path.setSize(img.width(), img.height());
        g.setColor(Color.BLUE);
        g.drawOval(posX, posY, 20, 20);
        System.out.println(posX + " , " + posY);

    }

    private IplImage[] getThresholdImage(IplImage orgImg) {
    	IplImage[] imgThresholdArray = new IplImage[2];
        IplImage imgThresholdRed = IplImage.create(cvGetSize(orgImg), 8, 1);
        IplImage imgThresholdBlue = IplImage.create(cvGetSize(orgImg), 8, 1);
        cvInRangeS(orgImg, rgba_min, rgba_max, imgThresholdRed);// red
        cvInRangeS(orgImg, rgbb_min, rgbb_max, imgThresholdBlue);// blue
        cvSmooth(imgThresholdRed, imgThresholdRed, CV_MEDIAN, 15);
        cvSmooth(imgThresholdBlue, imgThresholdBlue, CV_MEDIAN, 15);
        imgThresholdArray[0] = imgThresholdRed;
        imgThresholdArray[1] = imgThresholdBlue;
        return imgThresholdArray;
    }

    public static void main(String[] args) {
        RoboPong cot = new RoboPong();
        Thread th = new Thread(cot);
        th.start();
    }
    
}
 
Danke, der gc hat geholfen :)
 
NullPointer schrieb:
Versuch erst mal, deiner JVM mehr Speicher zur Verfügung stellen. Mit den Standardeinstellungen kann sie längst nicht den gesamten RAM deines Rechners nutzen.

Das ist keine Hilfe, sondern Faulheit und bringt auch nichts. Die zusätzlichen Ressourcen können auch ,,voll'' werden und dann stehst du wieder am Anfang.

Mumpitzelchen schrieb:
gc periodisch manuell aufrufen?
Der Aufruf zieht natürlich auch Rechenleistung mit sich und behebt das eigentliche Problem eben so wenig - er kann es im schlimmsten Fall auch nicht, sondern verlangsamt nur dein System unendlich. Würde ich also auch lassen.


Einfach vernünftig debuggen und gut ist.


ps:
stop(); macht man schon lange nicht mehr um einen Thread zu stoppen, sondern mit der Bedingung in der while-Schleife.
 
Zuletzt bearbeitet:
DasBoeseLebt schrieb:
Das ist keine Hilfe, sondern Faulheit und bringt auch nichts. Die zusätzlichen Ressourcen können auch ,,voll'' werden und dann stehst du wieder am Anfang.

Manche Programme brauchen nun mal mehr als die standardmäßigen (ich glaube) 128 MB. Dann kommt man nicht darum herum, ihnen mehr zu geben.
 
NullPointer schrieb:
Manche Programme brauchen nun mal mehr als die standardmäßigen (ich glaube) 128 MB. Dann kommt man nicht darum herum, ihnen mehr zu geben.

128MB sind schon lange nicht mehr Standard und waren es auch nie.
(Ich glaube von 64MB sind die bei 1.6 auf 1/4 des vorhandenen Rams bis zu 1GB, aber letzteres gibt es auch schon mit 1.7 nicht mehr, d.h. Java kann jetzt wie jede 32Bit Anwendung auch 1,8GB für sich benutzen)

Code:
long maxBytes = Runtime.getRuntime().maxMemory();
System.out.println("Max memory: " + maxBytes / 1024 / 1024 + "M");
 
Setze dich am besten mal mit VisualVM außeinander.

Den sleep call am Ende der Schleife solltest du wieder aktivieren, aber das muss keine ganze Sekunde sein. 25ms sollten da schon reichen. Sonst lässt der Thread dem Rest des Systems nämlich keine Chance zu arbeiten und der ganze Kern ist es blockiert. Thread.yield() gibt es da auch noch aber das performt sehr abhängig von der Plattform.
 
DasBoeseLebt schrieb:
ps:
stop(); macht man schon lange nicht mehr um einen Thread zu stoppen, sondern mit der Bedingung in der while-Schleife.

mit stop() wird nicht etwa ein Thread gestoppt, sondern der Motor des Roboters :)
 

Ähnliche Themen

Zurück
Oben