第六周周报

摘要

   本周对深度学习中权重衰退、丢弃法研究,掌握两种处理过拟合的方法。然后学习了数字图像处理的图像仿射变换理论进行学习,以及学习OPENCV,实现了阈值越界处理、边界填充、各类滤波处理、形态学中腐蚀和膨胀的方法。

   This week, I conducted research on weight decay and dropout techniques in the field of deep learning, and I mastered two methods for addressing overfitting. Additionally, I delved into the theoretical aspects of image affine transformations in digital image processing and learned OpenCV. I successfully implemented various image processing tasks, including threshold handling, boundary padding, various filtering techniques, as well as erosion and dilation methods in morphological operations.

深度学习

权重衰退

权重衰退是常见的处理过拟合的一种方法。把模型容量控制比较小有两种方法,方法一:模型控制的比较小,使得模型中参数比较少。方法二:控制参数选择范围来控制参数容量。
约束就是正则项。每个特征的权重都大会导致模型复杂,从而导致过拟合。控制权重矩阵范数可以使得减少一些特征的权重,甚至使他们权重为0,从而导致模型简单,减轻过拟合。
在这里插入图片描述
在这里插入图片描述
    拉格朗日乘子法原本是用于解决约束条件下的多元函数极值问题。举例,求f(x,y)的最小值,但是有约束C(x,y) = 0。乘子法给的一般思路是,构造一个新的函数g(x,y,λ) = f(x,y) +λC(x,y),当同时满足g’x = g’y = 0时,函数取到最小值。这件结论的几何含义是,当f(x,y)与C(x,y)的等高线相切时,取到最小值。
具体到机器学习这里,C(x,y) = w^2 -θ。所以视频中的黄色圆圈,代表不同θ下的约束条件。θ越小,则最终的parameter离原点越近。
在这里插入图片描述
(1)绿色的线就是原始损失函数l的等高线,优化原始损失l的最优解(波浪号即最优解)在中心位置。
(2)当原始损失加入二分之λ的项后,这个项是一个二次项,假如w就两个值,x1(横轴)、x2(纵轴),那么在图上这个二次项的损失以原点为中心的等高线为橙色的图所示。所以合并后的损失为绿色的和黄色的线加一起的损失。
(3)当加上损失项后,可以知道原来最优解对应的二次项的损失特别大,因此原来的最优解不是加上二次项后的公式的最优解了。若沿着橙色的方向走,原有l损失值会大一些,但是二次项罚的损失会变小,当拉到平衡点以内时,惩罚项减少的值不足以原有l损失增大的值,这样w * 就是加惩罚项后的最优解。
(4)损失函数加上正则项成为目标函数,目标函数最优解不是损失函数最优解。正则项就是防止达到损失函数最优导致过拟合,把损失函数最优点往外拉一拉。鼓励权重分散,将所有额特征运用起来,而不是依赖其中的少数特征,并且权重分散的话它的内积就小一些。
*(5) l2正则项会对大数值的权值进行惩罚。

   总结:1.权重衰退通过L2正则项使得模型参数不会过大,从而控制模型复杂度。2.正则项权重是控制模型复杂度的超参数。

代码实现:

%matplotlib inline
import torch
from torch import nn
from d2l import torch as d2l

n_train, n_test, num_inputs, batch_size = 20, 100, 200, 5 # 数据越简单,模型越复杂,越容易过拟合。num_inputs为特征维度
true_w, true_b = torch.ones((num_inputs,1)) * 0.01, 0.05
train_data = d2l.synthetic_data(true_w, true_b, n_train) # 生成人工数据集
train_iter = d2l.load_array(train_data, batch_size)
test_data = d2l.synthetic_data(true_w, true_b, n_test)
test_iter = d2l.load_array(test_data, batch_size, is_train=False)

# 初始化模型参数
def init_params():
    w = torch.normal(0,1,size=(num_inputs,1),requires_grad=True)
    b = torch.zeros(1,requires_grad=True)
    return [w,b]

# 定义L2范数惩罚
def l2_penalty(w):
    return torch.sum(w.pow(2)) / 2

