opencv中为啥保存的图片和显示的图片不同?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv中为啥保存的图片和显示的图片不同?相关的知识,希望对你有一定的参考价值。
例如以下代码:Mat o1=imread("../py/lib2_o1.jpg");Mat o1_g2=imread("../py/lib2_o1_g2.jpg");Mat pyDoG;subtract(o1,o1_g2,pyDoG, noArray(),CV_8SC1 ); imshow("Dog1", pyDoG);imwrite("../py/py_o1_0s2.jpg",pyDoG);但是imshow()显示的图片(左图)和保存的图片py_o1_0s2.jpg(右图)不一样?
应该是图像的深度不同一造成,先看一下你的代码中各个环节是否统一了图像的深度CV_8SC1,也有可能是系统的看图软件对8bit深度的图像显示的有问题。 参考技术A openCV中,在使用imwrite()保存图片时,会自动将各种格式的数据转换成符合规矩的uint8数据类型的数据,再保存。而使用imshow()显示图像的时候,并没有这一个操作。从你给的图里可以很容易的看出,左边的图不是一个灰度图像。因为数据类型不是uint8,所以openCV将其当做一个二值图像来处理。而右边的图像数据是一个显示正确的灰度图像。openCV第一篇
文章目录
前言:计算机眼中的图片
- 计算机中图片由许多个像素点组成,如一个500x500的图片,表示长宽各由500个像素点组成。
- 计算机中一个像素点的值在0-255表示该点亮度 0暗(黑)-255亮(白)
- 一张彩图通常是由RGB(red、green、blue)三个颜色通道所组成
- 一个500x500的图片那他们的 RGB 矩阵也各是500x500。
上图我们将一个大的图片分割成许多如20x20的小图片,同理每个小图片它们的RGB矩阵也各自是20x20。
注:opencv默认顺序不是RBG 而是 BGR。
1. 图片的读取与显示
1.1 图片的读取
import cv2
img = cv2.imread('./data/abv.jpg')
- img = cv2.imread('./data/abv.jpg')
比如我读入这样一张图片,命名为img;
我们可以看到img是一个三维ndarray结构,内部数据类型dtype=unit8:
- 三维:(1080,1920,3)表示高度、宽度、颜色通道个数(cv彩图BGR)
- ndarray结构:数据类型dtype=unit8, 0-255。
1.2 显示的图片
1.2.1 显示原始图片
cv2.imshow('image',img) # 第一个参数表示窗口指定的名字 第二个为上方img
cv2.waitKey(5000) # 等待时间 如果是5000则在5s后图片窗口自动关闭 0表示任意键关闭
cv2.destroyAllWindows() # 时间一到关闭窗口
ps: 图像的显示也可以是多个窗口
- cv2.imshow('image',img) 自己给将要弹出的窗口起个名 再加入变量img
- cv2.waitKey(5000) 等待时间 如果是5000则在5s后图片窗口自动关闭 0表示任意键关闭
- cv2.destroyAllWindows() 时间一到关闭窗口
为方便下面使用,我们自己定义一个函数cv_show():
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
执行代码,弹出窗口,显示图片:
cv_show('winName',img)
1.2.2 灰度图
在之前的基础之上加入参数:cv2.IMREAD_GRAYSCALE
img2=cv2.imread('./data/abv.jpg',cv2.IMREAD_GRAYSCALE) # cv2.IMREAD_COLOR
- img2=cv2.imread('./data/abv.jpg',cv2.IMREAD_GRAYSCALE)
- cv2.IMREAD_GRAYSCALE 读取为灰度图,也可以写0。
- cv2.IMREAD_COLOR 读取为彩图
我们执行:
cv_show('win2',img2)
可以看到该图片最终显示结果为二维(1080,1920) :
ps: img.size 输出像素点的个数,可以看到同一张图片BGR彩图是灰度图的三倍。
1.3 BGR转换成灰度图、RGB
当然我们也可以把已经读取进来的BGR彩图转换成灰度图,或者转换为RGB。
img2 = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv_show('win3',img2)
- img2 = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
- cv2.COLOR_BGR2RGB 将BGR格式转换成RGB
- cv2.COLOR_BGR2GRAY 将BGR格式转换成灰度图片
2. 保存图片
cv2.imwrite('./data/grayPhoto.jpg',img2)
- cv2.imwrite('./data/grayPhoto.jpg',img2)
此时我的data文件夹下就多了一张刚刚处理好名为grayPhoto的灰度图。
3. 视频的读取与显示
视频也是由图像组成的,每一帧都可以当作是一个静止的图像,把图像连在一起看着就像是一个视频了。 我们打游戏时,也是追求一些更高的帧率。
vc = cv2.VideoCapture('./data/stu.mp4')
- vc = cv2.VideoCapture('./data/stu.mp4')
# 检查是否正确打开
if vc.isOpened():
open,frame = vc.read()
else:
open = Flase
- vc.read()
- 读取视频中的第一帧 ,再次执行vc.read()的话读取视频第二帧
- 返回值中:第一个:布尔类型,能读进来就是True 第二个像是上面的img,这一帧图片的ndarray矩阵
循环图片播放视频:
while open:
ret,frame = vc.read()
if frame is None:
break
if ret == True:
# 原本frame是(h,w,3)的BGR图片矩阵 经下方加入参数转换成黑白gray
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
cv2.imshow('result',gray)
# cv2.waitKey(num) 该图片显示时间/速度 自己可以找一个合适的值
# 27指退出键ESC 退出窗口 当然也可以是 =='q'等
if cv2.waitKey(20)&0xFF == 27:
break
vc.release()
cv2.destroyAllWindows()
注:视频放完 ret, frame = vc.read()返回False和None 再次进行循环无法播放视频,需要重新读取。
- if cv2.waitKey(20)&0xFF == 27:
- 不是新知识点了,表示每张图片等20毫秒,如果按ESC键直接退出。
4. 截取图像部分
我们上面介绍,img是一个ndarray矩阵,因此对其进行切片操作:
pho = img[100:800,200:800] # 进行切片 高100到800 宽200到800
cv_show('win2',pho)
- pho = img[100:800,200:800]
5. 颜色通道提取
b,g,r = cv2.split(img)
# b.shape g.shape r.shape 都为 (1080, 1920)
执行
cv_show('win3',g) # 或者填 b、r
结果就是单通道图。
如果我们想显示单一颜色,如红色:
cur_img = img.copy()
cur_img[:,:,0] = 0 # B不要了 设置为0
cur_img[:,:,1] = 0 # G不要了 设置为0
#cur_img[:,:,2] = 0 # R不要了 设置为0
cv_show('winR',cur_img)
6. 边界填充
这个一般用于卷积,在图像周围填充一些像素。
我们以这个图片为例:
img = cv2.imread('./data/gd01.jpg')
# img.shape 为 (300, 400, 3)
cv_show('win1',img)
在图片的上下左右填充50个像素,介绍5种方法:
top_size,bottom_size,left_size,right_size=(50,50,50,50)
# 不同的填充方法 最后参数改个type值就行
replicate = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_REFLECT)
reflect101=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_WRAP)
constant = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_CONSTANT,value=0)
plt.subplot(231),plt.imshow(img,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')
# 注matplotlib默认R G B本例只介绍边界填充
plt.show()
- cv2.BORDER_REPLICATE
- 复制最边缘的像素
- cv2.BORDER_REFLECT
- 反射法对感兴趣的图像中的像素在两边进行复制 如fedcba|abcdefgh|hgfedcb 其中abcdefgh是图像"|"外的是填充内容
- cv2.BORDER_REFLECT_101
- 反射法,也就是以最边缘的像素为轴,对称,gfedcb|abcdefgh|gfedcba 上面是ba|ab 这个是bab。
- cv2.BORDER_WRAP
- 外包装法 如:cdefgh|abcdefgh|abcdefg。
- cv2.BORDER_CONSTANT
- 常量法,常数值value填充。
7. 数值计算
由于是uint8类型最大255 超过相当于结果为 num%256了
我们使用以下方法保留最大值:
cv2.add(img,img2)
- cv2.add(img,img2)
8. 图像融合
效果如下:
我们导入宽高相同的2张图片:
img = cv2.imread('./data/gd04.jpg')
img2 = cv2.imread('./data/gd05.jpg')
img3 = cv2.imread('./data/gd06.jpg')
print(img.shape,img2.shape,img3.shape) # (281, 600, 3) (281, 600, 3) (337, 600, 3)
如果大小不同 需要手动设置成一样的
img3 = cv2.resize(img3,(600,281))
# img3.shape (281, 600, 3)
- img3 = cv2.resize(img3,(600,281))
ps: cv2.resize()另一种操作
img4 = cv2.resize(img3,(0,0),fx=2,fy=1)
我们执行:
res = cv2.addWeighted(img,0.6,img2,0.4,0)
plt.imshow(res)
- res = cv2.addWeighted(img,0.6,img2,0.4,0)
R = ax1 + bx2 + c a、b为权重 c为偏置 这里意为在原亮度上变化多少
9. 知识点总结*
- 读取图片:
import cv2
img = cv2.imread('./data/abv.jpg')
img2 = cv2.imread('./data/abv.jpg',cv2.IMREAD_GRAYSCALE)
img3 = cv2.imread('./data/abv.jpg',cv2.IMREAD_COLOR)
- 显示图片:
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
- BGR转换成灰度图、RGB:
img2 = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img3 = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
- 保存图片:
cv2.imwrite('./data/grayPhoto.jpg',img2)
- 视频的读取与显示:
vc = cv2.VideoCapture('./data/stu.mp4')
# 检查是否正确打开
if vc.isOpened():
open,frame = vc.read()
else:
open = Flase
while open:
ret,frame = vc.read()
if frame is None:
break
if ret == True:
# 原本frame是(h,w,3)的BGR图片矩阵 经下方加入参数转换成黑白gray
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
cv2.imshow('result',gray)
# cv2.waitKey(num) 该图片显示时间/速度 自己可以找一个合适的值
# 27指退出键ESC 退出窗口 当然也可以是 =='q'等
if cv2.waitKey(20)&0xFF == 27:
break
vc.release()
cv2.destroyAllWindows()
- 截取图片部分:
pho = img[100:800,200:800] # 进行切片 高100到800 宽200到800
- 颜色通道提取:
b,g,r = cv2.split(img)
cur_img = img.copy()
cur_img[:,:,0] = 0 # B不要了 设置为0
cur_img[:,:,1] = 0 # G不要了 设置为0
#cur_img[:,:,2] = 0 # R不要了 设置为0
cv_show('winR',cur_img)
- 边界填充:
top_size,bottom_size,left_size,right_size=(50,50,50,50)
# 不同的填充方法 最后参数改个type值就行
replicate = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_REFLECT)
reflect101=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_WRAP)
constant = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_CONSTANT,value=0)
- 数值计算:
cv2.add(img,img2)
- 图像融合:
img3 = cv2.resize(img3,(600,281))
res = cv2.addWeighted(img,0.6,img2,0.4,0)
以上是关于opencv中为啥保存的图片和显示的图片不同?的主要内容,如果未能解决你的问题,请参考以下文章
OPENcv中把一个图片融合到另一个图像中为啥程序正确不显示图片