OpenCV(2):ROI提取,单通道处理与边界填充

昨天学习了图像的基本操作和视频的基本操作,说白了视频就是不断读取每一帧图像然后展示出来。

那么今天学习的内容就是昨天的进一步深入,下面我们开始今天的内容

ROI提取

ROI就是你的兴趣点,一张图片中你想要的部分,那么由于img是ndarray类型的一个三维数组,那么可以通过切片的方式来选择你想要的像素点。 注意img是(y,x,bound),即第一纬度代表y轴上栅格的索引,第二纬度代表x轴上栅格的索引,第三维度代表波段。

import cv2

img = cv2.imread('hawk.jpg')
#矩阵切片,把需要的东西提取出来
hawk = img[400:1100,550:1300]

#把三个方法写进一个方法cv_show里面,这里面就包括展示图片,按任意键继续,关闭窗口,这样就不用再每次都输入这三个方法了。
def cv_show(title,img):
    cv2.imshow(title,img)
    cv2.waitKey(0) 
    cv2.destroyAllWindows()
    
cv_show('hawk',hawk)

#保存一下提取完的图片
cv2.imwrite('the_hawk.jpg',hawk)

那么在实际应用中,ROI提取中矩阵的切片是要算出来的,切哪一部分,这个我们之后再说,现在只是说明ROI提取的本质就是矩阵的切片。

结果是

颜色通道单独提取处理与组合

那么我们首先看一下第一个通道是不是蓝色,验证前文说的OpenCV是BGR。

import cv2
import numpy as np

#将上文提到的ROI提取放到一个方法里面
def cv_show(title,img):
    ROI = img[400:1100,550:1300]
    cv2.imshow(title,ROI)
    cv2.waitKey(0) 
    cv2.destroyAllWindows()

#单独看一下第一个通道是不是b(蓝),将其余两个通道赋值0,
#此时注意使用numpy中的copy方法克隆一个新的矩阵而不是直接在原有矩阵上赋值,不然会改变原来的对象。
img = cv2.imread('hawk.jpg')
img_b = img.copy()
img_b[:,:,1] = 0#第二个波段赋值0
img_b[:,:,2] = 0#第三个波段赋值0
cv_show('change',img_b)

返回

其他两个波段以此类推,就是将另外两个波段都赋值为0然后imshow,

前文说过OpenCV读取的是一个BGR三通道(波段,一个意思)的图片,那么如果我每个波段要单独进行相关的计算该怎么做呢?

因为有些时候我们处理的并不是图片,有可能是其他的东西比如遥感影像之类的,他可能对于不同的波段要进行单独的操作,所以会讲这一块。 通过split方法将各个波段分开,对每个波段进行单独处理

然后再用cv2.merge方法将三个波段合成

import cv2
import numpy as np

#将上文提到的ROI提取放到一个方法里面
def cv_show(title,img):
    ROI = img[400:1100,550:1300]
    cv2.imshow(title,ROI)
    cv2.waitKey(0) 
    cv2.destroyAllWindows()

img = cv2.imread('hawk.jpg')
b,g,r = cv2.split(img)#此时三个纬度就会分别赋值给b,g,r,每个纬度都是一个n行n列的矩阵

#一系列处理和计算,在此没想好用什么例子,就随便写点,
#这边写的有点傻,其实可以先进行数据的切片然后对数据进行相关操作这样快一点,但是这边数据量很小就不用管
rd = 150*np.random.random(r.shape) #生成一个和r相同shape的一个随机矩阵,每个矩阵赋值一个0-150之间的数
r = r+ np.array(rd,dtype = 'uint8')#注意numpy生成的随机矩阵是浮点型的,而opencv识别的矩阵数据类型是uint8类型的,此时需要格式转换一下

img = cv2.merge((b,g,r))#用cv2.merge方法重新将三个波段组合起来
cv_show('change',img)

那么需要注意的是,作为代表颜色深浅的矩阵,opencv读取的img三维矩阵中每一个元素的范围只能是0到255。这也是由矩阵的元素类型:uint8决定的,这个数据类型的意思是u:无符号,int:整型,8:占8个字节,那么作为非负数,8个字节最多存2的8次方256个数,也就是0-255。

此文的例子中,r波段每个元素都加上了一个0-150之间的数,那么很有可能出现大于255的情况。那么对于uint8这个数据类型而言,如果大于255,那就会进行一个取余的行为。也就是假设我这个矩阵的数值本应该是1000,那么存到unit8中就变成了1000%256 =232。

看一下图像处理的结果:

此外,opencv中还提供了一个add函数。

在原本的波段运算中加入下行函数

r = cv2.add(r,r)

这个函数也是将两个矩阵相加,并且返回的矩阵数据类型依旧是uint8,那么与默认的加法运算不同,如果add方法中,加完后的结果大于255,那么直接取255作为结果

完整代码

import cv2
import numpy as np

#将上文提到的ROI提取放到一个方法里面
def cv_show(title,img):
    ROI = img[400:1100,550:1300]
    cv2.imshow(title,ROI)
    cv2.waitKey(0) 
    cv2.destroyAllWindows()

