【问题标题】:Can't pickle <type 'cv2.BRISK'>: attribute lookup cv2.BRISK failed无法腌制 <type 'cv2.BRISK'>:属性查找 cv2.BRISK 失败
【发布时间】:2025-11-26 15:30:01
【问题描述】:

我正在尝试同时运行多个CMT trackers。出于这个原因,我设置了一个线程池:

import argparse
import cv2
from multiprocessing import Pool
import numpy as np
import os
import sys
import time

import VARtracker
import util

CMT1 = VARtracker.CMT()

... # code lines removed

# Clean up
cv2.destroyAllWindows()

if args.inputpath is not None:
    # If a path to a file was given, assume it is a single video file
    if os.path.isfile(args.inputpath):
        cap = cv2.VideoCapture(args.inputpath)
        # Skip first frames if required
        if args.skip is not None:
            cap.set(cv2.cv.CV_CAP_PROP_POS_FRAMES, args.skip)

    # Otherwise assume it is a format string for reading images
    else:
        cap = util.FileVideoCapture(args.inputpath)
        # Skip first frames if required
        if args.skip is not None:
            cap.frame = 1 + args.skip

    # Check if videocapture is working
    if not cap.isOpened():
        print 'Unable to open video input.'
        sys.exit(1)

    # Read first frame
    status, im0 = cap.read()
    im_gray0 = cv2.cvtColor(im0, cv2.COLOR_BGR2GRAY)
    im_draw = np.copy(im0)

# Getting initial bounding boxes
tl1 = [405, 160]
br1 = [450, 275]

VARtracker.initialise(CMT1, im_gray0, tl1, br1)

frame = 1
while True:
    pool = Pool(processes=4)
    print frame

    # Read image
    status, im = cap.read()
    if not status:
        break
    im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
    im_draw = np.copy(im)

    tic = time.time()
    # Serial approach
    #res1 = VARtracker.process_frame(CMT1, im_gray)

    # Parallel approach
    res1 = pool.apply_async(VARtracker.process_frame, (CMT2, im_gray))
    pool.close()
    pool.join()
    res1 = res1.get()
    toc = time.time()

    # Display results
    if res1.has_result:
        cv2.line(im_draw, res1.tl, res1.tr, (255, 0, 0), 4)
        cv2.line(im_draw, res1.tr, res1.br, (255, 0, 0), 4)
        cv2.line(im_draw, res1.br, res1.bl, (255, 0, 0), 4)
        cv2.line(im_draw, res1.bl, res1.tl, (255, 0, 0), 4)

    if not args.quiet:
        cv2.imshow('main', im_draw)
        cv2.waitKey(pause_time)

    # Remember image
    im_prev = im_gray
    frame += 1

每当我评论 串行方法 并尝试使用线程(并行方法)时,都会遇到以下错误:

Traceback(最近一次调用最后一次):

文件“/home/rafael/GIT/CMT-Tracker/VaretoCMT/VARmain.py”,第 128 行,模块 res1 = res1.get()

文件“/usr/lib/python2.7/multiprocessing/pool.py”,第 558 行,在 get raise self._value 中

cPickle.PicklingError: Can't pickle : 属性查找 cv2.BRISK 失败

可以在VARmain.py, VARtracker.py and util.py 上遇到其他文件。

我尝试了很多方法,但仍然没有找到克服 Python 限制的方法。我发现我不能序列化类方法,只能序列化函数。如果可能的话,我想用 Python 标准库来解决它。

【问题讨论】:

  • 您的 GitHub 不允许访问那里的文件...
  • 一个 PyCharm 项目可以在这里找到:link
  • 在此处找到缺少的用于 Python 3.5 构建的 cv2 库:lfd.uci.edu/~gohlke/pythonlibs/#opencv 但在安装并运行 VARmain 后,出现此错误:VARtracker.initialise(CMT1, im_gray0, tl1, br1) NameError : 名称“im_gray0”未定义
  • if args.inputpath is not None: 循环永远不会到达,所以 cap 永远不会被设置,导致上述错误 - 我应该调用 VARmain 指向 video_tennis 目录吗?请告知@RafaelHenrique 如何执行此操作,以便我可以尝试提供帮助,谢谢

标签: python multithreading opencv


【解决方案1】:

我设法解决了。感谢@Matt 和@Yamaneko。 基本上,我将读取图像的块移到了工作函数中。因此,如果 pool size = 6 并且有六个边界框,则每个帧将被读取六次(在每个 worker 内)。这是我发现让它发挥作用的唯一方法。

当前版本可以在here找到。

import cv2 as cv
import multiprocessing as mp
import time

def worker(folder_path, list_name, top_left, bot_right, index):
    frame_path = folder_path + '/' + list_name[0]
    image_0 = cv.imread(frame_path)
    gray_0 = cv.cvtColor(image_0, cv.COLOR_BGR2GRAY)

    cmt = VARtracker.CMT()
    cmt.initialise(gray_0, top_left, bot_right)
    box_queue = mp.Queue()

    for name in list_name:
        frame_path = folder_path + '/' + name
        image_now = cv.imread(frame_path)
        gray_now = cv.cvtColor(image_now, cv.COLOR_BGR2GRAY)

        cmt.process_frame(gray_now)
        if cmt.has_result:
            print index, name, zip(cmt.tl, cmt.br)
            output.put((index, name, zip(cmt.tl, cmt.br)))
    print 'Process {} finished'.format(index)

def VARmethod(folder_path, final_frame, top_left, bot_right):
    tic = time.time()

    if len(top_left) == len(bot_right):
        list_frame = [index for index in range(1, final_frame + 1)]
        list_name = [str(index) + '.jpg' for index in list_frame]

        pool = mp.Pool(5)
        for index in range(0, len(top_left)):
            pool.apply_async(worker, args=(folder_path, list_name, top_left[index], bot_right[index], index))
        pool.close()
        pool.join()

        print 'Finished with the script'

    toc = time.time()
    print output.qsize()
    print (toc - tic)

【讨论】:

    【解决方案2】:

    在你的类周围试试这个代码段(这不是我的代码,感谢 Steven Bethard)——这是一种 pickle 类的解决方法;多处理模块使用 pickle 将作业发送给工人:

    def _pickle_method(method):
        func_name = method.im_func.__name__
        obj = method.im_self
        cls = method.im_class
        return _unpickle_method, (func_name, obj, cls)
    
    def _unpickle_method(func_name, obj, cls):
        for cls in cls.mro():
            try:
                func = cls.__dict__[func_name]
            except KeyError:
                pass
            else:
                break
        return func.__get__(obj, cls)
    
    import copy_reg
    import types
    copy_reg.pickle(types.MethodType, _pickle_method, _unpickle_method)
    

    multiprocessing这里使用它的例子Can't pickle <type 'instancemethod'> when using python's multiprocessing Pool.map()

    并不是说这很容易转换。如果您真的想要多线程,我建议使用带有 OpenMP 的 Cython。您可以只重写程序中需要与nogil 语句和from cython.parallel cimport prange 并行循环的部分......

    【讨论】: