2

I'm working on project named "Faciel Actions Units Detection" I'm using python2.7 and opencv 2.4

The error:

pickle.PicklingError: Can't pickle <type 'cv2.Boost'>: it's not the same object as cv2.Boost

A partial traceback, transcribed from a screenshot:

Loading classifier for action unit 27
Traceback (most recent call last):
  File "C:\Python27\audetect-master\audetect-interactive.py", line 59, in <module>
    main()
  File "C:\Python27\audetect-master\audetect-interactive.py", line 18, in main
    active_aus = detector.detect()
  File "C:\Python27\audetect-master\detect.py", line 67, in detect
    initial_points = self.ffdetector.locate_features(first)
  File "C:\Python27\audetect-master\detect.py", line 183, in locate_features
    thread.start()
  File "C:\Python27\lib\multiprocessing\process.py", line 130, in start
    self._popen = Popen(self)
  File "C:\Python27\lib\multiprocessing\forking.py", line 227, in __init__
    dump(process_obj, to_child, HIGHEST_PROTOCOL)
  File "C:\Python27\lib\multiprocessing\forking.py", line 199, in dump
    ForkingPickler(file, protocol).dump(obj)
  File "C:\Python27\lib\pickle.py", line 224, in dump
    self.save(obj)
  File "C:\Python27\lib\pickle.py", line 331, in save
    self.save_reduce(obj=obj, *rv)
  File "C:\Python27\lib\pickle.py", line 425, in save_reduce
    save(state)
  File "C:\Python27\lib\pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "C:\Python27\lib\pickle.py", line 655, in save_dict
    self._batch_setitems(obj.iteritems())
  File "C:\Python27\lib\pickle.py", line 687, in _batch_setitems
    save(v)
  File "C:\Python27\lib\pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "C:\Python27\lib\multiprocessing\forking.py", line 67, in dispatcher
    self.save_reduce(obj=obj, *rv)
  File "C:\Python27\lib\pickle.py", line 401, in save_reduce
    save(args)
  File "C:\Python27\lib\pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "C:\Python27\lib\pickle.py", line 554, in save_tuple
    save(element)
Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
  • 1
    Please include a minimal idea of your Python code that you tried to run. – Todd Palmer May 14 '18 at 19:35
  • thank you so much for your attention this is function def detect(self): """ Returns a tuple of active AUs """ first = self.sequence[0] last = self.sequence[-1] initial_points = self.ffdetector.locate_features(first) final_points = self.ffdetector.locate_features(last) if not initial_points or not final_points: sys.stderr.write("Error: could not locate face in supplied sequence") sys.stderr.write("\n") return [] aus = self.determine_aus(initial_points, final_points) return aus – chaimaa3012 May 14 '18 at 19:51
  • @ToddPalmer thank you for your attention the function above that rises the error – chaimaa3012 May 14 '18 at 20:17
  • 1
    Please, do not use screenshots of code and tracebacks. We can't copy those, nor can screenreaders for people with visual impairments read the text. Both error messages and code are *text*, please post them as text. – Martijn Pieters May 17 '18 at 15:08
  • I note that the traceback screenshot is also not *complete*. – Martijn Pieters Apr 03 '19 at 11:18
  • @MartijnPieters Wow, nice transcription! – jtlz2 Apr 03 '19 at 11:23

1 Answers1

4

Pickle is used by the multiprocessing module to communicate between the different parts, and in the programming guidelines it explains that you must ensure that all your data that you pass between processes must be compatible with pickling:

Picklability: Ensure that the arguments to the methods of proxies are picklable.

You are using data that is not picklable.

Specifically, what is going wrong is that the cv2.Boost class doesn't quite tell the truth on how you can create more copies of the class. pickle stores references to classes and functions, not their definition, because that's way more efficient. This means that instances only need to store the data for that instance, not also all of the class hierarchy and method definitions.

In order to do this, pickle takes the module a class or function is defined in, and the name of the object, and together that's the reference to the class or function. It then double-checks that it can use that name to load the same class or function back again.

