Search Unity

memory overflow at camera script

Discussion in 'Scripting' started by RHoogland, Feb 28, 2016.

  1. RHoogland

    RHoogland

    Joined:
    Jan 24, 2015
    Posts:
    30
    My script below does something very simple, it measures the light intensity the camera receives.
    The problem I am struggling with is that data accumulate in memory all the time. In the Windows Taskmanager I see that memory slowly fills itself up more and more until it hits the ceiling.
    Anyone knows what I need to change here ?




    Code (CSharp):
    1. void Start()
    2. {
    3.     this.camImage = new WebCamTexture(wantedWidth, wantedHeight, 24);//create web cam image
    4. }// end Start
    5.  
    6.  
    7. // Update is called once per frame
    8. void FixedUpdate()
    9. {
    10.     //selected cam:
    11.     if (Global.detector == WEBCAM && !this.camFound) //one time action until camera found
    12.     {
    13.         this.camImage.deviceName = Global.camName[Global.camNr];
    14.         this.camFound = true;
    15.         this.camImage.Play();
    16.     }
    17.  
    18.     this.realWidth = this.camImage.width;
    19.     this.realHeight = this.camImage.height;
    20.     this.intensity = this.getIntensity();
    21.  
    22.  
    23.  
    24. }//end update()
    25.  
    26.  
    27. private float getIntensity()
    28. {
    29.     this.pixelArray = new Color[this.realWidth * this.realHeight];
    30.     this.pixelArray = this.camImage.GetPixels(0, 0, this.realWidth, this.realHeight);//copy camImage pixels to pixelArray
    31.  
    32.     //use a small Width * Height window in the middle of the lens for light detection (in order to spare memory):
    33.     int jLow = (int)(this.realHeight / 2) - this.windowHeight / 2;
    34.     int jHigh = (int)(this.realHeight / 2) + this.windowHeight / 2;
    35.     int iLow = (int)(this.realWidth / 2) - this.windowWidth / 2;
    36.     int iHigh = (int)(this.realWidth / 2) + this.windowWidth / 2;
    37.  
    38.     j = jLow;//vertical index
    39.     while (j < jHigh)
    40.     {
    41.         i = iLow;//horizontal index
    42.         while (i < iHigh)
    43.         {  //add up all red, blue and green values:
    44.             this.red = this.red + pixelArray[i + j * this.realWidth].r;
    45.             this.blue = this.blue + pixelArray[i + j * this.realWidth].b;
    46.             this.green = this.green + pixelArray[i + j * this.realWidth].g;
    47.             i = i + 1;
    48.         }
    49.         j = j + 1;
    50.     }//end while*
    51.     this.red = this.red / (windowHeight * this.windowWidth); //average value
    52.     this.blue = this.blue / (this.windowHeight * this.windowWidth);//average value
    53.     this.green = this.green / (this.windowHeight * this.windowWidth);//average value
    54.  
    55.     return (this.green + this.blue + this.red) / 3;
    56.  
    57.  
    58. }//end getIntensity()
     
    Last edited: Feb 28, 2016
  2. mgear

    mgear

    Joined:
    Aug 3, 2010
    Posts:
    9,442
    any difference if you move this line inside Start() ?
    Code (CSharp):
    1.     this.pixelArray = new Color[this.realWidth * this.realHeight];
     
  3. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    You could actually delete that line altogether. GetPixels already returns a new array, so this line is just feeding the GC.

    This is normally because you are holding onto references. Why not make the pixelArray local in scope so the GC can deal with it when you are done?

    Edit: also there is no need to get the entire pixel set if you just want to average the centre pixels. GetPixels has an overload to only get a small block.
     
  4. RHoogland

    RHoogland

    Joined:
    Jan 24, 2015
    Posts:
    30
    Thanksa lot, BoredMormon, your advice improved performance.
    There was another thing going on as well, however. For testing reasons, just to see the images, I had temporarily added the following statements to my script:
    this.texture2d = new Texture2D (this.realWidth,this.realHeight);
    this.texture2d.SetPixels (this.pixelArray); //copy pixelArray to texture2d
    this.texture2d.Apply();
    Taking them away, solved the problem of memory blowing up, so for me the problem is solved.
    For other people who really need to show a video stream in their application, the question still remains, why these statements blew up memory?
    Is there a way to clean up the image stream, so that is does not accumulates in memory ?
    Maybe someone can answer that question for the sake of other people who might be wrestling with that problem.
     
  5. Lanre

    Lanre

    Joined:
    Dec 26, 2013
    Posts:
    3,973
    I can answer these questions from working with a lot of Textures for NatCam. The reason it blew up memory when you were creating a texture was because creating a new Texture2D allocates memory in two places: system memory (RAM) and GPU memory (VRAM). Calling Apply() creates a new GPU texture if not created and uploads the data you set when you called SetPixels(). If the texture goes out of scope without being explicitly destroyed, GC might clean up the pixels in RAM but cannot touch VRAM. This is practically equivalent to calling malloc and losing the pointer before you can free it. Your memory will accumulate.

    Back to your code, you should create a Color32[] in Start() and pass it in when you call GetPixels32(). This completely avoids allocating memory. Once you do this, iterate over the region you want.