# 定义训练函数
def train(lambd):
    w, b = init_params()
    net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_loss
    num_epochs, lr = 100, 0.003
    animator = d2l.Animator(xlabel='epoch',ylabel='loss',yscale='log',xlim=[5,num_epochs],legend=['train','test'])                   
    for epoch in range(num_epochs):
        for X, y in train_iter:
            #with torch.enable_grad():
            l = loss(net(X),y) + lambd * l2_penalty(w)
            l.sum().backward()
            d2l.sgd([w,b],lr,batch_size)
        if(epoch+1) % 5 == 0:
            if(epoch+1) % 5 ==0:
                animator.add(epoch + 1, (d2l.evaluate_loss(net, train_iter, loss), d2l.evaluate_loss(net,test_iter,loss)))
    print('w的L2范数是',torch.norm(w).item()) 

help(d2l.synthetic_data) # 查看函数用法
# 忽略正则化直接训练
train(lambd=0)  # 训练集小,过拟合,测试集损失不下降
# 使用权重衰退
train(lambd=3)

丢弃法

   一个好的模型需要对输入数据的扰动鲁棒,使用有噪音的数据等价于加入正则。
   丢弃法将一些输出项随机置0来控制模型复杂度,常作用在多层感知机的隐藏层输出上,丢弃概率是控制模型复杂度的超参数。
在这里插入图片描述
代码实现:


# 实现dropout_layer函数,该函数以dropout的概率丢弃张量输入x中的元素
import torch
from torch import nn
from d2l import torch as d2l

def dropout_layer(X, dropout):
    assert 0 <= dropout <= 1 # dropout大于等于0,小于等于1,否则报错
    if dropout == 1:
        return torch.zeros_like(X) # 如果dropout为1,则X返回为全0
    if dropout == 0:
        return X # 如果dropout为1,则X返回为全原值
    mask = (torch.randn(X.shape)>dropout).float() # 取X.shape里面0到1之间的均匀分布,如果值大于dropout,则把它选出来
    #print((torch.randn(X.shape)>dropout)) # 返回的是布尔值,然后转布尔值为0、1
    return mask * X / (1.0 - dropout)  #用乘法比选0的速度更快

X = torch.arange(16,dtype=torch.float32).reshape((2,8))
print(X)
print(dropout_layer(X, 0.))
print(dropout_layer(X, 0.5)) # 有百分之50的概率变为0
print(dropout_layer(X, 1.))

# 定义具有两个隐藏层的多层感知机,每个隐藏层包含256个单元
num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10 ,256, 256

dropout1, dropout2 = 0.2, 0.5

class Net(nn.Module):
    def __init__(self, num_inputs, num_outputs, num_hiddens1, num_hiddens2,is_training=True):       
        super(Net, self).__init__()
        self.num_inputs = num_inputs
        self.training = is_training
        self.lin1 = nn.Linear(num_inputs, num_hiddens1)
        self.lin2 = nn.Linear(num_hiddens1, num_hiddens2)
        self.lin3 = nn.Linear(num_hiddens2, num_outputs)
        self.relu = nn.ReLU()
        
    def forward(self, X):
        H1 = self.relu(self.lin1(X.reshape((-1,self.num_inputs))))
        if self.training == True: # 如果是在训练,则作用dropout,否则则不作用
            H1 = dropout_layer(H1, dropout1)
        H2 = self.relu(self.lin2(H1))
        if self.training == True:
            H2 = dropout_layer(H2,dropout2)
        out = self.lin3(H2) # 输出层不作用dropout
        return out
        
net = Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2)

# 训练和测试
num_epochs, lr, batch_size = 10, 0.5, 256
loss = nn.CrossEntropyLoss()
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
trainer = torch.optim.SGD(net.parameters(),lr=lr)
d2l.train_ch3(net,train_iter,test_iter,loss,num_epochs,trainer)

数字图像处理

   人眼具有的功能可以作为计算机视觉,比如可以识别物体,可以认识人,但不能够精确获得障碍的距离,但是足以避障。
   机器视觉能够精确测出一个物体的尺寸,物体和相机之间的相对距离和角度等。
   计算机视觉依赖人眼和大脑,有智能化功能,也依赖经验累计。机器视觉则是依赖严格的几何,重在精确。
   计算机视觉为机器视觉提供理论和算法基础,机器视觉是计算机视觉的工程实现。
   无论机器视觉、计算机视觉,处理的核心都是图像。数字图像处理技术就是以图像为基础,对图像做各种处理(滤波、变换、分割)等处理的技术。