img = cv2.imread('hawk.jpg')
b,g,r = cv2.split(img)#此时三个纬度就会分别赋值给b,g,r,每个纬度都是一个n行n列的矩阵

#一系列处理和计算,在此没想好用什么例子,就随便写点,
#这边写的有点傻,其实可以先进行数据的切片然后对数据进行相关操作这样快一点,但是这边数据量很小就不用管
rd = 150*np.random.random(r.shape) #生成一个和r相同shape的一个随机矩阵,每个矩阵赋值一个0-150之间的数
r = r+ np.array(rd,dtype = 'uint8')#注意numpy生成的随机矩阵是浮点型的,而opencv识别的矩阵数据类型是uint8类型的,此时需要格式转换一下
r = cv2.add(r,r)

img = cv2.merge((b,g,r))#用cv2.merge方法重新将三个波段组合起来
cv_show('change',img)

结果

边界填充

就是给图像周围加一圈数据,在很多算法中都需要用到这个,所以这边讲一下

用cv2.copyMakeBorder(img,上,下,做,右,borderType)进行填充

直接上代码

import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

img = cv2.imread('the_hawk.jpg')#读取一下前文咱们用ROI提取图像
#首先指定上下左右填什么东西
top,bottom,left,right = (100,100,100,100)#定义四个变量,表示上下左右都填充多少个数

使用cv2.copyMakeBorder()方法填充边界,以下代码分别为五种不同的边界填充方式,就是borderType不同

#复制法填充图像,填充的数据大小为复制图像最边缘的像素值
p_replicate = cv2.copyMakeBorder(img,top,bottom,left,right,borderType = cv2.BORDER_REPLICATE)
#反射法填充图像,填充数据的大小为邻近数据的反射
p_reflect   = cv2.copyMakeBorder(img,top,bottom,left,right,borderType = cv2.BORDER_REFLECT) 
#反射法进阶版,最近像素为轴做对称
p_reflect_101 = cv2.copyMakeBorder(img,top,bottom,left,right,borderType = cv2.BORDER_REFLECT_101)
#外包装法
p_wrap =  cv2.copyMakeBorder(img,top,bottom,left,right,borderType = cv2.BORDER_WRAP) 
#常量法
p_constant = cv2.copyMakeBorder(img,top,bottom,left,right,borderType = cv2.BORDER_CONSTANT)

详细解释一下不同填充方法的作用机理

复制法:cv2.BORDER_REPLICATE

把最邻近像元的值赋给增加的像元,下图中黄色部分代表原图的像元矩阵

反射法:cv2.BORDER_REFLECT

把邻近像元反转赋值给增加的像元,反转多少个元素取决于top,bottom,left,right的值

反射法进阶版:cv2.BORDER_REFLECT_101

以最邻近像元为轴反射像素值赋值给增加像元

外包装法:cv2.BORDER_WRAP

常量法:cv2.BORDER_CONSTANT

边界各像元都赋值0

结果

利用maplotlib输出图片看看不同方法结果怎么样

import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

img = cv2.imread('the_hawk.jpg')#读取一下前文咱们用ROI提取图像
#首先指定上下左右填什么东西
top,bottom,left,right = (100,100,100,100)#定义四个变量,表示上下左右都填充多少个数

#输入的参数除了上下左右的参数之外,还要输入borderType参数,下面看一下不同的参数结果是怎么样的
p_replicate = cv2.copyMakeBorder(img,top,bottom,left,right,borderType = cv2.BORDER_REPLICATE)#复制法填充图像,填充的数据大小为复制图像最边缘的像素值
p_reflect   = cv2.copyMakeBorder(img,top,bottom,left,right,borderType = cv2.BORDER_REFLECT) #反射法填充图像,填充数据的大小为邻近数据的反射
p_reflect_101 = cv2.copyMakeBorder(img,top,bottom,left,right,borderType = cv2.BORDER_REFLECT_101) #反射法进阶版,最近像素为轴做对称
p_wrap =  cv2.copyMakeBorder(img,top,bottom,left,right,borderType = cv2.BORDER_WRAP) #外包装法
p_constant = cv2.copyMakeBorder(img,top,bottom,left,right,borderType = cv2.BORDER_CONSTANT)#常量法

plt.subplot(231),plt.imshow(p_replicate),plt.title('p_replicate')
plt.subplot(232),plt.imshow(p_reflect),plt.title('p_reflect')
plt.subplot(233),plt.imshow(p_reflect_101),plt.title('p_reflect_101')
plt.subplot(234),plt.imshow(p_wrap),plt.title('p_wrap')
plt.subplot(235),plt.imshow(p_constant),plt.title('p_constant')

返回

注意,matplotlib中的imshow方法默认是读取RPG格式的文件,和opencv正好相反,所以才会产生颜色不对这种情况。需要解决这种情况,可以使用上文波段组合中提到的cv2.split()方法和cv2.merge()方法将BGR拆开来赋值给RGB。

b, g, r = cv2.split(img)
img = cv2.merge([r, g, b])

或者用cv自带的convert函数将img转换一下

img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

或者利用逆矩阵把img矩阵bound维反过来

img = img[:,:,::-1]

好,那么今天就学到这里。

猜你喜欢

转载自blog.csdn.net/m0_50317149/article/details/129561954
今日推荐