Question_01_10

本文介绍了图像处理的基础操作,包括RGB与BGR通道交换、灰度化、二值化、大津二值化算法、HSV变换、颜色量化、平均池化、最大池化、高斯滤波和中值滤波。通过Python代码实现这些操作,有助于初学者理解图像处理的基本原理。
摘要由CSDN通过智能技术生成
  • 本文整理来自Github项目:ImageProcessing100Wen
  • 源项目包含python代码和C++代码,本文代码仅含python,需要的可根据序号去源作者项目中获取
  • python略熟,所以加了一些注释
  • python 中显示图片的代码(自行添加调试):
cv2.imwrite("out.jpg", out)   # 保存图像out为out.jpg,若不需要保存则可去掉
cv2.imshow("result", out)     # 显示图像out,并给该显示窗口命名为result
cv2.waitKey(0)                # 显示图像后,等待按键操作
cv2.destroyAllWindows()       # 按键操作后,销毁所有窗口

Question:模板

这是一个Question的模块框架

代码:

print("这是一个Question的模块框架")

Question1:通道交换

我们熟悉的三原色通道是按RGB顺序储存的,但是openCV读取的图片是以BGR顺序储存的
在这里插入图片描述
关于RGB以及接下来的灰度图、二值图,可看:彩色图、灰度图和二值图

代码:

import cv2
img=cv2.imread("imori.jpg")

# 将三个矩阵分别复制
blue=img[:,:,0].copy()
green=img[:,:,1].copy()
red=img[:,:,2].copy()

# 将三个矩阵按RGB顺序赋值给img矩阵,替换原来的数据
# BGR->RGB
img[:, :, 0] = r
img[:, :, 1] = g
img[:, :, 2] = b

Question2:灰度化(Grayscale)

灰度化的图片仅有灰度信息,没有色彩,也就是俗称的黑白照
RGB格式转Gray的公式如下:
Y = 0.2126   R + 0.7152   G + 0.0722   B Y = 0.2126\ R + 0.7152\ G + 0.0722\ B Y=0.2126 R+0.7152 G+0.0722 B
在这里插入图片描述

代码:

import cv2
import numpy as np

# numpy是科学计算库,对于矩阵运行贼在行,这里是将cv2读取的图片转为了numpy中的float类型
img = cv2.imread("imori.jpg").astype(np.float)

b = img[:, :, 0].copy()
g = img[:, :, 1].copy()
r = img[:, :, 2].copy()

# r、g、b三个矩阵进行了公式换算,结果保存在out中
out = 0.2126 * r + 0.7152 * g + 0.0722 * b 

# 将float类型的out转换为uint8整数类型,因为灰度图矩阵储存的是整数元素
out = out.astype(np.uint8)

Question3:二值化(Thresholding)

相比于常见灰度图用255级表示亮度,二值化只有两种颜色:0(黑色)、1(白色)
如何将灰度图转为二值图,一种最简单的方式是将灰度图中亮度级<128的像素值置为0,亮度级>128的像素值置为1:
在这里插入图片描述
在这里插入图片描述

代码:

import cv2
import numpy as np

# 读取一张彩色图
img=cv2.imread("sample.jpg").astype(np.float32)

# 图片灰度化
b = img[:, :, 0].copy()
g = img[:, :, 1].copy()
r = img[:, :, 2].copy()
gray= 0.2126 * r + 0.7152 * g + 0.0722 * b
gray= gray.astype(np.uint8)

# 图片二值化
gray[gray< 128] = 0  # 将gray矩阵中像素值<128的置0
gray[gray>= 128] = 255  # 将gray矩阵中像素值>128的置1

Question4:大津二值化算法(Otsu)

在Q3中,我们通过人为设定一个阈值,将灰度图中像素分成了两类。但是在实际运用中这个设定的阈值不是万能的,我们能否根据图片信息来自动找到一个最佳的阈值,使得图片二值化更加的合理?
大津算法就是来根据图片本身求最佳阈值的,也成为最大类间方差法。大津算法并不是直接进行二值化,而是得到一个整型值,也即最佳阈值,最后再使用Q3中的方法,进行灰度图的二值化。更详细的介绍和原理

代码中使用的原理计算用每个灰阶做阈值时的类间方差,通过遍历255个灰度级找到最大方差和对应的灰阶值,这就是最佳阈值。
在这里插入图片描述
在代码中使用类间方差的公式:需要将计算四个值:两类各像素量占总像素量的比例,两类各自均值

代码:

import cv2
import numpy as np

# Gray scale
def BGR2GRAY(img):
    b = img[:, :, 0].copy()
    g = img[:, :, 1].copy()
    r = img[:, :, 2].copy()

    # Gray scale
    out = 0.2126 * r + 0.7152 * g + 0.0722 * b
    out = out.astype(np.uint8)

    return out

# 传入灰度图,返回最佳阈值
def otsu_t(out):
    max_sigma=0
    max_t=0
    w,h=out.shape
    
    for t in range(256):
        count0=out[np.where(out<t)]          # 得到所有小于阈值的像素的值,暂且称为0类
                                             # where得到符合条件测点的坐标,横坐标和纵坐标分别独立储存,类似[x..],[y..],第一个坐标为[x1,y1],以此类推
                                             # out[[x..],[y..]]表示取出[X1,y1],[x2,y2],[x3,y3]的值;
        mean0=count0.mean() if len(count0)>0 else 0  # 计算0类的均值
        ocp0=len(count0)/(w*h)                       # 计算0类像素数量占全部像素量的比例
        
        count1=out[np.where(out>=t)]
        mean1=count1.mean() if len(count1)>0 else 0
        ocp1=len(count1)/(w*h)
        
        sigma=ocp0*ocp1*((mean0-mean1)**2)          # 计算当前阈值_t分割的图像的方差sigma
        
        if sigma>max_sigma:                         # 如果这次分割的方差更大,则记录方差,并记录下这个阈值_t
            max_sigma=sigma
            max_t=t
    return max_t  

# Read image
img = cv2.imread("simple.jpg").astype(np.float32)
H, W, C =img.shape


# 灰度化
out = BGR2GRAY(img)  

# 大津法:找到分割该灰度图的最佳阈值
t= otsu_t(out)   

Question5:HSV变换

主要指RGB与HSV的相互变换,HAV指色相(Hue)、饱和度(Saturaton)、明度(Value)来表示色彩的一种方式。

  • 色相:将颜色使用0°到360°表示,一些典型的对照表如下:
绿青色蓝色品红
60°120°180°240°300°360°
  • 饱和度:指色彩的纯度或者深度,比如红色颜色是低饱和度时呈现粉色,高饱和度时呈现深红色,总的来说高饱和度的颜色更加鲜艳。(0≤S≤1)
  • 明度:即亮度,数值越高越亮越白,数值越低接近黑色(0≤V≤1);

转换步骤:

  • RGB→HSV:
    在这里插入图片描述
  • HSV→RGB:
    在这里插入图片描述

代码:

import cv2
import numpy as np


# BGR -> HSV
def BGR2HSV(_img):
	img = _img.copy() / 255.

	hsv = np.zeros_like(img, dtype=np.float32)

	# get max and min
	max_v = np.max(img, axis=2).copy()
	min_v = np.min(img, axis=2).copy()
	min_arg = np.argmin(img, axis=2)

	# H
	hsv[..., 0][np.where(max_v == min_v)]= 0
	## if min == B
	ind = np.where(min_arg == 0)
	hsv[..., 0][ind] = 60 * (img[..., 1][ind] - img[..., 2][ind]) / (max_v[ind] - min_v[ind]) + 60
	## if min == R
	ind = np.where(min_arg == 2)
	hsv[..., 0][ind] = 60 * (img[..., 0][ind] - img[..., 1][ind]) / (max_v[ind] - min_v[ind]) + 180
	## if min == G
	ind = np.where(min_arg == 1)
	hsv[..., 0][ind] = 60 * (img[..., 2][ind] - img[..., 0][ind]) / (max_v[ind] - min_v[ind]) + 300
		
	# S
	hsv[..., 1] = max_v.copy() - min_v.copy()

	# V
	hsv[..., 2] = max_v.copy()
	
	return hsv


def HSV2BGR(_img, hsv):
	img = _img.copy() / 255.

	# get max and min
	max_v = np.max(img, axis=2).copy()
	min_v = np.min(img, axis=2).copy()

	out = np.zeros_like(img)

	H = hsv[..., 0]
	S = hsv[..., 1]
	V = hsv[..., 2]

	C = S
	H_ = H / 60.
	X = C * (1 - np.abs( H_ % 2 - 1))
	Z = np.zeros_like(H)

	vals = [[Z,X,C], [Z,C,X], [X,C,Z], [C,X,Z], [C,Z,X], [X,Z,C]]

	for i in range(6):
		ind = np.where((i <= H_) & (H_ < (i+1)))
		out[..., 0][ind] = (V - C)[ind] + vals[i][0][ind]
		out[..., 1][ind] = (V - C)[ind] + vals[i][1][ind]
		out[..., 2][ind] = (V - C)[ind] + vals[i][2][ind]

	out[np.where(max_v == min_v)] = 0
	out = np.clip(out, 0, 1)
	out = (out * 255).astype(np.uint8)

	return out


