画像補正
- 画像強化の目的は、特定の応用分野における画像の視覚的な品質を向上させることです。
- 画像強調には、スムージング、シャープニング、エッジ抽出、反転、ノイズ除去、およびさまざまなフィルタリング処理が含まれます。
- その目的は、処理された画像が特定のアプリケーション(主に主観的な観察と分析)により適していることです。
- 普遍的な理論や手法はなく、主観的な評価が主流
画像の強化は、次の 2 つの大きなカテゴリに分類されます。
- 空間ドメイン画像強調: 「空間ドメイン」とは、画像自体の平面を指します。
- 周波数領域での画像強調: フーリエ変換に基づいて処理されます。
画像がヒストグラムに圧縮されると、空間情報が失われます
-
特性 1: 特定の画像には固有のヒストグラムがありますが、その逆はありません。
-
特性 2: 画像内の特定のオブジェクトのヒストグラムは、平行移動不変です。
-
特性 3: 画像内の特定のオブジェクトのヒストグラムは回転不変です。
-
性質4: 画像の面積 $ = \displaystyle\int_0^\infty H(D)$
-
特性 5: 離散画像の場合、∑ D = 0 255 H ( D ) = NL ∗ NS \sum_{D=0}^{255} H(D) = NL * NS∑D = 0255H ( D )=N L∗NS
ここで、NL と NS はそれぞれ画像の行と列です。
-
特性 6: 画像が 2 つの切断された領域で構成されている場合、画像全体のヒストグラムは 2 つの領域のヒストグラムの合計になります。
ヒストグラムの使用法
- 基本的な画像処理ツール: ライト イコライゼーション、特徴表現。
- 画像で使用されているグレースケール範囲が妥当であることを確認してください。【0-255】
- 画像の二値化境界閾値の選択。
デジタル画像のヒストグラム等化ステップ
- 確率:
pr (rk) nkn、k = 0、1、2、... 。。。。, L − 1 p_r(r_k)\frac{n_k}{n},k=0,1,2,....,L-1pr( rk)nnk、k=0 、1 、2 、…… 、L−1
- 累積分布関数:
P ( rk ) = ∑ j = 0 kpr ( rj ) = ∑ j = 0 knkn 、 k = 0 、 1 、 2 、 . 。。。。, L − 1 P(r_k) = \sum{_{j=0}^k}p_r(r_j) = \sum{_{j=0}^k} \frac{n_k}{n} ,k=0 、1、2、....、L-1P ( rk)=∑j = 0kpr( rj)=∑j = 0knnk、k=0 、1 、2 、…… 、L−1
- グレースケール マッピング数変換関数:
sk = T ( rk ) = ( L − 1 ) ∗ ∑ j = 0 kpr ( rj ) = ∑ j = 0 knkn 、 k = 0 , 1 , 2 , . 。。。。, L − 1 s_k = T(r_k) = (L-1)*\sum{_{j=0}^k}p_r(r_j) = \sum{_{j=0}^k} \frac{n_k {n} ,k=0,1,2,....,L-1sk=T ( rk)=( L−1 )∗∑j = 0kpr( rj)=∑j = 0knnk、k=0 、1 、2 、…… 、L−1
- 質問しますsk同じ[sk][s_k]がある場合は切り上げられます。[ sk] を選択してマージします。
from os import cpu_count
import cv2 as cv
import numpy as np
import sys
import math as m
sys.path.append('./')
class HistogramEqualization:
def __init__(self):
pass
def rgb2hsi(self, img_src):
# 对于图像不足3通道的,直接返回原图像
if len(img_src.shape) < 3:
return img_src
img = img_src.astype(np.float32)/255 # 归一化处理
img_r = img[:, :, 0] # 获取红色通道图像
img_g = img[:, :, 1] # 获取绿色通道图像
img_b = img[:, :, 2] # 获取蓝色通道图像
# 执行转换方程
sum_rgb = img_r + img_g + img_b # 求各通道像素值之和
np.where(sum_rgb > 0, sum_rgb, sys.float_info.min)
# 计算S分量
S = 1 - (3 * np.minimum(np.minimum(img_r, img_g), img_b)) / sum_rgb
# 计算H分量
den = ((0.5 * (img_r + img_r - img_g - img_b)) / (np.sqrt((img_r - img_g) * (img_r - img_g) + (img_r - img_b) * (img_g - img_b)) + sys.float_info.min))
# 防止求arccos时参数超出区间[-1, 1]
den[den > 1] = 1
den[den < -1] = -1
H = np.arccos(den)
index = np.where(img_b > img_g) # 找出B>G的坐标值
H[index] = 2 * m.pi - H[index]
H /= 2 * m.pi
H[S == 0] = 0
# 计算I分量
I = sum_rgb / 3
# 拼接三个颜色通道并返回
hsi = np.zeros(img.shape, dtype=np.float32)
hsi[:, :, 0] = H
hsi[:, :, 1] = S
hsi[:, :, 2] = I
return hsi
# HSI色彩空间转换为RGB色彩空间
def hsi2rgb(self, hsi):
# 对于图像不足3通道的,直接返回原图像
if len(hsi.shape) < 3:
return hsi
H = hsi[:, :, 0] # 提取H分量
S = hsi[:, :, 1] # 提取S分量
I = hsi[:, :, 2] # 提取I分量
R = np.zeros(H.shape, dtype=np.float32) # 创建红色通道
G = np.zeros(H.shape, dtype=np.float32) # 创建绿色通道
B = np.zeros(H.shape, dtype=np.float32) # 创建蓝色通道
H *= 2 * m.pi # 扩充弧度范围[0, 2*pi]
# 色调[0, 2*pi/3)范围内对应红->绿
boolh = np.where((H >= 0) & (H < 2 * m.pi / 3)) # 找出符合条件的二维图像数组下标
# 计算红色通道
R[boolh] = I[boolh] * (1 + (S[boolh] * np.cos(H[boolh])) / (np.cos(m.pi / 3 - H[boolh]) + sys.float_info.min))
# 计算蓝色通道
B[boolh] = I[boolh] * (1 - S[boolh])
# 计算绿色通道
G[boolh] = I[boolh] * 3 - (B[boolh] + R[boolh])
# 色调[2*pi/3, 4*pi/3)范围内对应绿->蓝
boolh = np.where((H >= 2 * m.pi / 3) & (H < 4 * m.pi / 3)) # 找出符合条件的二维图像数组下标
H[boolh] -= 2 * m.pi / 3
# 计算红色通道
R[boolh] = I[boolh] * (1 - S[boolh])
# 计算绿色通道
G[boolh] = I[boolh] * (1 + (S[boolh] * np.cos(H[boolh])) / (np.cos(m.pi / 3 - H[boolh])))
# 计算蓝色通道
B[boolh] = I[boolh] * 3 - (R[boolh] + G[boolh])
# 色调[4*pi/3, 2*pi]范围内对应蓝->红
boolh = np.where((H >= 4 * m.pi / 3) & (H < 2 * m.pi)) # 找出符合条件的二维图像数组下标
H[boolh] -= 4 * m.pi / 3
# 计算绿色通道
G[boolh] = I[boolh] * (1 - S[boolh])
# 计算蓝色通道
B[boolh] = I[boolh] * (1 + (S[boolh] * np.cos(H[boolh])) / (np.cos(m.pi / 3 - H[boolh])))
# 计算绿色通道
R[boolh] = I[boolh] * 3 - (B[boolh] + G[boolh])
# 拼接图像
rgb = np.zeros(hsi.shape, dtype=np.uint8)
rgb[:, :, 0] = (R * 255).astype(np.uint8)
rgb[:, :, 1] = (G * 255).astype(np.uint8)
rgb[:, :, 2] = (B * 255).astype(np.uint8)
return rgb
# 遍历每个像素点,把img里面的像素值转换成initImg的下标索引(0-256),并统计img相同像素点的个数
def toHisgram(self,img,initImg):
for i in range(img.shape[0]):
for j in range(img.shape[1]):
initImg[img[i,j]] +=1
return initImg
# 进行直方图均衡化,并返回最终图像
def toHist(self,img):
init_img =np.zeros(256,np.int32)
#直方图统计,获得概率
# hist = self.toHisgram(img,init_img)
hist,den= np.histogram(img,256,[0,255])
hist = hist/np.sum(hist) #统计的像素点(0-256)个数除以总数
cdf = np.zeros(256, dtype=np.float32)
#累计分布函数
np.cumsum(hist[0 : 256], dtype=np.float32, out=cdf[0 : 256])
#变换函数
t =np.zeros(256,np.uint8)
t[0 : 256] = 255 * cdf[0 : 256]
#合并
dstImg = np.zeros(img.shape, dtype=np.uint8)
dstImg[:,:] = t[img[:,:]]
return dstImg
#将RGB转换成HSI色彩空间域,返回HSI图像
def rgbToHsi(self,img):
r = img[:,:,0]
g = img[:,:,1]
b = img[:,:,2]
r = r.astype(np.float32)
g = g.astype(np.float32)
b = b.astype(np.float32)
I = (r+g+b)/3
I = I/255
img_min = np.min(img,axis=-1)
S = 1 - (3/(r+g+b)*img_min)
a = 0.5*((r-g)+(r-b))
botton = ((r-g)**2+((r-b)*(g-b))+ sys.float_info.min)**0.5
den =a /botton
den[den>1]=1
den[den<-1]=-1
H = np.arccos(den)
index = np.where(g<b)
H[index]= 2*m.pi-H[index]
H /= 2 * m.pi
H[S == 0] = 0
hsi = np.zeros([img.shape[0],img.shape[1],img.shape[2]],dtype=np.float32)
hsi[:,:,0] = H
hsi[:,:,1] = S
hsi[:,:,2] = I
return hsi
# 将HSI转换成RGB色彩空间域 ,返回RGB图像
def hsiToRgb(self,hsi):
H = hsi[:,:,0]
S = hsi[:,:,1]
I = hsi[:,:,2]
H *=2*m.pi
rgb = np.zeros(hsi.shape,np.uint8)
R = np.zeros(H.shape, dtype=np.float32)
G = np.zeros(H.shape, dtype=np.float32)
B = np.zeros(H.shape, dtype=np.float32)
index = np.where((H>=0)&(H<2*m.pi/3))
R[index] = I[index] * (1+(S[index]*np.cos(H[index]))/(np.cos(m.pi/3-H[index])))
B[index] = I[index]*(1-S[index])
G[index] = 3*I[index]-(B[index]+R[index])
index = np.where((H>=2*m.pi/3)&(H<4*m.pi/3))
R[index] = I[index]*(1-S[index])
G[index] = I[index] * (1+(S[index]*np.cos(H[index]-2*m.pi/3))/(np.cos(m.pi-H[index])))
B[index] = 3*I[index]-(R[index]+G[index])
index = np.where((H>=4*m.pi/3)&(H<2*m.pi))
B[index] = I[index] * (1+(S[index]*np.cos(H[index]-4*m.pi/3))/(np.cos(5*m.pi/3-H[index])))
G[index] = I[index]*(1-S[index])
R[index] = 3*I[index]-(G[index]+B[index])
rgb[:,:,0] = (255*R).astype(np.uint8)
rgb[:,:,1] = (255*G).astype(np.uint8)
rgb[:,:,2] = (255*B).astype(np.uint8)
return rgb
# 计算熵,首先统计img像素点的个数,计算每个像素点在img的分布概率,遍历计算p[i]*m.log2(p[i]),乘以-1返回熵
def solveEntropy(self,img):
n = img.shape[0] * img.shape[1]
hist,den= np.histogram(img,256,[0,255])
p = hist / n
entropy = 0
for i in range(0,256):
if (p[i]==0):
continue
entropy += p[i]*m.log2(p[i])
return -1 * entropy
#计算图像亮度、对比度、熵值
def compareResult(self,img,typeName):
light = np.mean(img)
contrast = np.std(img)
entropy = self.solveEntropy(img)
print(f"{typeName} 亮度为 {np.mean(img)}, 对比度为 {np.std(img)}, 熵为 {self.solveEntropy(img)}\n")
# 主函数 启动摄像头 处理每一帧的图像
def main(self):
cap = cv.VideoCapture(0)
while cap.isOpened():
ret,frame = cap.read()
hsi = self.rgb2hsi(frame)
# 直方图处理HSI亮度I
I = hsi[:,:,2]
I *= 255
self.compareResult(I,"原I")
I = I.astype(np.uint8)
I = self.toHist(I)
self.compareResult(I,"直方图均衡化I")
hsi[:,:,2] = I/255
rgb = self.hsiToRgb(hsi)
self.compareResult(frame,"原RGB图")
self.compareResult(rgb,"I直方图均衡化RGB图")
cv.imshow("Orgin RGB",frame)
cv.imshow("ToHist RGB",rgb)
if cv.waitKey(1)&0xFF == ord('q'):
break
cap.release()
#执行主函数,实例化HistogramEqualization
if __name__=='__main__':
HistogramEqualization().main()
if cv.waitKey(0)&0xFF == 27:
cv.destroyAllWindows()