0

Wrote a program in OpenCV, however it doesn't have comfortable canvas, so working with it is a pain in the neck for my purpose, decided to migrate to SDL2. Noticed the RAM usage is 3 times as high. For the dataset of 37 images OpenCV takes up 440mb, while SDL2 1300mb. For 166 images dataset OpenCV takes up 1500mb, and SDL2 5000mb. In the code I am storing image mipmaps and getting them on the runtime to blit on screen.

SDL2 code:

void SDLTexture::set_texture(Image* image, cv::Mat& pixels) {
    for (int i = 0; i < resolutions.size(); i++) {
        float scale = (float)resolutions[i]/pixels.cols;
        cv::resize(pixels, pixels, cv::Size(0, 0),
                    scale, scale, cv::INTER_AREA);

        mipmaps[i][image->get_name()] = SDL_CreateTexture(renderer,
                                           SDL_PIXELFORMAT_BGR24,
                                           SDL_TEXTUREACCESS_STATIC,
                                           pixels.cols,
                                           pixels.rows);

        SDL_UpdateTexture(mipmaps[i][image->get_name()], NULL, (void*)pixels.data, pixels.step1());
    }
}

and to get the texture I'm doing SDL_Texture* tex = sdltexture->get_texture(img, width);

OpenCV code:

void CVTexture::set_texture(Image* image, cv::Mat& pixels) {
    for (int i = 0; i < resolutions.size(); i++) {
        float scale = (float)resolutions[i]/pixels.cols;
        cv::resize(pixels, pixels, cv::Size(0, 0),
                    scale, scale, cv::INTER_AREA);
        mipmaps[i][image->get_name()] = pixels.clone();
    }
}

and to get the texture I'm doing cv::Mat tex = cvtexture->get_texture(img, width); Where get_texture just returns the reference to the texture stored in mipmap

