Numpy gradient() with OpenCV is never negative

Viewed 868

I've been playing around with OpenCV and Numpy and I just noticed that when taking the gradient of a grayscale image, it is never negative. I haven't tried it with colour. Why is this happening?

import cv2
import numpy as np

video_capture = cv2.VideoCapture(0)
ret, frame = video_capture.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

Gx,Gy = np.gradient(gray)
print "Gradient X"
print Gx[Gx<0]
print "\n\nGradient Y"
print Gy[Gy<0]

test_gx,test_gy = np.gradient(np.random.rand(10,10))
print "\n\nRandom Gradient X"
print test_gx[test_gx<0]
print "\n\nRandom Gradient Y"
print test_gy[test_gy<0]

Output:

Gradient X
[]


Gradient Y
[]


Random Gradient X
[-0.29390267 -0.57673461 -0.44496392 -0.18760622 -0.37758506 -0.02940484
 -0.09905821 -0.12909104 -0.22726427 -0.17175216 -0.08635539 -0.02969735
 -0.05939525 -0.02111877 -0.29544231 -0.00770492 -0.31914318 -0.12239945
 -0.30133711 -0.08622408 -0.04524624 -0.03998993 -0.40993412 -0.13088891
 -0.2491598  -0.14143661 -0.04846196 -0.30055182 -0.00323793 -0.49329475
 -0.07413882 -0.17564328 -0.13582564 -0.13390455 -0.07373904 -0.09886662
 -0.08773134 -0.06185525 -0.00729722 -0.18979578 -0.17536514 -0.25615883
 -0.26232646 -0.05403582 -0.05968006 -0.26843946 -0.26621363 -0.22504563
 -0.26470668 -0.02397445 -0.0782202  -0.0476783  -0.13333021]


Random Gradient Y
[-0.29521569 -0.23485359 -0.15549854 -0.00142858 -0.07242038 -0.32181099
 -0.26111095 -0.10534067 -0.20442231 -0.05366269 -0.01339253 -0.01597691
 -0.10289234 -0.14128584 -0.1705936  -0.14574768 -0.17571418 -0.04868263
 -0.46254485 -0.11305848 -0.208527   -0.03967778 -0.06671698 -0.35017431
 -0.68122837 -0.37782762 -0.30486289 -0.23501836 -0.25857174 -0.33494929
 -0.27348378 -0.319753   -0.06541161 -0.29203723 -0.1875851  -0.07090711
 -0.07814288 -0.20096383 -0.31743231 -0.17801282 -0.02341537 -0.11358367
 -0.3985152  -0.07670008 -0.02248808 -0.35775219 -0.28470273]
2 Answers

To elaborate a bit on yhenon’s answer : obviously the gradient of an unsigned integer array may have negative values. The point is that np.gradient(myarray) is of the same type as myarray, so it will force negative values to high positive values :

>>> myarray = np.array([1,3,5,7,8,6,4,2], dtype='uint8')
>>> np.gradient(myarray)
array([   2. ,    2. ,    2. ,    1.5,  127.5,  126. ,  126. ,  254. ])

Such behaviour is a bug, which has actually been reported as such and in principle fixed in numpy 1.18.1.

The workaround for earlier versions is indeed to cast the input into a signed integer type is just to allow negative values for the gradient itself :

>>> np.gradient(myarray.astype('int16'))
array([ 2. ,  2. ,  2. ,  1.5, -0.5, -2. , -2. , -2. ])

Beware that in order to handle correctly large variations one should cast to a twice larger type at least — i.e. uint8 to int16, uint32 to int64, etc. For the numpy bugfix they chose to convert to float64.

Related