图像处理基础

   图像是以矩阵的方式在计算机里存储,矩阵中的每个格子代表的颜色,颜色的深浅用灰度来表示。
   通过成像系统相机拍摄,数字化之后图像就是一个二维数组。
   访问图像:图像就是多维数组。
两种方式访问图像:
第一种:宽通道值
第二种:宽通道序列化
在OpenCV中,如下图访问:
在这里插入图片描述

图像仿射变换

仿射变换:“线性变换”+平移
线性变换:变换前是直线的,变换后依然是直线。直线比例保持不变。
在这里插入图片描述
对需要原始坐标左乘一个变换矩阵。

恒等变换:
在这里插入图片描述
尺度变换:
在这里插入图片描述
旋转变换:
在这里插入图片描述
平移:
在这里插入图片描述
偏移(垂直):
在这里插入图片描述偏移(水平):
在这里插入图片描述

灰度变换

(1)图像反转
原理:灰度级数-当前灰度 S=L-1-r
(2)对数变换
s = c ⋅ log ⁡ v + 1 ( 1 + v ⋅ r ) r ∈ [ 0 , 1 ] s=c \cdot \log _{v+1}(1+v \cdot r) \quad r \in[0,1] s=clogv+1(1+vr)r[0,1]
在这里插入图片描述
(3)Gamma变换
s = c r γ r ∈ [ 0 , 1 ] s=c r^\gamma \quad r \in[0,1] s=crγr[0,1]
Gamma变换就是幂指数校正,目的是将灰度较窄的区域拉伸为较宽的区域。
(4)直方图均衡化
由于图像的对比度不强,所以图像整体上较暗或较亮(即图像灰度几种在直方图的两端),为了增加对比度,让图像看起来更清楚,所以有了直方图均衡化。
比如如下一个图像矩阵:
在这里插入图片描述
然后统计灰度值个数,求出灰度占比,与对应灰度值个数相乘,最后均衡化,得出下表:
在这里插入图片描述

OpenCV示例

在这里插入图片描述
RGB图像是由三维数组构成的,是由一个个1行三列的矩阵构成的。

最基本的图像操作

图像读取和显示

import cv2 #opencv的缩写为cv2
import matplotlib.pyplot as plt # matplotlib库用于绘图展示
import numpy as np   # numpy数值计算工具包

