python - 如何停止在 Python 中打印 OpenCV 错误消息

标签 python opencv

this question here相同,除了 Python,不是 C++。

我在 OpenCV 中有一条错误消息正在打印,即使我对其进行了 except 捕获。

solution to the linked question建议使用重定向功能,但 this comment表明 OpenCV 的 Python 中不存在该函数。

如何停止打印 OpenCV 错误消息,同时仍允许我准确打印我想要的内容?

最佳答案

在写这个答案时(OpenCV 3.4.1 是最后发布的版本),没有办法只过滤默认错误处理程序的输出(我能想到),也没有办法改变错误处理程序。

但是,您的问题让我开始思考 — 在 highgui 模块中,我们已经有几个函数可以让我们为鼠标、轨迹栏和按钮事件设置 Python 回调,因此我们可以从中获得灵感对这个新功能进行编码和修补。

让我们使用版本 3.4.1。感兴趣的文件是 modules/python/src2/cv2.cpp .我们将从函数中汲取灵感开始 OnMousepycvSetMouseCallback .

让我们让 Python 错误处理程序具有与 C++ error handler 匹配的签名:

error_handler([int]status, [str]func_name, [str]err_msg, [str]file_name, [int]line, [obj]userdata) -> None

我们还要添加对重置为默认错误处理程序的支持——这是 highgui 函数还没有做的事情。

首先,我们需要一个静态错误处理函数,它将参数从 C++ 编码到 Python,并调用适当的 Python 函数来处理错误。就像我们从中获得灵感的函数一样,我们将使用用户数据参数来保存一个由函数对象和可选的 Python 用户数据组成的元组。

static int OnError(int status, const char *func_name, const char *err_msg, const char *file_name, int line, void *userdata)
{
    PyGILState_STATE gstate;
    gstate = PyGILState_Ensure();

    PyObject *o = (PyObject*)userdata;
    PyObject *args = Py_BuildValue("isssiO", status, func_name, err_msg, file_name, line, PyTuple_GetItem(o, 1));

    PyObject *r = PyObject_Call(PyTuple_GetItem(o, 0), args, NULL);
    if (r == NULL) {
        PyErr_Print();
    } else {
        Py_DECREF(r);
    }

    Py_DECREF(args);
    PyGILState_Release(gstate);

    return 0; // The return value isn't used
}

接下来,我们需要编写实现Python与C++绑定(bind)的函数。但是,由于我的 suspicions of potential memory leaks在我们从中汲取灵感的函数中,我们将添加一些内容来解决这个问题——我们将跟踪最新的用户数据对象并在必要时取消引用它。

// Keep track of the previous handler parameter, so we can decref it when no longer used
static PyObject* last_on_error_param = NULL;

static PyObject *pycvRedirectError(PyObject*, PyObject *args, PyObject *kw)
{
    const char *keywords[] = { "on_error", "param", NULL };
    PyObject *on_error;
    PyObject *param = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kw, "O|O", (char**)keywords, &on_error, &param))
        return NULL;

    if ((on_error != Py_None) && !PyCallable_Check(on_error))  {
        PyErr_SetString(PyExc_TypeError, "on_error must be callable");
        return NULL;
    }
    if (param == NULL) {
        param = Py_None;
    }

    if (last_on_error_param) {
        Py_DECREF(last_on_error_param);
        last_on_error_param = NULL;
    }

    if (on_error == Py_None) {
        ERRWRAP2(redirectError(NULL));        
    } else {
        last_on_error_param = Py_BuildValue("OO", on_error, param);
        ERRWRAP2(redirectError(OnError, last_on_error_param));
    }
    Py_RETURN_NONE;
}

最后,我们必须注册我们的 "special method" .与我们从中获得灵感的功能不同,我们不依赖于某些 GUI,并且我们希望它始终可用,因此我们将修改代码如下所示:

static PyMethodDef special_methods[] = {
  {"redirectError", (PyCFunction)pycvRedirectError, METH_VARARGS | METH_KEYWORDS, "redirectError(onError [, param]) -> None"},
#ifdef HAVE_OPENCV_HIGHGUI
  {"createTrackbar", pycvCreateTrackbar, METH_VARARGS, "createTrackbar(trackbarName, windowName, value, count, onChange) -> None"},
  {"createButton", (PyCFunction)pycvCreateButton, METH_VARARGS | METH_KEYWORDS, "createButton(buttonName, onChange [, userData, buttonType, initialButtonState]) -> None"},
  {"setMouseCallback", (PyCFunction)pycvSetMouseCallback, METH_VARARGS | METH_KEYWORDS, "setMouseCallback(windowName, onMouse [, param]) -> None"},
#endif
  {NULL, NULL},
};

现在我们可以重建 OpenCV,安装并测试它。我编写了以下脚本来演示功能:

import cv2

def verbose_error_handler(status, func_name, err_msg, file_name, line, userdata):
    print "Status = %d" % status
    print "Function = %s" % func_name
    print "Message = %s" % err_msg
    print "Location = %s(%d)" % (file_name, line)
    print "User data = %r" % userdata

def silent_error_handler(status, func_name, err_msg, file_name, line, userdata):
    pass


print "** Default handler"
try:
    cv2.imshow("", None) # This causes an assert
except cv2.error as e:
    pass

print "** Using verbose handler"
cv2.redirectError(verbose_error_handler, 42)
try:
    cv2.imshow("", None) # This causes an assert
except cv2.error as e:
    pass

print "** Using silent handler"
cv2.redirectError(silent_error_handler)
try:
    cv2.imshow("", None) # This causes an assert
except cv2.error as e:
    pass

print "** Back to default handler"
cv2.redirectError(None)
try:
    cv2.imshow("", None) # This causes an assert
except cv2.error as e:
    pass

使用我的 OpenCV 补丁版本(基于上述说明),我在控制台上得到以下输出:

** Default handler
OpenCV(3.4.1) Error: Assertion failed (size.width>0 && size.height>0) in cv::imshow, file F:\GitHub\opencv\modules\highgui\src\window.cpp, line 364
** Using verbose handler
Status = -215
Function = cv::imshow
Message = size.width>0 && size.height>0
Location = F:\GitHub\opencv\modules\highgui\src\window.cpp(364)
User data = 42
** Using silent handler
** Back to default handler
OpenCV(3.4.1) Error: Assertion failed (size.width>0 && size.height>0) in cv::imshow, file F:\GitHub\opencv\modules\highgui\src\window.cpp, line 364

当我开始写这个答案时,我的一个想法是,自从 default error handler使用以下格式化字符串将所有这些消息输出到 stderr

"OpenCV(%s) Error: %s (%s) in %s, file %s, line %d"

我们也许可以 Hook stderr 流,并过滤掉任何以常量前缀开头的行。 las,我无法实现这一目标。也许其他人可以?


编辑:补丁合并到 opencv:master 中并做了一些小修改(主要是我们去掉了用户数据参数,因为它在 Python 中是不必要的)。

关于python - 如何停止在 Python 中打印 OpenCV 错误消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49602465/

相关文章:

python - 我使用cv2.connectedComponentsWithStats时产生噪音

c++ - 带加速度的卡尔曼滤波器

python - 区分 PIL 和 CV2 图像

python - Swift 中函数的返回类型不一致

python - BeautifulSoup 解码错误

javascript - Python 到 Node/JS

java - android studio 上的错误 :Plugin with id 'kotlin-android' not found.

python - 访问 defaultdict 工厂中的 key

python - 如何在 Python 中打印不带逗号的元组列表?

opencv - 如何在不终止子进程的情况下在被调用进程中使用子进程的输出?