- Python 3.6
- opencv-python==3.4.2.16
- numpy==1.16.2
针对不同数据类型的numpy.ndarray,cv2.imshow()处理结果不同。
本文就此进行试验,新建不同类型的numpy.ndarray,使用cv2.imshow()显示结果分析。
使用pycharm按住Ctrl加鼠标左键进入函数cv2.imshow()可以看见文档
The function imshow displays an image in the specified window. If the window was created with the
. cv::WINDOW_AUTOSIZE flag, the image is shown with its original size, however it is still limited by the screen resolution.
. Otherwise, the image is scaled to fit the window. The function may scale the image, depending on its depth:
.
. - If the image is 8-bit unsigned, it is displayed as is.
. - If the image is 16-bit unsigned or 32-bit integer, the pixels are divided by 256. That is, the
. value range [0,255*256] is mapped to [0,255].
. - If the image is 32-bit or 64-bit floating-point, the pixel values are multiplied by 255. That is, the
. value range [0,1] is mapped to [0,255].
机翻:
imshow函数在指定窗口中显示图像。如果窗口是用WINDOW_AUTOSIZE标志,图像以其原始大小显示,但仍受屏幕分辨率的限制。
否则,将缩放图像以适应窗口。该函数可根据图像的深度缩放图像:
如果图像是8位无符号的,则按原样显示。
如果图像是16位无符号或32位整数,则像素除以256。也就是说值范围[0, 255*256]映射到[0, 255]。
如果图像是32位或64位浮点,则像素值乘以255。也就是说值范围[0, 1]映射到[0, 255]。
写demo演示
测试不同数据类型
uint8
import cv2
import numpy as np
height, width = 200, 200
def test_uint8():
img_a = np.zeros((height, width, 3), dtype='uint8')
img_a[:, :, 0] = 255
print(f'img_a[0, 0, 0]={img_a[0, 0, 0]}\n'
f'type(img_a)={type(img_a)}\nimg_a.dtype={img_a.dtype}\nimg_a.shape={img_a.shape}\n')
'''
img_a[0, 0, 0]=255
type(img_a)=<class 'numpy.ndarray'>
img_a.dtype=uint8
img_a.shape=(200, 200, 3)
'''
img_b = np.zeros((height, width, 3), dtype='uint8')
img_b[:, :, 0] = 0.8
print(f'img_b[0, 0, 0]={img_b[0, 0, 0]}\n'
f'type(img_b)={type(img_b)}\nimg_b.dtype={img_b.dtype}\nimg_b.shape={img_b.shape}\n')
'''
img_b[0, 0, 0]=0
type(img_b)=<class 'numpy.ndarray'>
img_b.dtype=uint8
img_b.shape=(200, 200, 3)
'''
img_c = np.zeros((height, width, 3), dtype='uint8')
img_c[:, :, 0] = 300
print(f'img_c[0, 0, 0]={img_c[0, 0, 0]}\n'
f'type(img_c)={type(img_c)}\nimg_c.dtype={img_c.dtype}\nimg_c.shape={img_c.shape}\n')
'''
img_c[0, 0, 0]=44
type(img_c)=<class 'numpy.ndarray'>
img_c.dtype=uint8
img_c.shape=(200, 200, 3)
'''
img_d = np.zeros((height, width, 3))
img_d[:, :, 0] = 256
print(f'img_d[0, 0, 0]={img_d[0, 0, 0]}\n'
f'type(img_d)={type(img_d)}\nimg_d.dtype={img_d.dtype}\nimg_d.shape={img_d.shape}\n')
# 对其进行类型转换
img_d = img_d.astype('uint8')
print(f'img_d[0, 0, 0]={img_d[0, 0, 0]}\n'
f'type(img_d)={type(img_d)}\nimg_d.dtype={img_d.dtype}\nimg_d.shape={img_d.shape}\n')
'''
img_d[0, 0, 0]=256.0
type(img_d)=<class 'numpy.ndarray'>
img_d.dtype=float64
img_d.shape=(200, 200, 3)
img_d[0, 0, 0]=0
type(img_d)=<class 'numpy.ndarray'>
img_d.dtype=uint8
img_d.shape=(200, 200, 3)
'''
cv2.imshow('a', img_a)
cv2.imshow('b', img_b)
cv2.imshow('c', img_c)
cv2.imshow('d', img_d)
cv2.waitKey(0)
cv2.destroyAllWindows()
显示图片为
uint8为无符号8位整型,uint8范围为0 ~
,即0~255。
将所有像素值类型都转换为uint8类型,imshow()再按原样显示。转换规则为
- 若为浮点型,直接去除小数部分
- 再将整数n按照取模运算n%256映射到0~255
import numpy as np
arr = np.array([-1.8, -1.3, -256.5, 100.3, 100.7])
print(f'arr.dtype={arr.dtype},\tarr={arr}')
# arr.dtype=float64, arr=[ -1.8 -1.3 -256.5 100.3 100.7]
arr = arr.astype('uint8')
print(f'arr.dtype={arr.dtype},\tarr={arr}')
# arr.dtype=uint8, arr=[255 255 0 100 100]
uint16
def test_uint16():
img_a = np.zeros((height, width, 3), dtype='uint16')
img_a[:, :, 2] = 255
print(f'img_a[0, 0, 2]={img_a[0, 0, 2]}\n'
f'type(img_a)={type(img_a)}\nimg_a.dtype={img_a.dtype}\n')
cv2.imshow('a', img_a)
'''
img_a[0, 0, 2]=255
type(img_a)=<class 'numpy.ndarray'>
img_a.dtype=uint16
'''
img_a[:, :, 2] = 255 * 256
print(f'img_a[0, 0, 2]={img_a[0, 0, 2]}\n'
f'type(img_a)={type(img_a)}\nimg_a.dtype={img_a.dtype}\n')
cv2.imshow('b', img_a)
'''
img_a[0, 0, 2]=65280
type(img_a)=<class 'numpy.ndarray'>
img_a.dtype=uint16
'''
img_a[:, :, 2] = 255 * 256 + 257
print(f'img_a[0, 0, 2]={img_a[0, 0, 2]}\n'
f'type(img_a)={type(img_a)}\nimg_a.dtype={img_a.dtype}\n')
cv2.imshow('c', img_a)
'''
img_a[0, 0, 2]=1
type(img_a)=<class 'numpy.ndarray'>
img_a.dtype=uint16
'''
img_a[:, :, 2] = 255 * 256 + 200
print(f'img_a[0, 0, 2]={img_a[0, 0, 2]}\n'
f'type(img_a)={type(img_a)}\nimg_a.dtype={img_a.dtype}\n')
cv2.imshow('d', img_a)
'''
img_a[0, 0, 2]=65480
type(img_a)=<class 'numpy.ndarray'>
img_a.dtype=uint16
'''
cv2.waitKey(0)
cv2.destroyAllWindows()
显示如图
uint16为无符号16位整型,uint16范围为0 ~
,即0~65535。
- 将所有像素值类型都转换为uint16类型。
转换规则同uint8类型转换类似,首先除去小数部分;再模65536。 - imshow()将uint16类型像素除以256再显示。
由实验猜测对于uint16类型像素值在范围0~255*256的除以256显示,对于超出范围的像素,去除超出部分按最小或最大值计算。
int32
def test_int32():
img_a = np.zeros((height, width, 3), dtype='int32')
img_a[:, :, 1] = 255
print(f'img_a[0, 0, 1]={img_a[0, 0, 1]}\n'
f'type(img_a)={type(img_a)}\nimg_a.dtype={img_a.dtype}\n')
'''
img_a[0, 0, 1]=255
type(img_a)=<class 'numpy.ndarray'>
img_a.dtype=int32
'''
cv2.imshow('a', img_a)
img_a[:, :, 1] = 255 * 256
print(f'img_a[0, 0, 1]={img_a[0, 0, 1]}\n'
f'type(img_a)={type(img_a)}\nimg_a.dtype={img_a.dtype}\n')
'''
img_a[0, 0, 1]=65280
type(img_a)=<class 'numpy.ndarray'>
img_a.dtype=int32
'''
cv2.imshow('b', img_a)
img_a[:, :, 1] = 3 * 255 * 256
print(f'img_a[0, 0, 1]={img_a[0, 0, 1]}\n'
f'type(img_a)={type(img_a)}\nimg_a.dtype={img_a.dtype}\n')
'''
img_a[0, 0, 1]=195840
type(img_a)=<class 'numpy.ndarray'>
img_a.dtype=int32
'''
cv2.imshow('c', img_a)
img_a[:, :, 1] = -255 * 256
print(f'img_a[0, 0, 1]={img_a[0, 0, 1]}\n'
f'type(img_a)={type(img_a)}\nimg_a.dtype={img_a.dtype}\n')
'''
img_a[0, 0, 1]=-65280
type(img_a)=<class 'numpy.ndarray'>
img_a.dtype=int32
'''
cv2.imshow('d', img_a)
cv2.waitKey(0)
cv2.destroyAllWindows()
显示图片如下
int32为有符号32位整型,范围为
。
同uint16一样,首先将像素值类型转换为int32类型;其次imshow()将像素值除以256显示
由实验猜测对于int32类型像素值在范围0~255x256的除以256显示,对于超出范围的去除超出部分按最小或最大值计算。
按0计算,
按255*256计算。
float32和float64
这里以float32为例,float64类似
def test_float32():
img_a = np.zeros((height, width, 3), dtype='float32')
img_a[:, :, 0] = 255
print(f'img_a[0, 0, 0]={img_a[0, 0, 0]}\n'
f'type(img_a)={type(img_a)}\nimg_a.dtype={img_a.dtype}\n')
'''
img_a[0, 0, 0]=255.0
type(img_a)=<class 'numpy.ndarray'>
img_a.dtype=float32
'''
cv2.imshow('a', img_a)
img_a[:, :, 0] = 1
print(f'img_a[0, 0, 0]={img_a[0, 0, 0]}\n'
f'type(img_a)={type(img_a)}\nimg_a.dtype={img_a.dtype}\n')
'''
img_a[0, 0, 0]=1.0
type(img_a)=<class 'numpy.ndarray'>
img_a.dtype=float32
'''
cv2.imshow('b', img_a)
img_a[:, :, 0] = 0.5
print(f'img_a[0, 0, 0]={img_a[0, 0, 0]}\n'
f'type(img_a)={type(img_a)}\nimg_a.dtype={img_a.dtype}\n')
'''
img_a[0, 0, 0]=0.5
type(img_a)=<class 'numpy.ndarray'>
img_a.dtype=float32
'''
cv2.imshow('c', img_a)
img_a[:, :, 0] = -0.8
print(f'img_a[0, 0, 0]={img_a[0, 0, 0]}\n'
f'type(img_a)={type(img_a)}\nimg_a.dtype={img_a.dtype}\n')
'''
img_a[0, 0, 0]=-0.800000011920929
type(img_a)=<class 'numpy.ndarray'>
img_a.dtype=float32
'''
cv2.imshow('d', img_a)
cv2.waitKey(0)
cv2.destroyAllWindows()
显示如图
float32是浮点型,imshow()是将0 ~ 1范围像素值乘以255,映射为0 ~ 255显示。
由实验猜测对于0~1以外的像素值,作截断处理,分别取对应的边界,然后再乘以255显示。
实际使用注意
根据实际使用中的经验,需要注意的地方有:
针对图像,如果是进行归一化后的float32或者float64类型numpy.ndarray,则像素值范围在0~1内,用cv2.imshow()可以正常显示。
但如果进行类型转换为uint8,再用cv2.imshow()显示,则为全黑,无法正常显示;如果转换为uint16或int32,则显示也是全黑。
def float32_else():
img = np.zeros((200, 300, 3), dtype='float32')
img[:, :, 2] = 1.0
print(f'img.dtype={img.dtype}, img[0,0,2]={img[0,0,2]}')
cv2.imshow('float32', img)
img = img.astype('uint8')
print(f'img.dtype={img.dtype}, img[0,0,2]={img[0,0,2]}')
cv2.imshow('uint8', img)
img = img.astype('uint16')
print(f'img.dtype={img.dtype}, img[0,0,2]={img[0,0,2]}')
cv2.imshow('uint16', img)
img = img.astype('int32')
print(f'img.dtype={img.dtype}, img[0,0,2]={img[0,0,2]}')
cv2.imshow('int32', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
'''
img.dtype=float32, img[0,0,2]=1.0
img.dtype=uint8, img[0,0,2]=1
img.dtype=uint16, img[0,0,2]=1
img.dtype=int32, img[0,0,2]=1
'''
显示图片为
同样,其他的数据类型转换,用cv2.imshow()也可能会出现问题,这里就不再一一演示了,感兴趣的同学可以自己尝试下。
但只要熟知针对不同数据类型的numpy.ndarray,cv2.imshow()的不同处理方式,就可以弄懂其中原因,并进行修改。
总结
- 首先得将图像像素值类型,强制转换为对应的数据类型。
- imshow()再根据不同类型进行不同操作后,显示出来。
如果图像是8位无符号的,则按原样显示。
如果图像是16位无符号或32位整数,则像素除以256。也就是说值范围[0, 255x256]映射到[0, 255]。
如果图像是32位或64位浮点,则像素值乘以255。也就是说值范围[0, 1]映射到[0, 255]。
根据实验猜测:对于超出映射范围的像素值,截断按其边界处理,再按规则映射,这里欢迎大家讨论交流,互相学习。