# 魔法指令,直接展示图,Jupyter notebook 特有
%matplotlib inline   
img = cv2.imread('01_Picture/01_cat.jpg') 
print(type(img)) # img 的类型为 numpy.ndarray 类型
img              # uint8 的取值范围在 0-255 之间
# opencv 默认读取格式是 BGR 格式,matplotlib 或其他库的读取格式可能是 RGB 的
# opencv 读取并用 opencv 自带的展示函数不需要进行通道转换,但 opencv 读取后用其他库展示图片需要通道转换    
# 图像显示时,可以创建多个窗口
# 第一个入口参数为展示图像窗口的名字
# 第二个入口参数为展示图像窗口中所展示的图像
img = cv2.imread('01_Picture/01_cat.jpg') 
cv2.imshow('image_cat',img)  
# 等待时间,毫秒级,0表示任意键终止,5000ms表示5s
cv2.waitKey(5000)  
# 销毁图像窗口
cv2.destroyAllWindows()
# 绘图显示(封装函数)
def cv_show(name,img):
    cv2.imshow(name,img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()  
      
cv_show('image_cat',img)

img = cv2.imread('01_Picture/01_cat.jpg') 
img.shape # (h,w,c) c表示 3 通道,这个 3 通道被 opencv 读进来是 BGR 的先后顺序的 3 通道    
			#在OpenCV里,通道顺序有所变化。BGR。
#读取灰度图像
img_gray = cv2.imread('01_Picture/01_cat.jpg',cv2.IMREAD_GRAYSCALE)    
#img_gray = cv2.imread('01_Picture/01_cat.jpg',cv2.IMREAD_COLOR)  # BGR图

#输出灰度图像的各种信息:  
img_gray  # 只有一个通道,同样是 uint8 类型
print('type(img_gray):',type(img_gray))
print('img_gray.size: ',img_gray.size)  # 414 × 500 = 20700
print('img_gray.dtype:',img_gray.dtype)
print('img_gray.shape:',img_gray.shape)

#保存图像
cv2.imwrite('01_Picture/02_cat_gray.jpg',img_gray) 
# 把当前的结果保存在路径中

在这里插入图片描述
在这里插入图片描述

HSV颜色空间

为什么引入HSV颜色空间?
(1)RGB 的局限性
  RGB 是我们接触最多的颜色空间,由三个通道表示一幅图像,分别为红色®,绿色(G)和蓝色(B)。这三种颜色的不同组合可以形成几乎所有的其他颜色。
RGB 颜色空间是图像处理中最基本、最常用、面向硬件的颜色空间,比较容易理解。RGB 颜色空间利用三个颜色分量的线性组合来表示颜色,任何颜色都与这三个分量有关,而且这三个分量是高度相关的,所以连续变换颜色时并不直观,想对图像的颜色进行调整需要更改这三个分量才行。
自然环境下获取的图像容易受自然光照、遮挡和阴影等情况的影响,即对亮度比较敏感。而 RGB 颜色空间的三个分量都与亮度密切相关,即只要亮度改变,三个分量都会随之相应地改变,而没有一种更直观的方式来表达。
但是人眼对于这三种颜色分量的敏感程度是不一样的,在单色中,人眼对红色最不敏感,蓝色最敏感,所以 RGB 颜色空间是一种均匀性较差的颜色空间。如果颜色的相似性直接用欧氏距离来度量,其结果与人眼视觉会有较大的偏差。对于某一种颜色,我们很难推测出较为精确的三个分量数值来表示。所以,RGB 颜色空间适合于显示系统,却并不适合于图像处理。

(2)HSV 颜色空间
  基于上述理由,在图像处理中使用较多的是 HSV 颜色空间,它比 RGB 更接近人们对彩色的感知经验。非常直观地表达颜色的色调、鲜艳程度和明暗程度,方便进行颜色的对比。在 HSV 颜色空间下,比 BGR 更容易跟踪某种颜色的物体,常用于分割指定颜色的物体。

HSV 表达彩色图像的方式由三个部分组成:

Hue(色调、色相)
Saturation(饱和度、色彩纯净度)
Value(明度)
在这里插入图片描述
在 GRB中 颜色由三个值共同决定,比如黄色为即 (255,255,0);在HSV中,黄色只由一个值决定,Hue=60即可。

  水平方向表示饱和度,饱和度表示颜色接近光谱色的程度。饱和度越高,说明颜色越深,越接近光谱色饱和度越低,说明颜色越浅,越接近白色。饱和度为0表示纯白色。取值范围为0~100%,值越大,颜色越饱和。
  竖直方向表示明度,决定颜色空间中颜色的明暗程度,明度越高,表示颜色越明亮,范围是 0-100%。明度为0表示纯黑色(此时颜色最暗)。在Hue一定的情况下,饱和度减小,就是往光谱色中添加白色,光谱色所占的比例也在减小,饱和度减为0,表示光谱色所占的比例为零,导致整个颜色呈现白色。明度减小,就是往光谱色中添加黑色,光谱色所占的比例也在减小,明度减为0,表示光谱色所占的比例为零,导致整个颜色呈现黑色。

  HSV 对用户来说是一种比较直观的颜色模型。我们可以很轻松地得到单一颜色,即指定颜色角H,并让V=S=1,然后通过向其中加入黑色和白色来得到我们需要的颜色。增加黑色可以减小V而S不变,同样增加白色可以减小S而V不变。例如,要得到深蓝色,V=0.4 S=1 H=240度。要得到浅蓝色,V=1 S=0.4 H=240度。
  HSV 的拉伸对比度增强就是对 S 和 V 两个分量进行归一化(min-max normalize)即可,H 保持不变。
RGB颜色空间更加面向于工业,而HSV更加面向于用户,大多数做图像识别这一块的都会运用HSV颜色空间,因为HSV颜色空间表达起来更加直观!

代码使用:

hsv = cv2.imread('01_Picture/01_cat.jpg', cv2.COLOR_BGR2HSV) 
cv2.imshow('hsv',hsv)
cv2.waitKey(0)
cv2.destroyAllWindows()

读取视频

import cv2 #opencv的缩写为cv2
import matplotlib.pyplot as plt # matplotlib库用于绘图展示
import numpy as np   # numpy数值计算工具包

# 魔法指令,直接展示图,Jupyter notebook特有
%matplotlib inline 

# 绘图显示(封装函数)
def cv_show(name,img):
    cv2.imshow(name,img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
vc = cv2.VideoCapture('02_Video/00_Scenery.mp4')
if vc.isOpened():   # 检查是否打开正确
    open, frame = vc.read() # 这里的 vc.read() 相当于读取图像的第一帧
                            # 若循环不断的执行 vc.read,则不断的读取第二帧、第三帧....
    print(open) # 正常打开时,open会返回 True
    cv_show('image_scenery',frame)
else:
    open = False
while open: # 如果正常打开,则不停循环读取,这里可替换成 i 值,来确定读取 i 帧     
    ret, frame = vc.read()
    if frame is None: # 视频读完以后的下一帧为空
        break
    if ret == True:
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 读取的图片转换成黑白的   
        cv2.imshow('result',gray)
        if cv2.waitKey(10) & 0xFF == 27: # cv2.waitKey(10)为等多少时间执行下一帧,0xFF为退出键ESC
            break
vc.release()
 # release()完成与 open() 相反的工作.释放 open() 向内核申请的所有资源
cv2.destroyAllWindows() # 销毁所有窗口

#截取部分图像数据
img = cv2.imread('01_Picture/01_cat.jpg')
cat = img[0:200,0:200]
cv_show('cat',cat)

#分离BGR通道
#把一张图片切出来三张出来,每张对应的
img = cv2.imread('01_Picture/01_cat.jpg')
b,g,r = cv2.split(img)  #一定记得cv2分离的通道顺序:bgr
cv_show('cat_b',b)
print('b.shape:',b.shape) # B通道,单通道,灰度图
cv_show('cat_g',g)
print('g.shape:',g.shape) # G通道,单通道,灰度图
cv_show('cat_r',r)
print('r.shape:',r.shape) # R通道,单通道,灰度图
img = cv2.merge((b,g,r))
print('img.shape:',img.shape) # 3 通道,彩色图

#又合并在一起显示
b,g,r = cv2.split(img)
img = cv2.merge((b,g,r))

#只展示B通道
cur_img = img.copy()
cur_img[:,:,0] = 0 
cur_img[:,:,1] = 0
cv_show('R',cur_img)

#只展示G通道
img = cv2.imread('01_Picture/01_cat.jpg')
cur_img = img.copy()
cur_img[:,:,1] = 0 
cur_img[:,:,2] = 0
cv_show('B',cur_img)
#只展示r通道
img = cv2.imread('01_Picture/01_cat.jpg')
cur_img = img.copy()
cur_img[:,:,1] = 0 
cur_img[:,:,2] = 0
cv_show('B',cur_img)

边界填充
边界填充就是对图像进行一些变换,让原始图像进行扩大。
边界填充的入口参数:
BORDER_REPLICATE:复制法,也就是复制最边缘像素。
BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制例如:fedcba|abcdefgh|hgfedcb
BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba
BORDER_WRAP:外包装法cdefgh|abcdefgh|abcdefg
BORDER_CONSTANT:常量法,常数值填充。

import cv2 
import matplotlib.pyplot as plt 
import numpy as np 
%matplotlib inline   
img = cv2.imread('01_Picture/01_cat.jpg')

top_size,bottom_size,left_size,right_size = (50,50,50,50)  # 填充多少区域

# 最后一个入口参数为填充方式

# 方式一:复制法  
replicate = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_REPLICATE) 
#BORDER_REPLICATE:复制法,也就是复制最边缘像素。

# 方式二:反射法
reflect = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_REFLECT)
#BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制例如:fedcba|abcdefgh|hgfedcb