# Read image
img = cv2.imread("imori.jpg").astype(np.float32)

# RGB > HSV
hsv = BGR2HSV(img)

# Transpose Hue
hsv[..., 0] = (hsv[..., 0] + 180) % 360

# HSV > RGB
out = HSV2BGR(img, hsv)

# Save result
cv2.imwrite("out.jpg", out)
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.destroyAllWindows()

Question6:减色处理(颜色量化)

我们将图像的颜色种类从2563种降低到43种,即将RGB的取值从0~255变为只取{32,96,160,224},这也被称为色彩量化。色彩值安下面方式定义:
在这里插入图片描述

代码:

import cv2

def decrease_color(img):
    out = img.copy()
    out=out//64*64+32
    return out
    
img=cv2.imread("simple.jpg")
out=decrease_color(img)
cv2.imshow("",out)
cv2.waitKey(0)

在这里插入图片描述

Question7:平均池化

所谓的池化,在视觉上可以理解为降低分辨率。比如44的图像降低为22的图像,但这意味着原来的2*2个像素块现在只能用一个像素表示:
在这里插入图片描述
平均是指:对于粉色像素块,用粉色像素块的平均值来表示这个区域,如(4+6+8+2)/4=5。
当然除了平均池化,还有最大值池化:即用像素区域中的最大值表示这个区域:
在这里插入图片描述

代码:

import cv2
import numpy as np


# average pooling
def average_pooling(img, G=8):
    out = img.copy()

    H, W, C = img.shape
    Nh = int(H / G)
    Nw = int(W / G)

    for y in range(Nh):
        for x in range(Nw):
            for c in range(C):
                out[G*y:G*(y+1), G*x:G*(x+1), c] = np.mean(out[G*y:G*(y+1), G*x:G*(x+1), c]).astype(np.int)
    
    return out


# Read image
img = cv2.imread("imori.jpg")

# Average Pooling
out = average_pooling(img)

# Save result
cv2.imwrite("out.jpg", out)
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述
上述是项目给的源代码,但实际上图像的shape仍为原大小128x128,严格来讲,用8x8网格池化原图像,最终图像shape应该为16x16。下面给出结果为16x16图像的代码,虽然在视觉上效果一样,但是对计算机来讲128x128≠16x16
在这里插入图片描述

import cv2
img=cv2.imread("imori.jpg")

import numpy as np
out=np.zeros((16,16,3),dtype="int") # 创建和结果图一样大的矩阵,方便写入结果

for i in range(16):
    for j in range(16):
        mat=img[i*8:(i+1)*8,j*8:(j+1)*8,...] # 得到每个8*8的区域块
        mean=np.mean(mat,axis=(0,1))   # 计算均值,mean是三个值
        out[i,j,...]=mean

cv2.imshow("",out.astype(np.uint8))
cv2.waitKey(0)

Question8:最大池化

上面已经讲过,用区域块中数值最大的值来表示该区域

代码:

import cv2
import numpy as np

# max pooling
def max_pooling(img, G=8):
    # Max Pooling
    out = img.copy()

    H, W, C = img.shape
    Nh = int(H / G)
    Nw = int(W / G)

    for y in range(Nh):
        for x in range(Nw):
            for c in range(C):
                out[G*y:G*(y+1), G*x:G*(x+1), c] = np.max(out[G*y:G*(y+1), G*x:G*(x+1), c])

    return out


# Read image
img = cv2.imread("imori.jpg")

# Max pooling
out = max_pooling(img)

# Save result
cv2.imwrite("out.jpg", out)
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述
同样的,该结果图仍为128x128,下面给出16x16的结果图代码:
在这里插入图片描述

import cv2
img=cv2.imread("imori.jpg")

import numpy as np
out=np.zeros((16,16,3),dtype="int")

for i in range(16):
    for j in range(16):
        mat=img[i*8:(i+1)*8,j*8:(j+1)*8,...]
        mean=np.max(mat,axis=(0,1)) # 与均值池化代码相比,只是将mean改成了max而已
        out[i,j,...]=mean

cv2.imshow("",out.astype(np.uint8))
cv2.waitKey(0)

Question9:高斯滤波

滤波是一种运算,详见卷积核(又称滤波器)。高斯滤波是卷积核的数值符合高斯分布的一种特殊的滤波器。由于高斯滤波器的特性是类似均值运算,所以对图像进行高斯滤波会造成图像的模糊化。
高斯分布公式计算权值:
在这里插入图片描述
可以看见,权值最大的是x=y=0时。
尺寸为3*3,sigma=1.3的,且经过归一化的(即Σ=1)高斯分布:
在这里插入图片描述
现在使用该滤波器对图像进行高斯滤波,效果如下:
在这里插入图片描述

