OpenCV里imshow()处理不同数据类型的numpy.ndarray分析

  • 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 ~ 2 8 1 2^8-1 ,即0~255。
将所有像素值类型都转换为uint8类型,imshow()再按原样显示。转换规则为

  1. 若为浮点型,直接去除小数部分
  2. 再将整数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 ~ 2 16 1 2^{16}-1 ,即0~65535。

  1. 将所有像素值类型都转换为uint16类型。
    转换规则同uint8类型转换类似,首先除去小数部分;再模65536。
  2. 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位整型,范围为 2 31 2 31 1 -2^{31} \sim 2^{31}-1
同uint16一样,首先将像素值类型转换为int32类型;其次imshow()将像素值除以256显示
由实验猜测对于int32类型像素值在范围0~255x256的除以256显示,对于超出范围的去除超出部分按最小或最大值计算。 2 31 0 -2^{31} \sim 0 按0计算, 255 × 256 + 1 2 31 1 255 \times 256+1\sim2^{31}-1 按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()的不同处理方式,就可以弄懂其中原因,并进行修改。

总结

  1. 首先得将图像像素值类型,强制转换为对应的数据类型。
  2. imshow()再根据不同类型进行不同操作后,显示出来。
    如果图像是8位无符号的,则按原样显示。
    如果图像是16位无符号或32位整数,则像素除以256。也就是说值范围[0, 255x256]映射到[0, 255]。
    如果图像是32位或64位浮点,则像素值乘以255。也就是说值范围[0, 1]映射到[0, 255]。
    根据实验猜测:对于超出映射范围的像素值,截断按其边界处理,再按规则映射,这里欢迎大家讨论交流,互相学习。
发布了4 篇原创文章 · 获赞 0 · 访问量 251

猜你喜欢

转载自blog.csdn.net/weixin_42421992/article/details/104120340