# 方式三:反射法二(不要最边缘的像素)
reflect101 = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_REFLECT_101)  
#BORDER_REFLECT:反射法,也就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba
  
# 方式四:外包装法
wrap = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_WRAP)
#外包装法cdefgh|abcdefgh|abcdefg
# 方式五:常量法
constant = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_CONSTANT,value=0)
#BORDER_CONSTANT:常量法,常数值填充。

import matplotlib.pyplot as plt
plt.subplot(231), plt.imshow(img,'gray'), plt.title('ORIGINAL')
#subplot(231)代表把一个图分成2行3列,最后一位代表图片的序号
plt.subplot(232), plt.imshow(replicate,'gray'), plt.title('REPLICATE')
plt.subplot(233), plt.imshow(reflect,'gray'), plt.title('REPLECT')
plt.subplot(234), plt.imshow(wrap,'gray'),plt.title('REFLECT_101')
plt.subplot(235), plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236), plt.imshow(constant,'gray'),plt.title('CONSTAVI')
plt.show()

输出图像:
在这里插入图片描述

图像融合
想要把两个不一样大小的图像加在一起,就需要用到OpenCV提供的函数:add()

import cv2 
import matplotlib.pyplot as plt 
import numpy as np 
%matplotlib inline
  
print(img_cat.shape)
print(img_dog.shape)
img_dog = cv2.resize(img_dog,(500,414))   #resize函数将矩阵变化其形态,改变的矩阵大小小于原矩阵就舍去多余元素,如果大于原大小则补0
img_dog.shape