代码:

import numpy as np
import cv2

# 得到一个高斯滤波器
def getGaussianKernel(size=3,sigma=1.3):
    K=np.zeros((size,size),dtype=np.float)
    
    padding=size//2
    for x in range(size):
        for y in range(size):
            K[x,y]=1/(2*np.pi*sigma**2)*np.exp(-(((x-padding)**2+(y-padding)**2)/(2*sigma**2)))
    K/=K.sum()  # 归一化
    return K


# 滤波操作
def filter(img,K):
    size=K.shape[1]
    padding=size//2
    
    if len(img.shape)==3:
        H,W,C=img.shape
    elif len(img.shape)==2:
        H,W=img.shape
        C=1
        
    tmp=np.zeros((H+2*padding,W+2*padding,C))
    tmp[padding:padding+H,padding:padding+W]=img
    
    out=np.zeros(img.shape)
    for h in range(H):
        for w in range(W):
            for c in range(C):
                out[h,w,c]=np.sum(K*tmp[h:h+size,w:w+size,c])
    out=out.clip(0,255).astype(np.uint8)
    return out


img=cv2.imread("imori_noise.jpg")
K=getGaussianKernel()
out=filter(img,K)
cv2.imshow("",out)
cv2.waitKey(0)

Question10:中值滤波

中值滤波是使用区域内数值居中的像素值代替新的像素。比如带有噪点的图:
在这里插入图片描述
对图进行中值滤波:对于3*3区域,得到该区域中排序位于中间的像素值150,使用150代替原来为0的中央像素,如此边成功将噪点0去掉,完成去噪。
在这里插入图片描述
结果图如下:
在这里插入图片描述

代码:

import numpy as np
import cv2


#中值滤波
def midfilter(img,size):
    padding=size//2
    
    if len(img.shape)==3:
        H,W,C=img.shape
    elif len(img.shape)==2:
        H,W=img.shape
        C=1
    
    tmp=np.zeros((H+2*padding,W+2*padding,C))
    tmp[padding:padding+H,padding:padding+W]=img
    
    out=np.zeros(img.shape)
    
    for h in range(H):
        for w in range(W):
            for c in range(C):
                out[h,w,c]=np.median(tmp[h:h+size,w:w+size,c])
    out=out.astype(np.uint8)
    return out



img=cv2.imread("imori_noise.jpg")
out=midfilter(img,3)
cv2.imshow("",out)
cv2.waitKey(0)
你可以在`payload`中添加一个`cid`字段,根据复选框的状态来判断是否传入`cid`。假设你的复选框是`self.cid_checkbox`,那么你可以这样修改`on_submit`方法: ``` def on_submit(self): prompt = self.prompt_edit.toPlainText() question = self.question_edit.toPlainText() api_url = "https://api.devbit.cn/ai/api/ai/chat" api_key = "ZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKSVV6STFOaUo5LmV5SjBkQ0k2TkN3aVlYVmtJam9pTlRNeE5XTmpZekV3TlRnME5HSTNNVGcyWmpoa09XRTVOVFppTnpGbU5HWWlMQ0pzZFNJNkltZGhiMmhoYjNkbGJpSXNJbVY0Y0NJNk1UWTROVFUwT0RjNU9Td2lkWElpT2pJc0ltcDBhU0k2SWtGUVNWOVVUMHRGVGw4MU16RTFZMk5qTVRBMU9EUTBZamN4T0RabU9HUTVZVGsxTm1JM01XWTBaaTAwSW4wLjM0WWhudzVpblNRVTZROWpkZ0RxRHlCQk1GM2xmN01HYTZBcXZJMV9ydjA=" headers = { "Authorization": "Bearer " + api_key, "Accept": "application/json" } payload = { "prompt": prompt, "question": question, "stream": False } # 根据复选框状态判断是否传入cid if self.cid_checkbox.isChecked(): payload["cid"] = self.cid response = requests.post(api_url, json=payload, headers=headers) if response.status_code == 200: print(response.json()) json_response = response.json() self.cid = str(json_response['data']['cid']) self.json_response_edit.setText(str(json_response['data']['answer'])) else: print(f"Request failed with error code {response.status_code}") self.json_response_edit.setText(f"请求失败,错误码 {response.status_code}") ``` 这样,如果`self.cid_checkbox`被选中,就会在`payload`中传入`cid`;否则不传入`cid`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是一个对称矩阵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值