简体   繁体   中英

Python Class Member Variable Containing OpenCV data initializes as a pointer

I have created a simple Python class that takes two values as arguments when instantiating it ( contents and resolution ). These arguments are then assigned to class members in the class' __init__ function.

For some reason, one of the class members ( contents ) appears to be a reference/pointer while the other is not ( resolution ). If I edit contents on one class, it updates the other even though I've instantiated two completely separate instances of the class. Here's the stripped down example:

TestBaseClasses.py

import cv2

class Frame():
    def __init__(self, contents, resolution):
        self.contents = contents
        self.resolution = [resolution[0], resolution[1]]

    def resize(self, fx, fy):
        self.contents = cv2.resize(self.contents, (0, 0), fx=fx, fy=fy)

test.py

import cv2
from copy import deepcopy
from TestBaseClasses import Frame

frame = cv2.imread("test_img.png")
h, w, _ = frame.shape

ProcessFrame = Frame(frame, [h, w])
OriginalFrame = Frame(frame, [h, w])

print(type(frame))
print(ProcessFrame is OriginalFrame)
print(ProcessFrame.contents is OriginalFrame.contents)
print(ProcessFrame.resolution is OriginalFrame.resolution)
print(id(ProcessFrame.contents))
print(id(OriginalFrame.contents))

print("########################")

ProcessFrame = Frame(deepcopy(frame), [h, w])
OriginalFrame = Frame(deepcopy(frame), [h, w])

print(type(frame))
print(ProcessFrame is OriginalFrame)
print(ProcessFrame.contents is OriginalFrame.contents)
print(ProcessFrame.resolution is OriginalFrame.resolution)
print(id(ProcessFrame.contents))
print(id(OriginalFrame.contents))

Output from test.py

<class 'numpy.ndarray'>
False
True
False
4405193824
4405193824
########################
<class 'numpy.ndarray'>
False
False
False
4409151200
4491382256

As you can see, I have to make a deepcopy of the frame variable in order to prevent it from being linked to the same reference in memory. You can also see that the frame variables' type is a numpy array (not some sort of reference/pointer).

When I use example one (without deepcopy ) and edit the contents member in ProcessFrame , it edits the contents member in OriginalFrame . This does not happen when I do the same for resolution .

What in the world is going on here? I'd like to not have to import the copy module and use deepcopy if I can avoid it.

It should be noted that I am using Python 3.6.


Update

I am beginning to think this is related to the cv2.imread() function. If I create a new class member and assign contents to it, it has the same value using id() (ie it's pointing to the same place in memory).

In the OpenCV docs here:
https://docs.opencv.org/3.4/d5/d98/tutorial_mat_operations.html

In the section 'Memory management and reference counting' , it mentions that the data read in using OpenCV imread function is the equivalent of Mat in C++. It goes on:

Mat is a structure that keeps matrix/image characteristics (rows and columns number, data type etc) and a pointer to data. So nothing prevents us from having several instances of Mat corresponding to the same data. A Mat keeps a reference count that tells if data has to be deallocated when a particular instance of Mat is destroyed.

Thus, although Python returns <class 'numpy.ndarray'> as the type, it is actually a pointer/reference (don't know the difference in Python, someone please expand on that) to that array in memory.In it's example they go on to show how to make copies of the image data:

img = cv.imread('image.jpg')
_img1 = np.copy(img)

In my case, I just updated the code in the Frame class where self.contents is initialized to:

self.contents = np.copy(contents)

And that fixed the problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM