Python实现:高斯滤波 均值滤波 中值滤波 Canny(边缘检测)PCA主成分分析 直方图规定化 Mean_Shift

Python实现:高斯滤波 均值滤波 中值滤波 Canny(边缘检测)PCA主成分分析 直方图规定化 Mean_Shift(文末附上整合这些函数的可视化界面并且已做打包处理)

1.高斯滤波(以下函数所有的图片路径为方便前来copy的同学,修改这里全设置为绝对路径,卷积核大小和其他参数按照自己需求改)

import cv2
import numpy as np
import math
SIZE = 3 # 卷积核大小(只能为奇数)
padding = SIZE // 2
sigma = 3

# 生成高斯卷积核(定卷积核中心坐标为(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大小的像素区域
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)  # 返回需要卷积的区域副本


"""***********************      一.读取图像                  ************************************"""
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内

"""***********************     三.卷积并修改原灰度图的像素值     ************************************"""
# 对img每个像素循环卷积修改img图像数组中的值
for x in range(h):
    for y in range(w):
        # 卷积(数组相乘)并修改原图像素
        img[x][y] = np.sum(GetMatrix(TmMask, x, y) * GaussKernel)

""" ***********************     四.高斯平滑后与原灰度图输出     ************************************"""
cv2.imshow("Gaussian", img)
cv2.imshow("SRC", img2)
cv2.waitKey()

左侧为原图右侧为高斯滤波后的图

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))  # 返回平均数(四舍五入)


"""***********************************************           START         ***********************************************************"""
# img = Image.open("E:\\a2.jpg").convert('L')#读取图片并转为灰度图
# img = Image.open(path).convert('L')#读取图片并转为灰度图
img = cv2.imread(path, 0)
img2 = cv2.imread(path, 0)
h, w = img.shape  # 行 列

# 生成一个带有padding的临时模板图像
TempArry = np.zeros([h + padding * 2, w + padding * 2])  # 生成一个和图像对应的加入padding的空白模板
TempArry[padding:h + padding, padding:w + padding] = img  # 把图像嵌入空白模板的padding内

# 循环对像素进行逐像素中值滤波
for x in range(0, h):
    for y in range(0, w):
        img[x, y] = Middle(TempArry, x, y, SIZE)  # 卷积后的中位数赋值给原灰度图像素

cv2.imshow("Mean", img)
cv2.imshow("SRC", img2)
cv2.waitKey()

左图为带有噪声的输入原图,右图为经过均值滤波后的图片

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)周围像素(也就是对应卷积区域)的像素放入列表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])  # 返回中位数


"""***********************************************           START         ***********************************************************"""
# img = Image.open("E:\\a2.jpg").convert('L')#读取图片并转为灰度图
# img = Image.open(path).convert('L')#读取图片并转为灰度图
img = cv2.imread(path, 0)
img2 = cv2.imread(path, 0)
h, w = img.shape  # 行 列

# 生成一个带有padding的临时模板图像
TempArry = np.zeros([h + padding * 2, w + padding * 2])  # 生成一个和图像对应的加入padding的空白模板
TempArry[padding:h + padding, padding:w + padding] = img  # 把图像嵌入空白模板的padding内

# 循环对像素进行逐像素中值滤波
for x in range(0, h):
    for y in range(0, w):
        img[x, y] = Middle(TempArry, x, y, SIZE)  # 卷积后的中位数赋值给原灰度图像素

cv2.imshow("Middle", img)
cv2.imshow("SRC", img2)
cv2.waitKey()

左图为带有噪声的输入原图,右图为经过中值滤波后的图

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()
"""

# 转化为灰度值图像
"""******************************      一 读取三通道图            ****************************"""
img = cv2.imread(r'C:\Users\Administrator\Desktop\GUI2\fun\images\world.jpg', 1)
# img5 =  cv2.imread('E:\\Lenna1.jpg',0)
"""*****************************        二 通道图转换成灰度图     ***************************"""


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)
"""
        当 “实际梯度 < 低阈值” 该点被录取为“非边缘点(背景点)
        当 “实际梯度 > 高阈值” 该点被录取为“边缘点”
        当低阈值< 实际梯度低< 高阈值 ”  ,需要判别
                                     如果周围八邻阈的点有大于高阈值的,凡梯度大者被录取为边缘点。
"""
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)

上图为输入原图,下图为经过Canny边缘检测后的图

5.直方图规定化

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(目标图概率密度)
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:  # 防止二分查找出现256下标越界
            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)  # 目标图

# 1.获取原图和目标图直方图信息
SrcHist = cv2.calcHist([Src], [0], None, [256], [0, 255])
DistHist = cv2.calcHist([Dist], [0], None, [256], [0, 255])

# 2.计算原图直方图和目标图各灰度级累计概率密度
pr = get_Sk(SrcHist)
pz = get_Sk(DistHist)

# 3.生成 原图直方图累计概率密度 和目标直方图累计概率密度单映射表
SML = get_lut(pr, pz)

# 4.遍历图像各个灰度值,完成原图直方图到目标直方图类型的映射
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)  # 规定花后的图
cv2.waitKey()

上图为原图
中图为规定目标图
下图为原图经过规定化后的图


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)

# 预处理
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是标准化后的特征向量
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)

左图为原输入图,右图为经过PCA后又还原回来的图(还原回来后图像信息有损失)

Mean_Shift(图像选小一些,参数R给大一些,运行就很慢这玩意一般都是调库,一般都是用C++实现的罕有Python实现)

import numpy as np
import cv2 as cv
# 图像预处理(放到数组中像素个数行5列的数组中)
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和坐标xy放入数组
    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半径内所有像素点信息
    #初始化标签flag,用来控制是否初始化中心点坐标
    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内的点,
        #并将像素值记录在temp_point中,下标信息记录在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内,距离<所定半径
                temp_point.append(rgb_array[i])#把符合条件的像素点信息与坐标储存起来
                index.append(i)

        #判断半径r内是否有像素点
        if len(temp_point) > 0:
            element0, element1, element2,element3, element4 = 0, 0, 0, 0, 0

            #求偏移距离
            #步骤:step1:将所有在半径内的像素点求和
            #     step2:对求和向量均值化,得到需要移动到终点
            #     step3:对均值化的求和向量减去中心点向量的模得到偏移距离(判断接下来是否进行漂移)

            # step1 向量求和(半径r范围内所有像素点点的五行一列的点向量求和)
            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 求和向量均值化,得到需要移动到终点坐标
            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 终点坐标减去中心点向量取距离得偏移距离
            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,继续漂移中心点继续更新

            # step3 偏移距离小于等于convergence停止漂移
            if new_L <= convergence:
                for i in range(0, len(index)):
                    #返回原图片对应的行列,在新图的相同位置使用终点位置值代替
                    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半径的漂移,准备下一轮
        temp_point = []

    #将dstimg像素值大小规定在 0-255范围内
    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()

左图为输入图像,右图为输入图聚类后的图像

2.可视化界面

这里我就用阿里云盘了:某度网盘不开户员,下载龟爬速度
函数可视化界面下载链接:GUI3
https://www.aliyundrive.com/s/8fyiWHb4Sny
这里是引用
运行下图中的那个红框的py文件就能出现下面这个界面。以上函数用到的测试图像在下面包里的images文件夹下。

这里我还给这个可视化界面打个包

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2023年11月14日
下一篇 2023年11月14日

相关推荐