Python實現(xiàn):高斯濾波 均值濾波 中值濾波 Canny(邊緣檢測)PCA主成分分析 直方圖規(guī)定化 Mean_Shift(文末附上整合這些函數(shù)的可視化界面并且已做打包處理)
1.高斯濾波(以下函數(shù)所有的圖片路徑為方便前來copy的同學(xué),修改這里全設(shè)置為絕對路徑,卷積核大小和其他參數(shù)按照自己需求改)
import cv2
import numpy as np
import math
SIZE = 3 # 卷積核大?。ㄖ荒転槠鏀?shù))
padding = SIZE // 2
sigma = 3
# 生成高斯卷積核(定卷積核中心坐標(biāo)為(0,0))
GaussKernel = np.zeros((SIZE, SIZE))
for i in range(SIZE):
for j in range(SIZE):
xxAddyy = math.pow(i - padding, 2) + math.pow(j - padding, 2)
GaussKernel[i, j] = math.exp(-xxAddyy / (2 * math.pow(sigma, 2))) / 2 * math.pi * math.pow(sigma, 2)
sum = np.sum(GaussKernel)
GaussKernel = GaussKernel / sum # 歸一化
""""
TmMask2 帶有padding和img的模板
x,y為TmMask2映射到Temp的像素偏移量
"""
# 獲取待模糊像素(x,y)附近Mask大小的像素區(qū)域
def GetMatrix(TmMask2, x, y):
Temp = np.zeros([SIZE, SIZE])
for X in range(SIZE):
for Y in range(SIZE):
Temp[X][Y] = TmMask2[x + X][y + Y]
return np.array(Temp) # 返回需要卷積的區(qū)域副本
"""*********************** 一.讀取圖像 ************************************"""
path_Gaussian =r'C:\Users\Administrator\Desktop\GUI2\fun\images\GaussianBlur.jpg'#輸入圖片的絕對路徑
img = cv2.imread(path_Gaussian, 0)
img2 = cv2.imread(path_Gaussian, 0)
h, w = img.shape # 高度 寬度
"""*********************** 二.制作padding模板 ************************************"""
TmMask = np.zeros([h + padding * 2, w + padding * 2])
TmMask[padding:h + padding, padding:w + padding] = img # 把圖像嵌入空白模板TmMask的padding內(nèi)
"""*********************** 三.卷積并修改原灰度圖的像素值 ************************************"""
# 對img每個像素循環(huán)卷積修改img圖像數(shù)組中的值
for x in range(h):
for y in range(w):
# 卷積(數(shù)組相乘)并修改原圖像素
img[x][y] = np.sum(GetMatrix(TmMask, x, y) * GaussKernel)
""" *********************** 四.高斯平滑后與原灰度圖輸出 ************************************"""
cv2.imshow("Gaussian", img)
cv2.imshow("SRC", img2)
cv2.waitKey()
左側(cè)為原圖右側(cè)為高斯濾波后的圖
2.均值濾波
import cv2
import numpy as np
path = r"C:\Users\Administrator\Desktop\GUI2\fun\images\LennaSaltNoise.jpg" # 圖像路徑
SIZE = 5#卷積核大小
padding = SIZE // 2 # 四個方向需要加的padding大小
def Middle(temp, x, y, SIZE):
sum = 0
for X in range(SIZE):
for Y in range(SIZE):
sum += temp[x + X][y + Y]
return round(sum / (SIZE * SIZE)) # 返回平均數(shù)(四舍五入)
"""*********************************************** START ***********************************************************"""
# img = Image.open("E:\\a2.jpg").convert('L')#讀取圖片并轉(zhuǎn)為灰度圖
# img = Image.open(path).convert('L')#讀取圖片并轉(zhuǎn)為灰度圖
img = cv2.imread(path, 0)
img2 = cv2.imread(path, 0)
h, w = img.shape # 行 列
# 生成一個帶有padding的臨時模板圖像
TempArry = np.zeros([h + padding * 2, w + padding * 2]) # 生成一個和圖像對應(yīng)的加入padding的空白模板
TempArry[padding:h + padding, padding:w + padding] = img # 把圖像嵌入空白模板的padding內(nèi)
# 循環(huán)對像素進行逐像素中值濾波
for x in range(0, h):
for y in range(0, w):
img[x, y] = Middle(TempArry, x, y, SIZE) # 卷積后的中位數(shù)賦值給原灰度圖像素
cv2.imshow("Mean", img)
cv2.imshow("SRC", img2)
cv2.waitKey()
左圖為帶有噪聲的輸入原圖,右圖為經(jīng)過均值濾波后的圖片
3.中值濾波
import cv2
import numpy as np
path = "E:\\LennaSaltNoise.jpg" # 圖像路徑
SIZE = 5 # 卷積核大小
padding = SIZE // 2 # 四個方向需要加的padding大小
def Middle(temp, x, y, SIZE):
# Mask = np.zeros([SIZE,SIZE])#臨時卷積核
s = [] # 把(x,y)周圍像素(也就是對應(yīng)卷積區(qū)域)的像素放入列表s
for X in range(SIZE):
for Y in range(SIZE):
# Mask[X,Y] = temp[x+X][y+Y]
s.append(temp[x + X][y + Y])
s.sort()
return int(s[SIZE * SIZE // 2 + 1]) # 返回中位數(shù)
"""*********************************************** START ***********************************************************"""
# img = Image.open("E:\\a2.jpg").convert('L')#讀取圖片并轉(zhuǎn)為灰度圖
# img = Image.open(path).convert('L')#讀取圖片并轉(zhuǎn)為灰度圖
img = cv2.imread(path, 0)
img2 = cv2.imread(path, 0)
h, w = img.shape # 行 列
# 生成一個帶有padding的臨時模板圖像
TempArry = np.zeros([h + padding * 2, w + padding * 2]) # 生成一個和圖像對應(yīng)的加入padding的空白模板
TempArry[padding:h + padding, padding:w + padding] = img # 把圖像嵌入空白模板的padding內(nèi)
# 循環(huán)對像素進行逐像素中值濾波
for x in range(0, h):
for y in range(0, w):
img[x, y] = Middle(TempArry, x, y, SIZE) # 卷積后的中位數(shù)賦值給原灰度圖像素
cv2.imshow("Middle", img)
cv2.imshow("SRC", img2)
cv2.waitKey()
左圖為帶有噪聲的輸入原圖,右圖為經(jīng)過中值濾波后的圖
4.Canny邊緣檢測
import numpy as np
import cv2
"""
# ******高斯平滑******
sigma1 = sigma2 = 1
sum = 0
gaussian = np.zeros([5, 5])
for i in range(5):
for j in range(5):
gaussian[i, j] = math.exp(-1 / 2 * (np.square(i - 2) / np.square(sigma1) # 生成二維高斯分布矩陣
+ (np.square(j - 2) / np.square(sigma2)))) / (2 * math.pi * sigma1 * sigma2)
sum = sum + gaussian[i, j]
gaussian = gaussian / sum
plt.show()
"""
# 轉(zhuǎn)化為灰度值圖像
"""****************************** 一 讀取三通道圖 ****************************"""
img = cv2.imread(r'C:\Users\Administrator\Desktop\GUI2\fun\images\world.jpg', 1)
# img5 = cv2.imread('E:\\Lenna1.jpg',0)
"""***************************** 二 通道圖轉(zhuǎn)換成灰度圖 ***************************"""
def rgb2gray(rgb):
return np.dot(rgb[..., :3], [0.114, 0.587, 0.299])
gray = rgb2gray(img) # 灰度圖
W, H = gray.shape
"""***************************** 三 生成梯度圖 ***************************"""
new_gray = cv2.GaussianBlur(gray, (5, 5), 0) # 高斯模糊
# 求梯度幅值
W1, H1 = new_gray.shape
dx = np.zeros([W1 - 1, H1 - 1])
dy = np.zeros([W1 - 1, H1 - 1])
d = np.zeros([W1 - 1, H1 - 1]) # 圖像幅度值
# dx1 = np.zeros([W1 - 1, H1 - 1])
# dy1 = np.zeros([W1 - 1, H1 - 1])
# d1 = np.zeros([W1 - 1, H1 - 1])#圖像幅度值
for i in range(W1 - 1):
for j in range(H1 - 1):
dy[i, j] = new_gray[i + 1, j] - new_gray[i, j]
dx[i, j] = new_gray[i, j + 1] - new_gray[i, j]
d[i, j] = np.sqrt(np.square(dx[i, j]) + np.square(dy[i, j])) # 圖像梯度幅值作為圖像強度值
# dy1[i, j] = new_gray2[i + 1, j] - new_gray2[i, j]
# dx1[i, j] = new_gray2[i, j + 1] - new_gray2[i, j]
# d1[i, j] = np.sqrt(np.square(dx1[i, j]) + np.square(dy1[i, j])) # 圖像梯度幅值作為圖像強度值
"""***************************** 四 非極大值抑制 ***************************"""
W2, H2 = d.shape
NMS = np.copy(d)
NMS[0, :] = NMS[W2 - 1, :] = NMS[:, 0] = NMS[:, H2 - 1] = 0
for i in range(1, W2 - 1):
for j in range(1, H2 - 1):
if d[i, j] == 0:
NMS[i, j] = 0
else:
gradX = dx[i, j]
gradY = dy[i, j]
gradTemp = d[i, j]
# 如果Y方向幅度值較大
if np.abs(gradY) > np.abs(gradX):
weight = np.abs(gradX) / np.abs(gradY)
grad2 = d[i - 1, j]
grad4 = d[i + 1, j]
# 如果x,y方向梯度符號相同
if gradX * gradY > 0:
grad1 = d[i - 1, j - 1]
grad3 = d[i + 1, j + 1]
# 如果x,y方向梯度符號相反
else:
grad1 = d[i - 1, j + 1]
grad3 = d[i + 1, j - 1]
gradTemp1 = weight * grad1 + (1 - weight) * grad2
gradTemp2 = (1 - weight) * grad3 + weight * grad4
# 如果X方向幅度值較大
else:
weight = np.abs(gradY) / np.abs(gradX)
grad2 = d[i, j - 1]
grad4 = d[i, j + 1]
# 如果x,y方向梯度符號相同
if gradX * gradY > 0:
grad1 = d[i + 1, j - 1]
grad3 = d[i - 1, j + 1]
# 如果x,y方向梯度符號相反
else:
grad1 = d[i - 1, j - 1]
grad3 = d[i + 1, j + 1]
gradTemp1 = (1 - weight) * grad1 + weight * grad2
gradTemp2 = weight * grad3 + (1 - weight) * grad4
if gradTemp >= gradTemp1 and gradTemp >= gradTemp2:
NMS[i, j] = gradTemp
else:
NMS[i, j] = 0
"""***************************** 五 雙閾值算法檢測 ***************************"""
W3, H3 = NMS.shape
DT = np.zeros([W3, H3])
# 定義高低閾值
TL = 0.1 * np.max(NMS)
TH = 0.15 * np.max(NMS)
"""
當(dāng) “實際梯度 < 低閾值” 該點被錄取為“非邊緣點(背景點)
當(dāng) “實際梯度 > 高閾值” 該點被錄取為“邊緣點”
當(dāng)?shù)烷撝?lt; 實際梯度低< 高閾值 ” ,需要判別
如果周圍八鄰閾的點有大于高閾值的,凡梯度大者被錄取為邊緣點。
"""
for i in range(1, W3 - 1):
for j in range(1, H3 - 1):
if (NMS[i, j] < TL): # 小于低閾值背景點
DT[i, j] = 0
elif (NMS[i, j] > TH): # 大于高閾值邊緣點
DT[i, j] = 255
elif (np.any((NMS[i - 1, j - 1:j + 2] > TH)).any() or NMS[i, j - 1] > TH or NMS[
i, j + 1] > TH # 低閾值 < x < 高閾值 比較八鄰域
or np.any((NMS[i + 1, j - 1:j + 2] > TH))):
DT[i, j] = 255
cv2.imshow("gray", DT)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite("E:\\car_canny.jpg", DT)
上圖為輸入原圖,下圖為經(jīng)過Canny邊緣檢測后的圖
5.直方圖規(guī)定化
import numpy as np
import cv2
import bisect
# 計算累計概率分布Pr
def get_Sk(Hist):
sum_Hist = sum(Hist)
Pr = Hist / sum_Hist
# 計算累計概率Sk
Sk = []
temp = 0
for n in Pr:
sk = temp + n
Sk.append(sk)
temp = sk
Sk = np.asarray(Sk)
return Sk
# 映射表 sk1(原圖概率密度) sk2(目標(biāo)圖概率密度)
def get_lut(sk1, sk2):
index = np.zeros(256, dtype='uint8')
a = 0
for i in np.nditer(sk1):
subscript = bisect.bisect_right(sk2, i)
if subscript >= 256: # 防止二分查找出現(xiàn)256下標(biāo)越界
subscript = 255
if abs(sk2[subscript] - i) < abs(sk2[subscript - 1] - i):
index[a] = subscript
else:
index[a] = subscript - 1
a = a + 1
return index
"""************************************* START ******************************************"""
Src = cv2.imread(r'C:\Users\Administrator\Desktop\GUI2\fun\images\MV.jpg', 0) # 輸入圖
Dist = cv2.imread(r'C:\Users\Administrator\Desktop\GUI2\fun\images\car.jpg', 0) # 目標(biāo)圖
# 1.獲取原圖和目標(biāo)圖直方圖信息
SrcHist = cv2.calcHist([Src], [0], None, [256], [0, 255])
DistHist = cv2.calcHist([Dist], [0], None, [256], [0, 255])
# 2.計算原圖直方圖和目標(biāo)圖各灰度級累計概率密度
pr = get_Sk(SrcHist)
pz = get_Sk(DistHist)
# 3.生成 原圖直方圖累計概率密度 和目標(biāo)直方圖累計概率密度單映射表
SML = get_lut(pr, pz)
# 4.遍歷圖像各個灰度值,完成原圖直方圖到目標(biāo)直方圖類型的映射
h, w = Src.shape # 列 行
ImgOutput = Src.copy()
for i in range(0, h):
for j in range(0, w):
ImgOutput[i][j] = SML[ImgOutput[i][j]]
cv2.imshow("Src", Src)
cv2.imshow("Aim", Dist)
cv2.imshow("After", ImgOutput) # 規(guī)定花后的圖
cv2.waitKey()
上圖為原圖
中圖為規(guī)定目標(biāo)圖
下圖為原圖經(jīng)過規(guī)定化后的圖
6.PCA
import numpy as np
import cv2
path = r"C:\Users\Administrator\Desktop\GUI2\fun\images\MV.jpg"
# 讀入 灰度圖
img: np.ndarray = cv2.imread(path)
img = img[:, :, 0]
cv2.imshow("before", img)
# 預(yù)處理
h, w = img.shape
b = np.mean(img, axis=0)
STD = np.std(img, axis=0)
img = img - b
img = img / STD
img_T = img.transpose()
Mat = img_T.dot(img) # 得到 X * X_T
# 求 X * X_T 的前k大特征向量
eigvals, vecs = np.linalg.eig(Mat) # 注意求出的eigvals是特征值,vecs是標(biāo)準(zhǔn)化后的特征向量
indexs = np.argsort(eigvals)
indexs = indexs[::-1]
print(eigvals.shape)
# 編碼矩陣 D 是前k大特征向量組成的矩陣(正交矩陣)——topk_evecs
k = 60 #取前60大特征值
topk_evecs: np.ndarray = vecs[:, indexs[0:k]]
print(topk_evecs.shape)
# X * D = 維度壓縮后的圖片信息——encode (信息由 512 x 512 壓縮為 512 x 64)
encode = img.dot(topk_evecs)
# 譯碼矩陣即 D_T
img_new = ((encode.dot(topk_evecs.transpose()) * STD) + b).astype(np.uint8) # 譯碼得到的新圖片
# print(img_new)
img_new[img_new < 0] = 0
img_new[img_new > 255] = 255
cv2.imshow("after", img_new)
cv2.waitKey(0)
左圖為原輸入圖,右圖為經(jīng)過PCA后又還原回來的圖(還原回來后圖像信息有損失)
Mean_Shift(圖像選小一些,參數(shù)R給大一些,運行就很慢這玩意一般都是調(diào)庫,一般都是用C++實現(xiàn)的罕有Python實現(xiàn))
import numpy as np
import cv2 as cv
# 圖像預(yù)處理(放到數(shù)組中像素個數(shù)行5列的數(shù)組中)
def mean_shift(img):
rows, cols, channel = img.shape
rgb_array = np.zeros((rows * cols, 5))# (rows*cols)行 5列(RGBxy) 每一行保存一個像素點的信息
dstimg = np.zeros((rows, cols, channel))#原圖同大小同深度的空白圖像
k = 0
#遍歷圖像把每個像素點三通道RGB和坐標(biāo)xy放入數(shù)組
for i in range(0, rows):
for j in range(0, cols):
rgb_array[k][0], rgb_array[k][1], rgb_array[k][2], rgb_array[k][3], rgb_array[k][4] = img[i][j][0], img[i][j][1], img[i][j][2], i, j
k += 1
# 聚類的圓形半徑與收斂條件
r = 60# r是每次圓形聚類的半徑
convergence = 50#每次圓形聚類收斂的條件 (中心點代替此次聚類半徑中所有點)
temp_point = []#臨時存放中心點r半徑內(nèi)所有像素點信息
#初始化標(biāo)簽flag,用來控制是否初始化中心點坐標(biāo)
flag = False
while rgb_array.shape[0] != 0:
#在rows*cols個像素點中隨機找出一個作為初始化中心點
if flag == False:
index_row = np.random.randint(0, rgb_array.shape[0]) # 任意尋找一個點作為開始的點
mean_r = rgb_array[index_row][0]
mean_g = rgb_array[index_row][1]
mean_b = rgb_array[index_row][2]
mean_x = rgb_array[index_row][3]
mean_y = rgb_array[index_row][4]
#對每個像素點進行遍歷,找出在空間r內(nèi)的點,
#并將像素值記錄在temp_point中,下標(biāo)信息記錄在index中
index = []
for i in range(0, rgb_array.shape[0]):#按行遍歷
#(該點到中心點的距離)(像素點的五個信息全部參與運算這樣會更精確一些)
L = ((rgb_array[i][0] - mean_r) ** 2 + (rgb_array[i][1] - mean_g) ** 2 +(rgb_array[i][2] - mean_b) ** 2 + (rgb_array[i][3] - mean_x) ** 2 + (rgb_array[i][4] - mean_y) ** 2) ** 0.5
if L <= r:#該像素點在球半徑r內(nèi),距離<所定半徑
temp_point.append(rgb_array[i])#把符合條件的像素點信息與坐標(biāo)儲存起來
index.append(i)
#判斷半徑r內(nèi)是否有像素點
if len(temp_point) > 0:
element0, element1, element2,element3, element4 = 0, 0, 0, 0, 0
#求偏移距離
#步驟:step1:將所有在半徑內(nèi)的像素點求和
# step2:對求和向量均值化,得到需要移動到終點
# step3:對均值化的求和向量減去中心點向量的模得到偏移距離(判斷接下來是否進行漂移)
# step1 向量求和(半徑r范圍內(nèi)所有像素點點的五行一列的點向量求和)
for i in range(0, len(temp_point)):
element0 += temp_point[i][0]
element1 += temp_point[i][1]
element2 += temp_point[i][2]
element3 += temp_point[i][3]
element4 += temp_point[i][4]
# step2 求和向量均值化,得到需要移動到終點坐標(biāo)
element0 = element0 / len( temp_point)
element1 = element1 / len(temp_point)
element2 = element2 / len(temp_point)
element3 = element3 / len(temp_point)
element4 = element4 / len(temp_point)
# step2 終點坐標(biāo)減去中心點向量取距離得偏移距離
new_L = ((element0 - mean_r) ** 2 + (element1 - mean_g) ** 2 + (element2 - mean_b) ** 2 + (element3 - mean_x) ** 2 + (element4 - mean_y) ** 2) ** 0.5
#偏移距離超參
#小于convergence,停止漂移,并將原圖中所有半徑<r像素點用中心點去替代他們,并賦值給空白圖像dstimg
#大于convergence,繼續(xù)漂移中心點繼續(xù)更新
# step3 偏移距離小于等于convergence停止漂移
if new_L <= convergence:
for i in range(0, len(index)):
#返回原圖片對應(yīng)的行列,在新圖的相同位置使用終點位置值代替
row = int((temp_point[i])[3])
col = int((temp_point[i])[4])
dstimg[row][col][0] = element0
dstimg[row][col][1] = element1
dstimg[row][col][2] = element2
rgb_array = np.delete(rgb_array, index, 0)#按行刪除(index儲存的就是要刪除的每一行)
flag = False
#中心點更新
else:
mean_r = element0
mean_g = element1
mean_b = element2
mean_x = element3
mean_y = element4
flag = True
#清空上一輪r半徑的漂移,準(zhǔn)備下一輪
temp_point = []
#將dstimg像素值大小規(guī)定在 0-255范圍內(nèi)
dstimg = np.array(dstimg, np.uint8)
return dstimg
path7 = r'C:\Users\Administrator\Desktop\GUI2\fun\images\home.jpg'
src = cv.imread(path7)
#src2 = dist = cv.bilateralFilter(src, 0, 40, 15)
img = mean_shift(src)
#img2 = mean_shift(src2)
cv.imshow("SRC",src)
cv.imshow("Out",img)
#cv.imshow("Out2",img2)
cv.waitKey()
左圖為輸入圖像,右圖為輸入圖聚類后的圖像
文章來源:http://www.zghlxwxcb.cn/news/detail-734254.html
2.可視化界面
這里我就用阿里云盤了:某度網(wǎng)盤不開戶員,下載龜爬速度
函數(shù)可視化界面下載鏈接:GUI3
https://www.aliyundrive.com/s/8fyiWHb4Sny
這里是引用
運行下圖中的那個紅框的py文件就能出現(xiàn)下面這個界面。以上函數(shù)用到的測試圖像在下面包里的images文件夾下。
這里我還給這個可視化界面打個包文章來源地址http://www.zghlxwxcb.cn/news/detail-734254.html
到了這里,關(guān)于Python實現(xiàn):高斯濾波 均值濾波 中值濾波 Canny(邊緣檢測)PCA主成分分析 直方圖規(guī)定化 Mean_Shift的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!