Visualize in memory OpenCV image or matrix from GDB pretty printers

asmwarrior asmwarrior@gmail.com
Tue Apr 30 08:44:00 GMT 2013


Hi, I'm pleased to share the script to show an in memory cv::Mat or Images when debugging.
Suppose you have such C++ code:

#include <opencv/cv.h>
#include <opencv/highgui.h>
using namespace cv; 
...
Mat img = imread("1.jpg", CV_LOAD_IMAGE_GRAYSCALE);
...

When debugging those code under GDB, I would like to see how the in memory data "img" looks like. Thanks to GDB and OpenCV, both of them have Python interface, so here is the python pretty script (I released the script code under GPLv3)

Before that, you need 
1, GDB with python enabled
2, OpenCV python interface (under Windows, it is one file cv2.pyd)
3, install python, numpy

############################################################
#filename: cvplot.py
import gdb
import cv2.cv as cv
import sys


class PlotterCommand(gdb.Command):
    def __init__(self):
        super(PlotterCommand, self).__init__("plot",
                                             gdb.COMMAND_DATA,
                                             gdb.COMPLETE_SYMBOL)
    def invoke(self, arg, from_tty):
        args = gdb.string_to_argv(arg)
        
        
        # generally, we type "plot someimage" in the GDB commandline
        # where "someimage" is an instance of cv::Mat
        v = gdb.parse_and_eval(args[0])
        
        # the value v is a gdb.Value object of C++
        # code's cv::Mat, we need to translate to
        # a python object under cv2.cv
        image_size =  (v['cols'],v['rows'])
        # print v
        # these two below lines do not work. I don't know why
        # channel = gdb.execute("call "+ args[0] + ".channels()", False, True)
        # channel = v.channels();
        CV_8U =0
        CV_8S =1
        CV_16U=2
        CV_16S=3
        CV_32S=4
        CV_32F=5
        CV_64F=6
        CV_USRTYPE1=7
        CV_CN_MAX = 512
        CV_CN_SHIFT = 3
        CV_MAT_CN_MASK = (CV_CN_MAX - 1) << CV_CN_SHIFT
        flags = v['flags']
        channel = (((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1
        CV_DEPTH_MAX = (1 << CV_CN_SHIFT)
        CV_MAT_DEPTH_MASK = CV_DEPTH_MAX - 1
        depth = (flags) & CV_MAT_DEPTH_MASK
        IPL_DEPTH_SIGN = 0x80000000
        cv_elem_size = (((4<<28)|0x8442211) >> depth*4) & 15
        if (depth == CV_8S or depth == CV_16S or depth == CV_32S):
                mask = IPL_DEPTH_SIGN
        else:
                mask = 0
        ipl_depth = cv_elem_size*8 | mask     
        img = cv.CreateImageHeader(image_size, ipl_depth, channel)
        
        # conver the v['data'] type to "char*" type
        char_type = gdb.lookup_type("char")
        char_pointer_type =char_type.pointer()
        buffer = v['data'].cast(char_pointer_type)
        
        # read bytes from inferior's memory, because
        # we run the opencv-python module in GDB's own process
        # otherwise, we use memory corss processes        
        buf = v['step']['buf']
        bytes = buf[0] * v['rows'] # buf[0] is the step? Not quite sure.
        inferior = gdb.selected_inferior()
        mem = inferior.read_memory(buffer, bytes)
        
        # set the img's raw data
        cv.SetData(img, mem)
        
        # create a window, and show the image
        cv.NamedWindow('debugger')
        cv.ShowImage('debugger', img)
        
        # the below statement is necessory, otherwise, the Window
        # will hang
        cv.WaitKey(0) 
        
PlotterCommand()
############################################################
The script above add a new GDB command "plot" to show the in memory data cv::Mat.
Now, you can simply type: "source cvplot.py" to load this script to GDB, then type: "plot img" to show the cv::Mat in OpenCV's Window, to let GDB continue, just close the debugger window.

BTW: I found one issue, if I uncomment "# print v" in the script source, then this script will complain such message and abort:
Python Exception <type 'exceptions.UnicodeEncodeError'> 'ascii' codec can't encode characters in position 80-100: ordinal not in range(128): 
Error occurred in Python command: 'ascii' codec can't encode characters in position 80-100: ordinal not in range(128)

But if I run the command "print img" directly in the GDB's command line, it shows:
$2 = {flags = 1124024320, dims = 2, rows = 243, cols = 322, data = 0xb85020 "\370\362趔è²
鲵篝躜貘篚赧鲶篥躐篝趔篝趔趑趑趑趑蝓趔\372\357豸趑趑趑趑趑趑趑趑趑趱貂趑趑趔篝趑篌趑趑趑趔篌篌篝貂趑篝趱鲺豸趑趔篝趑趔篝篝趱趑趑鲴蝥铛鲳豸趑趑趑趑趑趑趑貂趑趑貂蝮趱貂趑篚躞篚豸貂趑趑趑趑趑篌趑趱豸篌貊"..., refcount = 0xb981c8, datastart = 0xb85020 "\370\362趔è²
鲵篝躜貘篚赧鲶篥躐篝趔篝趔趑趑趑趑蝓趔\372\357豸趑趑趑趑趑趑趑趑趑趱貂趑趑趔篝趑篌趑趑趑趔篌篌篝貂趑篝趱鲺豸趑趔篝趑趔篝篝趱趑趑鲴蝥铛鲳豸趑趑趑趑趑趑趑貂趑趑貂蝮趱貂趑篚躞篚豸貂趑趑趑趑趑篌趑趱豸篌貊"..., dataend = 0xb981c6 "\255\272\001", datalimit = 0xb981c6 "\255\272\001", allocator = 0x0, size = {p = 0x22fe64}, step = {p = 0x22fe8c, buf = {322, 1}}}

I'm not sure how to fix this, but surely I can see it was some issue that python try to decode the raw buffer to normal text. (I'm using WinXP)

Many thanks to Tromey, Andre_, Pmuldoon for their kind help in GDB IRC, also thanks to Hui Ning(ninghui8673@126.com) for great help and suggestion.

Yuanhui Zhang








More information about the Gdb mailing list