res = cv2.addWeighted(img_cat,0.4,img_dog,0.6,0) # img_cat 的权重为 0.4,img_dog 的权重为 0.6 
print(img_dog.shape)
plt.imshow(res)

输出图像:
在这里插入图片描述
图像缩放

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

img = cv2.imread('01_Picture/01_cat.jpg') 
res = cv2.resize(img,(0,0),fx=3,fy=1) # (0,0)表示不确定具体值,fx=3 相当于行像素 x 乘 3,fy=1 相当于 y 乘 1   
plt.imshow(res)

#res = cv2.resize(img,(2width,2height), fx, fy, interpolation=cv2.INTER_CUBIC)
#第一个参数img要缩放的图像名,可以通过两种方法确定缩放后图片大小,第一种利用参数(2width,2height),用来直接指定缩放后的宽度和长度;第二种,利用参数fx和fy,分别指定宽度和高度的缩放比例因子。

res = cv2.resize(img,(0,0),fx=1.5,fy=1.5) # 同比例放缩
plt.imshow(res)

在这里插入图片描述
在这里插入图片描述
图像阈值
src: 输入图,只能输入单通道图像,通常来说为灰度图
thresh: 阈值
dst: 输出图
ret: 阈值
maxval: 当像素值超过了阈值 ( 或者小于阈值,根据 type 来决定 ),所赋予的值
type:二值化操作的类型,包含以下5种类型:
cv2.THRESH_BINARY 超过阈值部分取maxval ( 最大值 ),否则取0
cv2.THRESH_BINARY_INV THRESH_BINARY的反转
小于127取255,大于127的取0
cv2.THRESH_TRUNC 大于阈值部分设为阈值,否则不变
cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0
cv2.THRESH_TOZERO_INV THRESH_TOZERO的反转
代码应用:

import cv2 
import matplotlib.pyplot as plt 
import numpy as np  
%matplotlib inline  
img = cv2.imread('01_Picture/01_cat.jpg',cv2.IMREAD_COLOR)  
img_gray = cv2.imread('01_Picture/01_cat.jpg',cv2.IMREAD_GRAYSCALE)   
#先导入两个图,一个是读入彩色图片,一个是读入灰度图片

 
ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)    
print(ret)
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV) # THRESH_BINARY_INV 相对 THRESH_BINARY 黑的变成白的,白的变成黑的       
print(ret)
ret, thresh3 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC)     
print(ret)
ret, thresh4 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)
print(ret)
ret, thresh5 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV)
print(ret)

titles = ['original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']        
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]  

for i in range(6):
    plt.subplot(2,3,i+1), plt.imshow(images[i],'gray')  
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()

输出结果为:
在这里插入图片描述

阈值越界处理

img_cat = cv2.imread('01_Picture/01_cat.jpg')
img_dog = cv2.imread('01_Picture/03_dog.jpg')

img_cat2 = img_cat + 10 # 将 img_cat 矩阵中每一个值都加 10
print(img_cat[:5,:,0])  #python中切片和索引的同时使用,取最外层0到5行,所有列的,第0个元素,即B值。
print(img_cat2[:5,:,0])
print((img_cat+img_cat2)[:5,:,0])  # 0-255 若相加越界后 294 用 294%256 获得余数 38    

cv2.add(img_cat,img_cat2)[:5,0] # cv2.add 是越界后取最大值 255  

输出为:
在这里插入图片描述
在这里插入图片描述图像处理
假设有一张图像上有很多噪声点,想要修复图像,就使用均值滤波的函数,它也是简单的平均卷积操作。每一个点的值等于它一周(包含它自己)其余点的和的平均值。
均值滤波

