Opencv_contrib: Opencv Unwrapping does not work in Python

Created on 10 Nov 2020  路  5Comments  路  Source: opencv/opencv_contrib

System information (version)
  • OpenCV => 4.3.0
  • Operating System / Platform => Windows 10, 64 Bit, Version 2004 (OS Build 19041.572)
  • Compiler => python 3.7.6 | packaged by conda-forge | (default, Jun 1 2020, 18:11:50) [MSC v.1916 64 bit (AMD64)]
Detailed description

I wanted to unwrap an image like in this C++ tutorial (https://docs.opencv.org/4.5.0/d9/dfb/tutorial_unwrap.html) and wanted to use python for this.

It is not possible to use the histogram unwrapping algorithm of opencv in python. In Pycharm, I got the error: "Process finished with exit code -1073741819 (0xC0000005)" after executing the function cv2.phase_unwrapping_HistogramPhaseUnwrapping.unwrapPhaseMap.

I used pip install opencv-contrib-python to get opencv for python.

Steps to reproduce

```.py
import numpy as np
import cv2
import sys

print('python ' + sys.version)
print('numpy version: ' + np.__version__)
print('cv2 version: ' + cv2.__version__)

add_shadow_mask = False

X, Y = np.meshgrid(np.linspace(-1, 1, 512) * 5, np.linspace(-1, 1, 512) * 5)
img = -(X * X + Y * Y)
print('Image size: %dx%d pixels', img.shape[0], img.shape[1])

add noise

img = img + np.random.rand(img.shape[0], img.shape[1]) * 0.5

add an ignored area for shadow-mask

if add_shadow_mask:
shadow_mask = np.zeros_like(img)
shadow_mask[int(shadow_mask.shape[0] / 4): 3 * int(shadow_mask.shape[0] / 4),
int(shadow_mask.shape[1] / 4): 3 * int(shadow_mask.shape[1] / 4)] = 1
img[np.where(shadow_mask)] = 0

wrap the image

img = np.mod(img, 2 * np.pi)
img[(img == 0) & (img > 0)] = 2 * np.pi

Make sure it is float32

img = img.astype(np.float32)

show image as uint8

cv2.imshow('wrapped image', ((img+np.pi)/(2np.pi)255).astype(np.uint8))
cv2.waitKey(1)

cv2_unwrap_img = np.copy(img)

Phase unwrapping with OpenCV

create params

params = cv2.phase_unwrapping_HistogramPhaseUnwrapping_Params()
params.height = img.shape[0]
params.width = img.shape[1]
print('params.height: %i' % params.height)
print('params.width: %i' % params.width)
print('params.histThresh: %f' % params.histThresh)
print('params.nbrOfLargeBins: %i' % params.nbrOfLargeBins)
print('params.nbrOfSmallBins: %i' % params.nbrOfSmallBins)

create histogram unwrapping instance

unwrapping_instance = cv2.phase_unwrapping_HistogramPhaseUnwrapping()
unwrapping_instance.create(params)

unwrap

if not add_shadow_mask:
unwrapping_instance.unwrapPhaseMap(img, cv2_unwrap_img, shadowMask=None)
else:
unwrapping_instance.unwrapPhaseMap(img, cv2_unwrap_img, shadowMask=shadow_mask)

show image

cv2.imshow('wrapped image', ((cv2_unwrap_img+np.pi)/(2np.pi)255).astype(np.uint8))
cv2.waitKey(1)
```

bug phase_unwrapping python bindings

Most helpful comment

this is another case, where you cannot use the (python generated) object constructor, but where you have to use a create() function to obtain a valid object.

# not a valid object !!
unwrapping_instance = cv2.phase_unwrapping_HistogramPhaseUnwrapping()
# you're throwing away the actual instance (it does not work "in-place") !!
unwrapping_instance.create(params)

again, it's the same known problem as

o = cv2.ORB() # will crash if you call any method on it
o = cv2.ORB_create() # ok

here's a working version:

>>> import cv2, numpy as np
>>> img=np.random.rand(512,512)
>>> img.dtype
dtype('float64')
>>> img = img.astype(np.float32) # important, see comment below !
>>> img.dtype
dtype('float32')
>>> params = cv2.phase_unwrapping_HistogramPhaseUnwrapping_Params()
>>> params.height = img.shape[0]
>>> params.width = img.shape[1]
>>> unwrapping_instance = cv2.phase_unwrapping.HistogramPhaseUnwrapping_create(params)
>>> unwrapping_instance.unwrapPhaseMap(img)
array([[0.46003717, 0.57322496, 0.29821473, ..., 0.3278197 , 0.90027434,
        0.16586247],
       [0.26952022, 0.81003857, 0.31112662, ..., 0.7896576 , 0.27345425,
        0.59084547],
       [0.6687096 , 0.4159716 , 0.69473535, ..., 0.23015377, 0.75404686,
        0.29580984],
       ...,
       [0.3466923 , 0.31816754, 0.67690355, ..., 0.2545285 , 0.72241044,
        0.496577  ],
       [0.41468897, 0.35860705, 0.34866506, ..., 0.3936315 , 0.5833978 ,
        0.05523415],
       [0.20499845, 0.64845246, 0.5746443 , ..., 0.43141046, 0.42942145,
        0.47305965]], dtype=float32)

note, that unwrapPhaseMap() expects a float32 image as input, and will crash without error given any other format.

there should be asserts (in the c++ code), and it should be explicitly mentioned in the docs

All 5 comments

I also re-installed OpenCV 4.5.0 via anaconda and I can still see the same problem (crashing at function unwrapPhaseMap).

python 3.7.9 (default, Aug 31 2020, 17:10:11) [MSC v.1916 64 bit (AMD64)]
numpy version: 1.19.1
cv2 version: 4.5.0
Image size: %dx%d pixels 512 512
params.height: 512
params.width: 512
params.histThresh: 29.608812
params.nbrOfLargeBins: 5
params.nbrOfSmallBins: 10

this is another case, where you cannot use the (python generated) object constructor, but where you have to use a create() function to obtain a valid object.

# not a valid object !!
unwrapping_instance = cv2.phase_unwrapping_HistogramPhaseUnwrapping()
# you're throwing away the actual instance (it does not work "in-place") !!
unwrapping_instance.create(params)

again, it's the same known problem as

o = cv2.ORB() # will crash if you call any method on it
o = cv2.ORB_create() # ok

here's a working version:

>>> import cv2, numpy as np
>>> img=np.random.rand(512,512)
>>> img.dtype
dtype('float64')
>>> img = img.astype(np.float32) # important, see comment below !
>>> img.dtype
dtype('float32')
>>> params = cv2.phase_unwrapping_HistogramPhaseUnwrapping_Params()
>>> params.height = img.shape[0]
>>> params.width = img.shape[1]
>>> unwrapping_instance = cv2.phase_unwrapping.HistogramPhaseUnwrapping_create(params)
>>> unwrapping_instance.unwrapPhaseMap(img)
array([[0.46003717, 0.57322496, 0.29821473, ..., 0.3278197 , 0.90027434,
        0.16586247],
       [0.26952022, 0.81003857, 0.31112662, ..., 0.7896576 , 0.27345425,
        0.59084547],
       [0.6687096 , 0.4159716 , 0.69473535, ..., 0.23015377, 0.75404686,
        0.29580984],
       ...,
       [0.3466923 , 0.31816754, 0.67690355, ..., 0.2545285 , 0.72241044,
        0.496577  ],
       [0.41468897, 0.35860705, 0.34866506, ..., 0.3936315 , 0.5833978 ,
        0.05523415],
       [0.20499845, 0.64845246, 0.5746443 , ..., 0.43141046, 0.42942145,
        0.47305965]], dtype=float32)

note, that unwrapPhaseMap() expects a float32 image as input, and will crash without error given any other format.

there should be asserts (in the c++ code), and it should be explicitly mentioned in the docs

Hi berak,

thank you for helping me out with the object instance.
I already faced the problem with float32 and I checked the type of the shadowMask again: It should be uint8 with 0s and 255s. This is also working now.

The following code is an example for using the histogram unwrapping algorithm from OpenCV in python - feel free to use and to share. It would be nice to see more python example code of the OpenCV lib (especially for this incredible fast algorithm!)!
```.py
import numpy as np
import cv2
import sys

def float_to_uint8(image):
# Note: This function does histogram equalization from any range to range [0 255]
return ((image - np.min(np.min(image))) / np.max(np.max(image)) * 255).astype(np.uint8)

print('python ' + sys.version)
print('numpy version: ' + np.__version__)
print('cv2 version: ' + cv2.__version__)

add_shadow_mask = False

Create 2D gaussian with height of 4.5 2pi

X, Y = np.meshgrid(np.linspace(-1, 1, 512) * 5, np.linspace(-1, 1, 512) * 5)
d = np.sqrt(XX+YY)
sigma = 3.0
mu = 0.0
img = np.exp(-((d-mu)2 / (2.0 * sigma2))) * 4.5 * 2*np.pi
print('Image size: %i x %i pixels' % (img.shape[0], img.shape[1]))

add noise

img = img + np.random.rand(img.shape[0], img.shape[1]) * 0.5

Make sure it is float32 for OpenCV unwrap function!

img = img.astype(np.float32)

Show the image as np.uint8

cv2.imshow('image', float_to_uint8(img))
cv2.waitKey(1)

now we are going to wrap this image

wrapped_img = np.copy(img)
wrapped_img = np.mod(wrapped_img, 2 * np.pi)
wrapped_img[(wrapped_img == 0) & (wrapped_img > 0)] = 2 * np.pi

add an ignored area for shadow-mask

if add_shadow_mask:
shadow_mask = np.ones_like(wrapped_img) * 255
shadow_mask[int(shadow_mask.shape[0] / 4): 3 * int(shadow_mask.shape[0] / 4),
int(shadow_mask.shape[1] / 4): 3 * int(shadow_mask.shape[1] / 4)] = 0
# Shadow mask must be uint8
shadow_mask = shadow_mask.astype(np.uint8)
wrapped_img[np.where(shadow_mask == 0)] = 0

show image as uint8

cv2.imshow('wrapped image', float_to_uint8(wrapped_img))
cv2.waitKey(1)

unwrap_img = np.zeros_like(wrapped_img)

Phase unwrapping with OpenCV

create params

params = cv2.phase_unwrapping_HistogramPhaseUnwrapping_Params()
params.height = img.shape[0]
params.width = img.shape[1]
print('params.height: %i' % params.height)
print('params.width: %i' % params.width)
print('params.histThresh: %f' % params.histThresh)
print('params.nbrOfLargeBins: %i' % params.nbrOfLargeBins)
print('params.nbrOfSmallBins: %i' % params.nbrOfSmallBins)

create histogram unwrapping instance

unwrapping_instance = cv2.phase_unwrapping.HistogramPhaseUnwrapping_create(params)

Check the types

print("wrapped_img type: " + str(wrapped_img.dtype)) # should be np.float32
print("unwrap_img type: " + str(unwrap_img.dtype)) # should be np.float32

unwrap

if not add_shadow_mask:
unwrap_img = unwrapping_instance.unwrapPhaseMap(wrapped_img)
else:
# Check the type
print("shadow_mask type: " + str(shadow_mask.dtype)) # should be np.uint8
unwrap_img = unwrapping_instance.unwrapPhaseMap(wrapped_img, shadowMask=shadow_mask)

show image

cv2.imshow('unwrapped image', float_to_uint8(unwrap_img))
cv2.waitKey(0)

```

Not sure if this is the 'right' way to do it but I ended up completely uninstalling anaconda and rebuilding it. When I then made a new virtual environment the problem resolved. If others have the same issue this might work too.

Hi,

I also used OpenCV 4.2.X installed with Anaconda and the anaconda-forge channel and the opencv unwrap phase package did not work.

You only have to update the opencv to the latest version (currently 4.5.X) with the following command:
conda update opencv -c conda-forge

Note:
before this check if you installed the opencv library with conda-forge using command conda list! it is also possible to use the pip installation pip install opencv-contrib. The opencv-contrib pip package does also provide the unwrap algorithm of opencv.

Was this page helpful?
0 / 5 - 0 ratings