That sanity check failed for the cv2.Boost class. You have instances of a class is named Boost and that claims to have come from the cv2 module, but when pickle then goes to the cv2 module and looks up the Boost attribute of that module it found a different object. This means your data could not be unpickled.

There are ways to correct this; you need to teach the pickle module to use a different function to load the same data again, using the copyreg.pickle() function; if such a registration exists for the cv2.Boost class then pickle will not make the above check:

import copyreg
import cv2

def _pickle_boost(boost):
    return cv2.Boost, (
        boost.trainData,
        boost.tflag,
        boost.responses, 
        boost.varIdx,
        boost.sampleIdx,
        boost.varType,
        boost.missingDataMask,
        boost.paramsd,
    )

copyreg.pickle(cv2.Boost().__class__, _pickle_boost)

WARNING: I didn't actually test if the above will work, because I don't have a 2.4.x version of cv2 installed locally; I merely referred to the cv2.Boost() documentation to guess at what attributes such a class would have. You'll probably need to adjust it. The idea is that for the cv2.Boost().__class__ type, the _pickle_boost() function is called, returning a callable (cv2.Boost) to create a new instance, and the arguments that you want to pass to that callable.

If any of the above values are themselves more cv2 types that exhibit the same problem, then you need to register more functions.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Is there any chance you could add a minimal complete working example? Trying to get my head round this one. Trying to understand how to cache a cv2.ORB() object (in Flask) but failing miserably.. Thanks for all help – jtlz2 Apr 03 '19 at 09:18
  • @jtlz2: it's already a MCVE, for the `cv2.Boost` type. As I already state in my answer, I can't test this with the specific type, but the general principle should be the same for `cv2.ORB()`. Create a function that returns the constructor and a tuple of values given an instance, then register that function for the actual class object for `cv2.ORB()` instances. – Martijn Pieters Apr 03 '19 at 11:04
  • @jtlz2: so `def _pickle_orb(orb): return cv2.ORB, (orb.nfeatures, orb.scaleFactor, orb.nlevels, orb.edgeThreshold, orb.firstLevel, orb.WTA_K, orb.scoreType, orb.patchSize)`, where I assume that `cv2.ORB()` instances have the same attributes as [the constructor parameters](https://docs.opencv.org/2.4/modules/features2d/doc/feature_detection_and_description.html#orb-orb), then `copyreg.pickle(cv2.ORB().__class__, _pickle_orb)`. – Martijn Pieters Apr 03 '19 at 11:05
  • 1
    @jtlz2: what then happens is that when pickle encounters another `cv2.ORB()` instance, it'll find the `_pickle_orb()` function in the registry, call it with that instance as argument, and is given back the `(cv2.ORB, (....))` tuple to store in the pickle data stream. When unpickling, pickle finds the `cv2.ORB` reference, and calls `cv2.ORB(....)` to create a new instance with the same values. – Martijn Pieters Apr 03 '19 at 11:07
  • your explanation is much appreciated and all much clearer to me now - thank you thank you! – jtlz2 Apr 03 '19 at 11:29
  • How would one go about "teaching" flask-caching to serialize via this method? – jtlz2 Apr 03 '19 at 11:31
  • 1
    @jtlz2: Flask-Caching doesn't need to be taught anything. It just uses `pickle`, and it is `pickle` that uses the `copyreg` registry. – Martijn Pieters Apr 03 '19 at 11:34
  • OK got it, thanks. The ORB registration above doesn't work because it rather uses set and get methods e.g. getScaleFactor()/setScaleFactor() - does that mean different pickle/unpickle methods - or just e.g. orb.scaleFactor -> orb.setScaleFactor? – jtlz2 Apr 03 '19 at 11:38
  • 1
    @jtlz2: just call the getters then. The goal is to produce the values that you need to pass to the `cv2.ORB()` constructor to recreate the value. – Martijn Pieters Apr 03 '19 at 11:39