img = cv2.imread('01_Picture/04_LenaNoise.png')
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
blur = cv2.blur(img,(3,3)) # (3,3) 为核的大小,通常情况核都是奇数 3、5、7
cv2.imshow('blur',blur)
cv2.waitKey(0)
cv2.destroyAllWindows()

方块滤波
在 Python 中 -1 表示自适应填充对应的值,这里的 -1 表示与颜色通道数自适应一样

# 方框滤波
# 基本和均值一样,可以选择归一化

# 在 Python 中 -1 表示自适应填充对应的值,这里的 -1 表示与颜色通道数自适应一样
box = cv2.boxFilter(img,-1,(3,3),normalize=True)  # 方框滤波如果做归一化,得到的结果和均值滤波一模一样
box = cv2.boxFilter(img,-1,(3,3),normalize=False)  # 越界的值取 255,因此得到的图像就非常的白
cv2.imshow('box',box)
cv2.waitKey(0)
cv2.destroyAllWindows()

高斯滤波

# 高斯函数,越接近均值时,它的概率越大。
# 离中心值越近的,它的权重越大,离中心值越远的,它的权重越小。

aussian = cv2.GaussianBlur(img,(5,5),1)
cv2.imshow('aussian',aussian)
cv2.waitKey(0)
cv2.destroyAllWindows()

中值滤波

# 中值滤波
# 排序后拿中值替代中间元素值的大小

median = cv2.medianBlur(img,5)
cv2.imshow('median',median)
cv2.waitKey(0)
cv2.destroyAllWindows()


#展示所有滤波
res = np.hstack((blur,aussian,median)) # 矩阵横着拼接
#res = np.vstack((blur,aussian,median)) # 矩阵竖着拼接
print(res)
cv2.imshow('median vs average', res)      
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

形态学操作

腐蚀操作
腐蚀(erode)

img = cv2.imread('01_Picture/05_Dige.png')
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(img,kernel,iterations=1) #iterations是表示迭代几次,1次就表示腐蚀一次。
cv2.imshow('erosion',erosion)
cv2.waitKey(0)
cv2.destroyAllWindows()

膨胀操作
膨胀与腐蚀相反(dilate)

kernel = np.ones((3,3),np.uint8)
dige_erosion = cv2.erode(img,kernel,iterations=1)     
cv2.imshow('erosion',dige_erosion)
cv2.waitKey(0)
cv2.destroyAllWindows()

kernel = np.ones((3,3),np.uint8)
dige_dilate = cv2.dilate(dige_erosion,kernel,iterations=1)   #膨胀函数    
cv2.imshow('dilate',dige_dilate)
cv2.waitKey(0)
cv2.destroyAllWindows()

以上膨胀的代码是实现了一个把汉字周围毛边修复后,再加粗汉字形态粗细的功能。输出如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
膨胀圆形图案,会使其变的像圆形矩阵。

pie = cv2.imread('01_Picture/06_pie.png')

kernel = np.ones((30,30),np.uint8)
dilate_1 = cv2.dilate(pie,kernel,iterations=1)
dilate_2 = cv2.dilate(pie,kernel,iterations=2)
dilate_3 = cv2.dilate(pie,kernel,iterations=3)
res = np.hstack((dilate_1,dilate_2,dilate_3))
cv2.imshow('res',res)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

开运算与闭运算
开运算就是先腐蚀再膨胀
闭运算就是先膨胀再腐蚀

import cv2
import matplotlib.pyplot as plt 
import numpy as np  
%matplotlib inline   
img = cv2.imread('01_Picture/05_Dige.png')

kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel) 

cv2.imshow('opening',opening)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 闭:先膨胀,再腐蚀
img = cv2.imread('01_Picture/05_Dige.png')

kernel = np.ones((5,5),np.uint8)
closing = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel) 

cv2.imshow('closing',closing)
cv2.waitKey(0)
cv2.destroyAllWindows()

** 梯度运算**
梯度 = 膨胀-腐蚀

# 梯度 = 膨胀-腐蚀
pie = cv2.imread('01_Picture/06_pie.png')

kernel = np.ones((7,7),np.uint8)
dilate = cv2.dilate(pie,kernel,iterations=5) 
erosion = cv2.erode(pie,kernel,iterations=5) 

res = np.hstack((dilate,erosion))

