以前没接触深度学习的时候没注意过图片位深度的问题,最近研究深度学习图片输入弄的也是莫名奇妙,焦头烂额。记录一下自己搜的资料的总结。首先要明白计算机的储存方式位二进制,只有0和1,因此图片的像素矩阵也不例外
这里可以看到图片的位深度为16,因此可以表示的颜色为2**16=655536
但是为什么是2的16呢,这里就是因为计算机的储存方式了。因为是从0开始所以,65535转化为二进制,1111 1111 1111 1111 每一位有两种形式0和1,因此是2**16。同理其他的位深度也是这样计算。
现在转入正题,图片转化位数组形式,目前我掌握的有两 种方式:
import cv2
import numpy as np
import skimage.io
list = np.zeros((500,500))
list[:30,50:500] = 125
cv2.imwrite('1.png',list)
# cv2.S
# print(list)
img = cv2.imread('1.png')
img2 = skimage.io.imread('1.png')
print(img.shape)
print(img2.shape)
生成一个二维数组,然后保存成图片,再用两种方式去读取看看会发生什么。
(500, 500, 3)
(500, 500)
可以看出opencv默认的读取方式就是按照三维数组后面是通道数,而skimage是按照原始的形式去读取。如果设置三维的图片呢。修改代码:
list = np.zeros((500,500,3))
输出结果:
(500, 500, 3)
(500, 500, 3)
全部都是三维了。
但是我转念一想如果是4通道呢
list = np.zeros((500,500,4))
输出结果:
(500, 500, 3)
(500, 500, 4)
opencv竟然依旧是三维的,怎么回事呢,原来opencv读取默认是rgb的方式,如果要加上通道则后面加上参数-1,0则为灰度图,没有通道数,1就是默认rgb 3通道。好了读取的方式现在明白了,那么不同位深度如何转换呢。先上一段代码,这个代码来自别人,图像处理时,我偶然发现位深度竟然改变了。
def change(img):
t = img.astype(np.float32)
# 减小异常值的影响
maxv = np.percentile(t, 99)
minv = np.percentile(t, 1)
t = (t-minv)/(maxv-minv)
meanv = t.mean()
# 均值统一拉到0.5
t += (0.5-meanv)
# 扩大范围至0-65535
t *= 65535
t[t<0] = 0
t[t>65535] = 65535
img = t.astype(np.uint16)
cv2.imwrite('5.png',img)
PS:在处理四通道的图片的时候我竟然又有了新的发现(我的发现为什么这么多,无知啊,知识了解的太少,各位同学记得好好学习)修改代码,
list = np.zeros((500,500,4))
list[50:100,50:100] = 255
cv2.imwrite('1.png',list)
这个代码本来应该生成一个中间有一个白色板块,其他都是黑色的图片,但是结果呢
3通道生成图片 4通道生成图片
只有一个白色的框,很明显四通道中数值为0的是透明的,因为rgba的a通道是透明通道,原来如此,竟然是透明的。
继续用change函数看下结果如何。这里需要说明一下,2维数组位深度位为8,三通道则是24,四通道则是32,为什么一开始是8这里其实我不是很明白,但是根据上面说明的,8通道总共的颜色是2**8=256 而我们熟知的rgb正好是0-255,所以应该是和计算机显示有关系,但是并不影响了,同时还涉及到图片格式,坑以后填上。在最后一个通道上的测试中我又发现了(好吧,发现了就记得,不然明天就忘记了)
我尝试读取一张彩色的图片的时候输出的像素矩阵究竟最后一位是255,那么如果把最后一位变成0对前面的有什么影响吗,(数组全是0的,图片颜色会随着最后一位通道数0-255而逐渐变深,就是最后通道数值为255的时候图片是全黑的上度娘)通过度娘的一番诉说,发现自己真的无知第四个是透明通道,数值变成255意思就是百分百不透明啊,不透明不是黑色难道不成是粉色?不过利用这一点可以用代码实现图片透明度的改变。
ist[:,:,3]=100#数组切片,索引最后一个可以理解为行数吧,但其实是维度,但是正好对应,这样也好理解,
#或者就理解成对应的通道数,0是r,1是g,2是b,3是a,把第四个通道行列全部裁剪,值变成100
a通道为0原图
a通道为100图
但是如何把一张黑图变成粉色的呢,首先我搜到了粉色的rgb值(241,204,226)
改写代码:
img = skimage.io.imread('1_255.png')
img[:,:,0]=249
img[:,:,1]=204
img[:,:,2]=226
print(img)
cv2.imwrite('fen.png',img)
出来的结果:
和想想中不太一样啊,黑的就算是变成粉色很不自然
那么到底深度是如何改变的呢:我突然想起了开头的16位深度,和代码中的数值 t *= 65535 数组中的每个数值都 * 65535,这样如果有数据,数据不都变成比65535大的数值了吗,用二进制位数表达时,位数不就改变了吗?但是要是数值是0,或者数值不为0,乘积肯定要大于 65535,这就不只是64位,但是 t[t<0] = 0 t[t>65535] = 65535这两个条件限制了范围,统统16位表示,当然了这里不是排列组合所以是,4*16=64,神奇的二进制!
由此提出两个猜想:一、位深度是数据中最大的一个数的数值的二进制长度*通道数。二、以上猜想正确。
为了验证我的猜想,我做了如下实验:全部是0,或全部和1
可见并不是如猜想那样,0也很神奇,二进制0的长度可以变化?还是如果没有最大值的时候,会根据维度默认补全生成?
或者是图片一个通道按照标准必须是2**8 = 256 255转化位二级制 1111 1111 如果这样,我的猜想在标准成立的条件下是成立的,现在认为猜想在每个通道初始是8的基础上成立,那么用此猜想改变下位深度试试。如果三个维度分别是16,48,64,那么此猜想可作为经验来解决改变位深度的问题。上结果
完全ok!
ps:opencv和skimage读取图片的方式其实是不一样的,opencv是按照bgr的方式读取,并且就转成rgb,用cv2保存时依旧是bga
解决方法就是用skimage去保存转换后的图片。