Creating SDL textures on the fly will be too slow I believe. The code is equivalent and the performance is comparable(even though opencv doesn't use gpu). So I am a bit lost as to why the memory usage difference is that drastic. Am I leaking memory somewhere? Does OpenCV use some optimizations to store the matrix while maintaining the same performance?(sounds a little bit too good to be true)

here's minimum reproducible example as requested, not sure if it's helpful tho and it bloats the post:

#include <iostream>
#include <string>

#include <SDL.h>

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>

const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 800;

void init(SDL_Window** window_ptr, SDL_Surface** surface_ptr, SDL_Renderer** renderer_ptr);
void close(SDL_Window** window);

int main( int argc, char* args[] ) {
    bool USE_SDL = false;
    std::string path = "1.png";

    SDL_Window* window = NULL;
    SDL_Surface* screenSurface = NULL;
    SDL_Renderer* renderer = NULL;
    SDL_Event event;

    init(&window, &screenSurface, &renderer);

    bool running = true;

    std::vector<int> resolutions = {1600, 800, 400, 200, 100, 50, 25};
    std::vector<SDL_Texture*> sdl_mipmaps;
    std::vector<cv::Mat> cv_mipmaps;
    cv::Mat pixels;

    for (int i = 0; i < 100; i++) {
        pixels = cv::imread(path, cv::IMREAD_COLOR);

        for (int i = 0; i < resolutions.size(); i++) {
            // RESIZE
            float scale = (float)resolutions[i]/pixels.cols;
            cv::resize(pixels, pixels, cv::Size(0, 0),
                        scale, scale, cv::INTER_AREA);

            if (USE_SDL) {
                // ADD TO SDL TEXTURES
                SDL_Texture* sdltex = SDL_CreateTexture(renderer,
                                           SDL_PIXELFORMAT_BGR24,
                                           SDL_TEXTUREACCESS_STATIC,
                                           pixels.cols,
                                           pixels.rows);

                SDL_UpdateTexture(sdltex, NULL, (void*)pixels.data, pixels.step1());
                sdl_mipmaps.push_back(sdltex);
            } else {
                // ADD TO OPENCV TEXTURES
                cv_mipmaps.push_back(pixels.clone());
            }
        }
    }

    // EVERYTHING BELOW CAN BE IGNORED, NOT THE MAIN IDEA.

    // RENDER SAMPLE TEXTURE TO SHOW THAT IT WORKS. RENDERS IN A WEIRD PATTERN AND THATS ALRIGHT, not the point
    SDL_Texture* sample;
    SDL_Rect texr{0, 0, 1600, 1600};
    while(running) {
        while( SDL_PollEvent( &event ) != 0 )
        {
            if( event.type == SDL_QUIT )
            {
                running = false;
            }


            for (int i = 0; i < 50; i++) {
                texr.x = i * 50;
                texr.y = i * 50;
                texr.w = resolutions[i%7];
                texr.h = resolutions[i%7];

                if (USE_SDL) {
                    sample = sdl_mipmaps[i];
                    SDL_RenderCopy(renderer, sample, NULL, &texr);
                } else {
                    sample = SDL_CreateTexture(renderer,
                                                   SDL_PIXELFORMAT_BGR24,
                                                   SDL_TEXTUREACCESS_STATIC,
                                                   cv_mipmaps[i].cols,
                                                   cv_mipmaps[i].rows);
                    SDL_UpdateTexture(sample, NULL, (void*)cv_mipmaps[i].data, cv_mipmaps[i].step1());
                    SDL_RenderCopy(renderer, sample, NULL, &texr);
                    SDL_DestroyTexture(sample);
                }

            }


            SDL_RenderPresent(renderer);
        }
    }
    close(&window);

    return 0;
}


/* Handles initializing SDL window. */
void init(SDL_Window** window_ptr, SDL_Surface** surface_ptr, SDL_Renderer** renderer_ptr) {
    SDL_Init( SDL_INIT_VIDEO );

    *window_ptr = SDL_CreateWindow( "imageVimage", SDL_WINDOWPOS_UNDEFINED,
                               SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH,
                                SCREEN_HEIGHT, SDL_WINDOW_SHOWN );

    *renderer_ptr = SDL_CreateRenderer( *window_ptr, -1, SDL_RENDERER_ACCELERATED );

    SDL_SetRenderDrawColor( *renderer_ptr, 0xFF, 0xFF, 0xFF, 0xFF );

    *surface_ptr = SDL_GetWindowSurface( *window_ptr );
}

/* Handles closing the window and deallocating the memory. */
void close(SDL_Window** window) {
    SDL_DestroyWindow( *window );
    *window = NULL;
    SDL_Quit();
}

at the start of main there is USE_SDL bool, if set to true you load sdl textures, otherwise you load opencv textures. Path to file is also at the start of main. On my texture of 1170x700 pixels opencv used 669mb and SDL used 1850mb.

Elmontov
  • 11
  • 4
  • You create multiple textures in a loop. How many times do you call this function? Where do you call [SDL_DestroyTexture](https://wiki.libsdl.org/SDL2/SDL_DestroyTexture)? – kiner_shah Mar 02 '23 at 07:22
  • 1
    Please show a [mre] – Alan Birtles Mar 02 '23 at 07:23
  • I create different sizes of textures to not have to resize them and just look them up. I do not call SDL_DestroyTexture until the program terminates. But the steps I did in SDL version are the same I did in OpenCV version. In OpenCV I create the same number of images in a loop and not clear them until the end of the program. So I am lost as to why SDL uses up 3 times as much memory than OpenCV – Elmontov Mar 02 '23 at 07:26
  • @AlanBirtles added minimum reproducible example – Elmontov Mar 02 '23 at 07:52
  • How do you measure memory consumption? Which step causes extra memory consumption? – user7860670 Mar 02 '23 at 08:18
  • I monitor the memory consumption through the task manager. I can't pinpoint the step which causes the memory consumption, in opencv example I am creating new cv::Mat's and in sdl example I am creating new SDL_Texture's(from pixels of cv::Mat), I can't see where the memory difference comes from, all the images are the same size. – Elmontov Mar 02 '23 at 08:22
  • Perhaps just using SDL_Textures is more memory consuming? But its BGR24 so should be the same as cv::Mat, from which I am just calling .data to get the pixels, all of the other steps except this one are the same(see min reproducible example) – Elmontov Mar 02 '23 at 08:24
  • Not the source of the leak, but I believe `pixels.step1()` is a number of elements, not a number of bytes. (Also, you should verify that `SDL_UpdateTexture` succeeds.) – molbdnilo Mar 02 '23 at 08:33
  • What's your OS? On my system, it's rather opposite and SDL version uses much less memory (but ofc much more VRAM). In some implementations, SDL saves a copy of texture to restore it in case of device loss. You can check different implementations by setting SDL_HINT_RENDER_DRIVER before creating window/renderer. I suppose with software renderer you'll have the same memory usage as your opencv version. Render textures don't have RAM caching as they're not restored upon device loss. What was your initial problem you were trying to solve? – keltar Mar 02 '23 at 08:52
  • Windows 10. I am on a laptop so integrated gpu. I will experiment with SDL_HINT's tommorow. – Elmontov Mar 02 '23 at 09:10
  • I am creating a mosaic of images of different sizes, each of which can appear suddenly. Datasets can have quite a bit of images. With opencv it was possible to have ~800 images with highest res 800x800. But I figured that if SDL memory usage is this high it would probably not be feasible. – Elmontov Mar 02 '23 at 09:12
  • @Elmontov you can use SDL_Surface instead of texture, and get rid of renderer. That'll result in the same thing that you had in opencv. Renderer is GPU backed and could be useful for dynamic rescale, scrolling, rotating and so on. Even as-is, you can load textures dynamically when they're on screen (or presumably soon will be), but e.g. keep surface in memory and use it as a source for texture when it will be required. – keltar Mar 02 '23 at 09:17
  • You can move around or zoom into the mosaic as well. I thought about dynamically loading, but the amount of images that appear seems to be pretty high, having to load 50-100 images + resize them per screen is just to much too be at 30fps. So I figured I would keep low res images(up to say 600x600 or something) and dynamically load anything higher than that if user zooms in. – Elmontov Mar 02 '23 at 09:22
  • Although benchmarked a long time ago, but I believe originally I only kept the highest res of each image and resized it on the fly and that itself was too slow. – Elmontov Mar 02 '23 at 09:23
  • Ahhh but perhaps you are right, I am not modifying the images themselves in this scenario too much. Too many things to benchmark hahhaha. – Elmontov Mar 02 '23 at 09:24
  • Using software rendering does help. On a program where opengl takes up 1900mb sdl on software rendering takes up 2400mb. I think I will stick to this for now, really don't want to benchmark all the resizing stuff in sdl, plus having the option to fall back on gpu rendering is nice. So I think I won't try the surfaces for now. Will implement dynamic load and see how that affects the memory consumption(ultimately writing mipmaps to disk and dynamically loading might be the move, but that sounds so meh) Thanks @keltar! – Elmontov Mar 02 '23 at 17:44

0 Answers0