cv2.imshow('res',res)
cv2.waitKey(0)
cv2.destroyAllWindows()

gradient = cv2.morphologyEx(pie,cv2.MORPH_GRADIENT,kernel)
cv2.imshow('gradient',gradient)
cv2.waitKey(0)
cv2.destroyAllWindows()

礼帽
礼帽操作=原始输入-开运算
cv.morphologyEx(,cv2.MORPH_TOPHAT,)
原始带刺,开运算不带刺,原始输入-开运算 = 刺

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


# 礼帽 
# 原始带刺,开运算不带刺,原始输入-开运算 = 刺
img = cv2.imread('01_Picture/05_Dige.png')
kernel = np.ones((5,5),np.uint8)
tophat = cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel)
cv2.imshow('tophat',tophat)
cv2.waitKey(0)
cv2.destroyAllWindows()

黑帽

# 黑帽  
# 原始带刺,闭运算带刺并且比原始边界胖一点,闭运算-原始输入 = 原始整体
img = img = cv2.imread('01_Picture/05_Dige.png')
kernel = np.ones((5,5),np.uint8)
blackhat = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel)
cv2.imshow('blackhat',blackhat)
cv2.waitKey(0)
cv2.destroyAllWindows()

Sobel算子、Scharr算子、Laplacian算子

Sobel算子

cv2.Sobel(src, ddepth, dx, dy, ksize),返回值为Sobel算子处理后的图像。
src:输入的图像
ddepth:图像的深度,在实际操作中,计算梯度值可能会出现负数。如果处理的图像是8位图类型,则在ddepth的参数值为-1时,意味着指定运算结果也是8位图类型,那么所有负数会自动截断为0,发生信息丢失。为了避免信息丢失,在计算时要先使用更高的数据类型cv2.CV_64F,再通过取绝对值将其映射为cv2.CV_8U(8位图)类型。
因此通常要将函数cv2.Sobel()内参数ddepth的值设置为“cv2.CV_64F”。
dx 和 dy 分别表示水平和竖直方向
ksize 是 Sobel 算子的大小

import cv2
import matplotlib.pyplot as plt 
import numpy as np  
%matplotlib inline   
pie = cv2.imread('01_Picture/06_pie.png') # 读取图像
cv2.imshow('img',pie)
cv2.waitKey()
cv2.destroyAllWindows()
# 梯度就是边界点,左边右边不一样
def cv_show(img,name):
    cv2.imshow(name,img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    
# 白到黑是整数,黑到白是负数了,所有的负数会被截断成 0,所以要取绝对值
sobelx = cv2.Sobel(pie,cv2.CV_64F,1,0,ksize=3) # 1,0 表示只算水平方向梯度
cv_show(sobelx,'sobelx')
#这时候显示出来的只是左边边缘的图像,因为是从右往左减。
sobelx = cv2.Sobel(pie,cv2.CV_64F,1,0,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx) # 取负数时,取绝对值
cv_show(sobelx,'sobelx')  #显示的是整个边缘的图像(除了正上方和正下方的图像)


sobely = cv2.Sobel(pie,cv2.CV_64F,0,1,ksize=3)#0.1表示垂直方向的梯度
sobely = cv2.convertScaleAbs(sobelx)#同样去绝对值
cv_show(sobely,'sobely')

#计算x和y后,再求和
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0) 
#0是偏执项
cv_show(sobelxy,'sobelxy')


# 不建议直接计算,还有重影
sobelxy = cv2.Sobel(pie,cv2.CV_64F,1,1,ksize=3)
sobelxy = cv2.convertScaleAbs(sobelxy)
cv_show(sobelxy,'sobelxy')

一般求图像梯度,是先分成水平和垂直两个方向上进行分别计算,然后再用带权的加法函数进行统一计算。
不建议直接计算,因为会产生重影。

不求绝对值时
在这里插入图片描述
在这里插入图片描述

计算后相加的图像

总结

由于我是直接看的OPENCV的课程,将就的是快速上手,对其中比较多的图像处理的方法背后的原理不是很理解,比如最开始获取一个图像的灰度图,代码就是cv2.IMREAD_GRAYSCALE,但是背后它是怎么获得的,每个像素点的各个通道的值是各取多少,最后怎么转换为灰度的值,这些我就没有掌握。因此需要兼顾着基础理论的学习,计算机视角、数字图像处理的相关课程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值