1

I want to convert an SVG graphic to an OpenCV Mat object. Therefore the SVG graphic is loaded into a QSvgRenderer object and afterwards converted into a QImage object from which I use its raw data to create my final Mat object:

 void scaleSvg(const cv::Mat &in, QSvgRenderer &svg, cv::Mat &out)
 {
     if (!svg.isValid())
     {
          return;
     }

     QImage image(in.cols, in.rows, QImage::Format_ARGB32);

     // Get QPainter that paints to the image
     QPainter painter(&image);
     svg.render(&painter);

     std::cout << "Image byte count: " << image.byteCount() << std::endl;
     std::cout << "Image bits: " << (int*)image.constBits() << std::endl;
     std::cout << "Image depth: " << image.depth() << std::endl;

     uchar *data = new uchar[image.byteCount()];
     memcpy(data, image.constBits(), image.byteCount());

     out = cv::Mat(image.height(), image.width(), CV_8UC4, data, CV_AUTOSTEP);
     std::cout << "New byte count: " <<  out.size() << std::endl;
     std::cout << "New depth: " << out.depth() << std::endl;
     std::cout << "First bit: " << out.data[0] << std::endl;
 }

Unfortunately, I get a "memory access violation" error when writing my resulting object into a file:

 std::cout << (int*)out.data << std::endl; // pointer can still be accessed without errors
 cv::imwrite("scaled.png", out); // memory access error

The file which is being written gets to to size of 33 Bytes not more (header data only??). On the Internet there is some explanation of pointer ownership in cv::Mat and I thought it would be released after the last reference to it is release which should not be the case since "out" is a reference. Btw. another way to convert an SVG into a cv::Mat is always welcome. As OpenCV seem to do not support SVGs this looked like a simple way to get it done.

Baradé
  • 1,290
  • 1
  • 15
  • 35

2 Answers2

2

As constBits does indeed not work and it is not always safe to assume that the number of bytes per line will be the same (it causes a segfault for me). I found the following suggestion in StereoMatching's anwser:

cv::Mat(img.height(), img.width(), CV_8UC4, img.bits(), img.bytesPerLine()).clone()

Baradé's concern about using bits is valid, but because you clone the result the Mat will copy the data from bits, so it will not be a issue.

Community
  • 1
  • 1
Chris
  • 574
  • 7
  • 24
0

usually, cv::Mat is refcounted, but this case is special. if you use an external / borrowed data pointer, you'll have to clone() the mat, to make sure it owns its own copy of the pixels, else, as soon as you leave the scope of 'scaleSvg()', the Mat 'out' holds a 'dangling pointer'.

you tried to 'new' the data to be copied, unfortunately, that does not solve it (you only added another problem there).

you also would have to delete[] the uchar *data pixels on your own, too, and you don't, so your code currently combines the worst of all worlds.

instead, try :

out = cv::Mat(image.height(), image.width(), CV_8UC4, image.constBits(), CV_AUTOSTEP).clone();
berak
  • 39,159
  • 9
  • 91
  • 89
  • thanks for the answer but it does not work this way. constBits() cannot be used since it returns a const pointer. When I use bits() which seems wrong since the destruction of cv::Mat will probably free bits() if it uses a reference counted mechanism (maybe I am wrong here) there occurs the memory mapped error even in the statement you posted and not afterwards. – Baradé May 18 '14 at 14:53