传统机器学习算法解析(opencv实现)

news/2024/11/30 7:37:13/

前言

文本主要解析在传统机器学习当中一些小的算法与思想,只是传统机器学习算法当中的一小部分,更多传统机器学习算法可参考我的另外几篇博客
链接1: PCA主成分分析
链接2: Canny边缘检测算法
链接3: K-Means聚类算法
链接4: SIFT算法分析

1. opencv

  • OpenCV是一个开源的计算机视觉库,可以从 http://opencv.org 获取。
  • OpenCV 库用C语言和 C++ 语言编写,可以在 Windows、Linux、Mac OS X 等系统运行。同时也在积极开发 Python、Java、Matlab 以及其他一些语言的接口,将库导入安卓和 iOS 中为移动设备开发应用。
  • OpenCV 设计用于进行高效的计算,十分强调实时应用的开发。它由 C++ 语言编写并进行了深度优化,从而可以享受多线程处理的优势。
  • OpenCV 的一个目标是提供易于使用的计算机视觉接口,从而帮助人们快速建立精巧的视觉应用。
  • OpenCV 库包含从计算机视觉各个领域衍生出来的 500 多个函数,包括工业产品质量检验、医学图像处理、安保领域、交互操作、相机校正、双目视觉以及机器人学

因为opencv的底层实现是由C++写的,C++的最大优势在于高效,相同类型的·函数在不同的库当中运行速度是不一样的。

opencv大坑之BGR
opencv对于读进来的图片的通道排列是BGR,而不是主流的RGB!谨记!

#opencv读入的矩阵是BGR,如果想转为RGB,可以这么转
img4 = cv2.imread(‘1.jpg’)
img4 = cv2.cvtColor(img4,cv2.COLOR_BGR2RGB)

注意点

  1. 除了opencv读入的彩色图片以BGR顺序存储外,其他所有图像库读入彩色图片都以RGB存储。
  2. 除了PIL读入的图片是img类之外,其他库读进来的图片都是以numpy 矩阵。
  3. 各大图像库的性能,最好的OpenCv,无论是速度还是图片操作的全面性,都属于碾压的存在, 毕竟他是一个巨大的cv专用库。

下面是一组实验数据,一张3120*4160的图像,跑100次所花费的时间
在这里插入图片描述

2. 线性回归

什么是线性回归?
举个例子,某商品的利润在售价为2元、5元、10元时分别为4元、10元、20元, 我们很容易得出商品的利润与售价的关系符合直线:y=2x. 在上面这个简单的一元线性回归方程中,我们称“2”为回归系数,即斜率为其回归系数。 回归系数表示商品的售价(x)每变动一个单位,其利润(y)与之对应的变动关系。

在这里插入图片描述

线性回归表示这些离散的点总体上“最逼近”哪条直线。类似于均值的概念。

最小二乘法

  • 关键点在于最小化误差的平方和
  • 假设我们现在有一系列的数据点(xi,yi) (i=1,…,m),那么由我们给出的拟合函数h(x)得到的估计量就是h(xi)
  • 残差:ri = h(xi) – yi
  • 我们要追求的目标就是使得残差平方和最小

由此,我们可以写出最小二乘法的定义了:
在这里插入图片描述

这是一个无约束的最优化问题,分别对k和b求偏导,然后令偏导数为0,即可获得极值点。
在这里插入图片描述

3. RANSAC

  • 随机采样一致性
  • 注意,RANSAC是一种思想,一个求解已知模型的参数的框架。它不限定某一特定的问题,可以是计 算机视觉的问题,同样也可以是统计数学,甚至可以是经济学领域的模型参数估计问题。
  • 它是一种迭代的方法,用来在一组包含离群的被观测数据中估算出数学模型的参数。 RANSAC是一个非确定性算法,在某种意义上说,它会产生一个在一定概率下合理的结果,其允许使用更多次的迭代来使其概率增加。
  • RANSAC的基本假设是 “内群”数据可以通过几组模型参数来叙述其数据分布,而“离群”数据则是不适合模型化的数据。 数据会受噪声影响,噪声指的是离群,例如从极端的噪声或错误解释有关数据的测量或不正确的假设。 RANSAC假定,给定一组(通常很小的)内群,存在一个程序,这个程序可以估算最佳解释或最适用于这一数据模型的参数。

注意,RANSAC只是一种思想,是用来求已知模型参数的框架,对于已知的模型是没有限定的,它可以是任何的模型,但是是个模型就会有参数,比如y = ax + b。a,b分别等于多少才是RANSAC需要求解的问题,也就是说无论你是y = ax + b,还是y = kx + c,亦或是z = ax + by + c。RANSAC是不管的,你只要按着它的方法走,就能把参数求出来。比如说把大象放在冰箱需要3步,那么我们不管能不能放入,而只关心这三步的动作,因为此时的冰箱相当于一个黑盒。所以不管我们是把大象放进冰箱还是把飞机放进冰箱,RANSAC并不关心,RANSAC只关心这三步动作,而这三步动作不会因为物体的不同而不同。

RANSAC与最小二乘法

  • 生产实践中的数据往往会有一定的偏差。
  • 例如我们知道两个变量X与Y之间呈线性关系,Y=aX+b,我们想确定参数a与b的具体值。通过实验, 可以得到一组X与Y的测试值。虽然理论上两个未知数的方程只需要两组值即可确认,但由于系统误 差的原因,任意取两点算出的a与b的值都不尽相同。我们希望的是,最后计算得出的理论模型与测 试值的误差最小。
  • 最小二乘法:通过计算最小均方差关于参数a、b的偏导数为零时的值。事实上,很多情况下,最小 二乘法都是线性回归的代名词。
  • 遗憾的是,最小二乘法只适合于误差较小的情况。
  • 在模型确定以及最大迭代次数允许的情况下,RANSAC总是能找到最优解。(对于包含80%误差的数 据集,RANSAC的效果远优于直接的最小二乘法。)
  • 由于一张图片中像素点数量大,采用最小二乘法运算量大,计算速度慢。

在这里插入图片描述
观察上面那幅图,如果使用最小二乘法进行拟合,得到的会是红色的那条线,明显与预期效果有很大偏差。这是因为最小二乘法是对噪声点不敏感的,所以最小二乘法只适用于误差较小的情况,而我们利用RANSAC方法就能得到合理的解
RANSAC的步骤
RANSAC算法的输入:

  1. 一组观测数据(往往含有较大的噪声或无效点)
  2. 一个用于解释观测数据的参数化模型,比如 y=ax+b(即模型是已知的)
  3. 一些可信的参数
  1. 在数据中随机选择几个点设定为内群
  2. 计算适合内群的模型 e.g. y=ax+b ->y=2x+3 y=4x+5
  3. 把其它刚才没选到的点带入刚才建立的模型中,计算是否为内群 e.g. hi=2xi+3->ri
  4. 记下内群数量
  5. 重复以上步骤
  6. 比较哪次计算中内群数量最多,内群最多的那次所建的模型就是我们所要求的解

注意:不同问题对应的数学模型不同,因此在计算模型参数时方法必定不同,RANSAC的作用不在于 计算模型参数。(这导致ransac的缺点在于要求数学模型已知)
这里有几个问题:

  1. 一开始的时候我们要随机选择多少点(n)
  2. 以及要重复做多少次(k)

RANSAC的参数确定

  • 假设每个点是真正内群的概率为 w:
    w = 内群的数目/(内群数目+外群数目)
  • 通常我们不知道 w 是多少, w^n是所选择的n个点都是内群的机率, 1-w^n 是所选择的n个点至少有一个 不是内群的机率, (1 − wn)k 是表示重复 k 次都没有全部的n个点都是内群的机率, 假设算法跑 k 次以后 成功的机率是p,那么:
    1 − p = (1 − wn)k
    p = 1 − (1 − wn)k
  • 我们可以通过P反算得到抽取次数K,K=log(1-P)/log(1-w^n)。
  • 所以如果希望成功机率高:
  • 当n不变时,k越大,则p越大; 当w不变时,n越大,所需的k就越大。
  • 通常w未知,所以n 选小一点比较好。

RANSAC的优缺点
优点:

  1. 它能鲁棒的估计模型参数。例如,它能从包含大量局外点的数据集中估计出高精度的参数。

缺点:

  1. 它计算参数的迭代次数没有上限;如果设置迭代次数的上限,得到的结果可能不是最优的结果,甚 至可能得到错误的结果。
  2. RANSAC只有一定的概率得到可信的模型,概率与迭代次数成正比。
  3. 它要求设置跟问题相关的阀值。
  4. RANSAC只能从特定的数据集中估计出一个模型,如果存在两个(或多个)模型,RANSAC不能找到 别的模型。
  5. 要求数学模型已知

代码实现

import numpy as np
import scipy as sp
import scipy.linalg as sldef ransac(data, model, n, k, t, d, debug = False, return_all = False):"""输入:data - 样本点model - 假设模型:事先自己确定n - 生成模型所需的最少样本点k - 最大迭代次数t - 阈值:作为判断点满足模型的条件d - 拟合较好时,需要的样本点最少的个数,当做阈值看待输出:bestfit - 最优拟合解(返回nil,如果未找到)iterations = 0bestfit = nil #后面更新besterr = something really large #后期更新besterr = thiserrwhile iterations < k {maybeinliers = 从样本中随机选取n个,不一定全是局内点,甚至全部为局外点maybemodel = n个maybeinliers 拟合出来的可能符合要求的模型alsoinliers = emptyset #满足误差要求的样本点,开始置空for (每一个不是maybeinliers的样本点){if 满足maybemodel即error < t将点加入alsoinliers }if (alsoinliers样本点数目 > d) {%有了较好的模型,测试模型符合度bettermodel = 利用所有的maybeinliers 和 alsoinliers 重新生成更好的模型thiserr = 所有的maybeinliers 和 alsoinliers 样本点的误差度量if thiserr < besterr{bestfit = bettermodelbesterr = thiserr}}iterations++}return bestfit"""iterations = 0bestfit = Nonebesterr = np.inf #设置默认值best_inlier_idxs = Nonewhile iterations < k:maybe_idxs, test_idxs = random_partition(n, data.shape[0])print ('test_idxs = ', test_idxs)maybe_inliers = data[maybe_idxs, :] #获取size(maybe_idxs)行数据(Xi,Yi)test_points = data[test_idxs] #若干行(Xi,Yi)数据点maybemodel = model.fit(maybe_inliers) #拟合模型test_err = model.get_error(test_points, maybemodel) #计算误差:平方和最小print('test_err = ', test_err <t)also_idxs = test_idxs[test_err < t]print ('also_idxs = ', also_idxs)also_inliers = data[also_idxs,:]if debug:print ('test_err.min()',test_err.min())print ('test_err.max()',test_err.max())print ('numpy.mean(test_err)',numpy.mean(test_err))print ('iteration %d:len(alsoinliers) = %d' %(iterations, len(also_inliers)) )# if len(also_inliers > d):print('d = ', d)if (len(also_inliers) > d):betterdata = np.concatenate( (maybe_inliers, also_inliers) ) #样本连接bettermodel = model.fit(betterdata)better_errs = model.get_error(betterdata, bettermodel)thiserr = np.mean(better_errs) #平均误差作为新的误差if thiserr < besterr:bestfit = bettermodelbesterr = thiserrbest_inlier_idxs = np.concatenate( (maybe_idxs, also_idxs) ) #更新局内点,将新点加入iterations += 1if bestfit is None:raise ValueError("did't meet fit acceptance criteria")if return_all:return bestfit,{'inliers':best_inlier_idxs}else:return bestfitdef random_partition(n, n_data):"""return n random rows of data and the other len(data) - n rows"""all_idxs = np.arange(n_data) #获取n_data下标索引np.random.shuffle(all_idxs) #打乱下标索引idxs1 = all_idxs[:n]idxs2 = all_idxs[n:]return idxs1, idxs2class LinearLeastSquareModel:#最小二乘求线性解,用于RANSAC的输入模型    def __init__(self, input_columns, output_columns, debug = False):self.input_columns = input_columnsself.output_columns = output_columnsself.debug = debugdef fit(self, data):#np.vstack按垂直方向(行顺序)堆叠数组构成一个新的数组A = np.vstack( [data[:,i] for i in self.input_columns] ).T #第一列Xi-->行XiB = np.vstack( [data[:,i] for i in self.output_columns] ).T #第二列Yi-->行Yix, resids, rank, s = sl.lstsq(A, B) #residues:残差和return x #返回最小平方和向量   def get_error(self, data, model):A = np.vstack( [data[:,i] for i in self.input_columns] ).T #第一列Xi-->行XiB = np.vstack( [data[:,i] for i in self.output_columns] ).T #第二列Yi-->行YiB_fit = sp.dot(A, model) #计算的y值,B_fit = model.k*A + model.berr_per_point = np.sum( (B - B_fit) ** 2, axis = 1 ) #sum squared error per rowreturn err_per_pointdef test():#生成理想数据n_samples = 500 #样本个数n_inputs = 1 #输入变量个数n_outputs = 1 #输出变量个数A_exact = 20 * np.random.random((n_samples, n_inputs))#随机生成0-20之间的500个数据:行向量perfect_fit = 60 * np.random.normal( size = (n_inputs, n_outputs) ) #随机线性度,即随机生成一个斜率B_exact = sp.dot(A_exact, perfect_fit) # y = x * k#加入高斯噪声,最小二乘能很好的处理A_noisy = A_exact + np.random.normal( size = A_exact.shape ) #500 * 1行向量,代表XiB_noisy = B_exact + np.random.normal( size = B_exact.shape ) #500 * 1行向量,代表Yiif 1:#添加"局外点"n_outliers = 100all_idxs = np.arange( A_noisy.shape[0] ) #获取索引0-499np.random.shuffle(all_idxs) #将all_idxs打乱outlier_idxs = all_idxs[:n_outliers] #100个0-500的随机局外点A_noisy[outlier_idxs] = 20 * np.random.random( (n_outliers, n_inputs) ) #加入噪声和局外点的XiB_noisy[outlier_idxs] = 50 * np.random.normal( size = (n_outliers, n_outputs)) #加入噪声和局外点的Yi#setup model all_data = np.hstack( (A_noisy, B_noisy) ) #形式([Xi,Yi]....) shape:(500,2)500行2列input_columns = range(n_inputs)  #数组的第一列x:0output_columns = [n_inputs + i for i in range(n_outputs)] #数组最后一列y:1debug = Falsemodel = LinearLeastSquareModel(input_columns, output_columns, debug = debug) #类的实例化:用最小二乘生成已知模型linear_fit,resids,rank,s = sp.linalg.lstsq(all_data[:,input_columns], all_data[:,output_columns])#run RANSAC 算法ransac_fit, ransac_data = ransac(all_data, model, 50, 1000, 7e3, 300, debug = debug, return_all = True)if 1:import pylabsort_idxs = np.argsort(A_exact[:,0])A_col0_sorted = A_exact[sort_idxs] #秩为2的数组if 1:pylab.plot( A_noisy[:,0], B_noisy[:,0], 'k.', label = 'data' ) #散点图pylab.plot( A_noisy[ransac_data['inliers'], 0], B_noisy[ransac_data['inliers'], 0], 'bx', label = "RANSAC data" )else:pylab.plot( A_noisy[non_outlier_idxs,0], B_noisy[non_outlier_idxs,0], 'k.', label='noisy data' )pylab.plot( A_noisy[outlier_idxs,0], B_noisy[outlier_idxs,0], 'r.', label='outlier data' )pylab.plot( A_col0_sorted[:,0],np.dot(A_col0_sorted,ransac_fit)[:,0],label='RANSAC fit' )pylab.plot( A_col0_sorted[:,0],np.dot(A_col0_sorted,perfect_fit)[:,0],label='exact system' )pylab.plot( A_col0_sorted[:,0],np.dot(A_col0_sorted,linear_fit)[:,0],label='linear fit' )pylab.legend()pylab.show()if __name__ == "__main__":test()

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

4. 图像相似度比较哈希算法

相似图像搜索的哈希算法有三种:

  1. 均值哈希算法
  2. 差值哈希算法
  3. 感知哈希算法

什么是哈希(Hash)?

  • 散列函数(或散列算法,又称哈希函数,英语:Hash Function)是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值(hash values,hash codes,hash sums, 或hashes)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表。
  • 通过哈希算法得到的任意长度的二进制值映射为较短的固定长度的二进制值,即哈希值。此外, 哈希值是一段数据唯一且极其紧凑的数值表示形式,如果通过哈希一段明文得到哈希值,哪怕只 更改该段明文中的任意一个字母,随后得到的哈希值都将不同。
  • 哈希算法是一个函数,能够把几乎所有的数字文件都转换成一串由数字和字母构成的看似乱码的 字符串。

哈希函数作为一种加密函数,其拥有两个最重要特点:

  1. 不可逆性。输入信息得出输出的那个看似乱码的字符串(哈希值)非常容易,但是从输出的字符串反推出输入的结果却是却非常非常难
  2. 输出值唯一性和不可预测性。只要输入的信息有一点点区别,那么根据哈希算法得出来的输出值也相差甚远。

汉明距离
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。
在这里插入图片描述

均值哈希算法
步骤

  1. 缩放:图片缩放为8*8,保留结构,除去细节。
  2. 灰度化:转换为灰度图。
  3. 求平均值:计算灰度图所有像素的平均值。
  4. 比较:像素值大于平均值记作1,相反记作0,总共64位。
  5. 生成hash:将上述步骤生成的1和0按顺序组合起来既是图片的指纹(hash)
  6. 对比指纹:将两幅图的指纹对比,计算汉明距离,即两个64位的hash值有多少位是不一样的,不相同位数越少,图片越相似。

差值哈希算法
差值哈希算法相较于均值哈希算法,前期和后期基本相同,只有中间比较hash有变化。
步骤

  1. 缩放:图片缩放为8*9,保留结构,除去细节。
  2. 灰度化:转换为灰度图。
  3. 求平均值:计算灰度图所有像素的平均值。 —这步没有,只是为了与均值哈希做对比
  4. 比较:像素值大于后一个像素值记作1,相反记作0。本行不与下一行对比,每行9个像素, 八个差值,有8行,总共64位
  5. 生成hash:将上述步骤生成的1和0按顺序组合起来既是图片的指纹(hash)。
  6. 对比指纹:将两幅图的指纹对比,计算汉明距离,即两个64位的hash值有多少位是不一样 的,不相同位数越少,图片越相似。

感知哈希算法
均值哈希算法过于严格,不够精确,更适合搜索缩略图,为了获得更精确的结果可以选择感知哈希 算法,它采用的是DCT(离散余弦变换)来降低频率的方法。
步骤:

  1. 缩小图片:32 * 32是一个较好的大小,这样方便DCT计算
  2. 转化为灰度图:把缩放后的图片转化为灰度图。
  3. 计算DCT:DCT把图片分离成分率的集合
  4. 缩小DCT:DCT计算后的矩阵是32 * 32,保留左上角的8 * 8,这些代表图片的最低频率。
  5. 计算平均值:计算缩小DCT后的所有像素点的平均值。
  6. 进一步减小DCT:大于平均值记录为1,反之记录为0.
  7. 得到信息指纹:组合64个信息位,顺序随意保持一致性。
  8. 最后比对两张图片的指纹,获得汉明距离即可。

代码实现
均值哈希和差值哈希的实现:

import cv2
import numpy as np#均值哈希算法
def aHash(img):#缩放为8*8img=cv2.resize(img,(8,8),interpolation=cv2.INTER_CUBIC)#转换为灰度图gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#s为像素和初值为0,hash_str为hash值初值为''s=0hash_str=''#遍历累加求像素和for i in range(8):for j in range(8):s=s+gray[i,j]#求平均灰度avg=s/64#灰度大于平均值为1相反为0生成图片的hash值for i in range(8):for j in range(8):if  gray[i,j]>avg:hash_str=hash_str+'1'else:hash_str=hash_str+'0'            return hash_str#差值算法
def dHash(img):#缩放8*9img=cv2.resize(img,(9,8),interpolation=cv2.INTER_CUBIC)#转换灰度图gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)hash_str=''#每行前一个像素大于后一个像素为1,相反为0,生成哈希for i in range(8):for j in range(8):if   gray[i,j]>gray[i,j+1]:hash_str=hash_str+'1'else:hash_str=hash_str+'0'return hash_str#Hash值对比
def cmpHash(hash1,hash2):n=0#hash长度不同则返回-1代表传参出错if len(hash1)!=len(hash2):return -1#遍历判断for i in range(len(hash1)):#不相等则n计数+1,n最终为相似度if hash1[i]!=hash2[i]:n=n+1return nimg1=cv2.imread('lenna.png')
img2=cv2.imread('lenna_noise.png')
hash1= aHash(img1)
hash2= aHash(img2)
print(hash1)
print(hash2)
n=cmpHash(hash1,hash2)
print('均值哈希算法相似度:',n)hash1= dHash(img1)
hash2= dHash(img2)
print(hash1)
print(hash2)
n=cmpHash(hash1,hash2)
print('差值哈希算法相似度:',n)

在这里插入图片描述

三种算法的比较:

  • aHash:均值哈希。速度比较快,但是有时不太精确。
  • pHash:感知哈希。精确度较高,但是速度方面较差一些
  • dHash:差值哈希。精确度较高,且速度也非常快。

素材生成

import cv2 as cv
import numpy as np
from PIL import Image
import os.path as path
from PIL import ImageEnhancedef rotate(image):def rotate_bound(image, angle):# grab the dimensions of the image and then determine the# center(h, w) = image.shape[:2](cX, cY) = (w // 2, h // 2)# grab the rotation matrix (applying the negative of the# angle to rotate clockwise), then grab the sine and cosine# (i.e., the rotation components of the matrix)M = cv.getRotationMatrix2D((cX, cY), -angle, 1.0)cos = np.abs(M[0, 0])sin = np.abs(M[0, 1])# compute the new bounding dimensions of the imagenW = int((h * sin) + (w * cos))nH = int((h * cos) + (w * sin))# adjust the rotation matrix to take into account translationM[0, 2] += (nW / 2) - cXM[1, 2] += (nH / 2) - cY# perform the actual rotation and return the imagereturn cv.warpAffine(image, M, (nW, nH))return rotate_bound(image, 45)def enhance_color(image):enh_col = ImageEnhance.Color(image)color = 1.5return enh_col.enhance(color)def blur(image):# 模糊操作return cv.blur(image, (15, 1))def sharp(image):# 锐化操作kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32)return cv.filter2D(image, -1, kernel=kernel)def contrast(image):def contrast_brightness_image(src1, a, g):"""粗略的调节对比度和亮度:param src1: 图片:param a: 对比度:param g: 亮度:return:"""# 获取shape的数值,height和width、通道h, w, ch = src1.shape# 新建全零图片数组src2,将height和width,类型设置为原图片的通道类型(色素全为零,输出为全黑图片)src2 = np.zeros([h, w, ch], src1.dtype)# addWeighted函数说明如下return cv.addWeighted(src1, a, src2, 1 - a, g)return contrast_brightness_image(image, 1.2, 1)def resize(image):# 缩放图片return cv.resize(image, (0, 0), fx=1.25, fy=1)def light(image):# 修改图片的亮度return np.uint8(np.clip((1.3 * image + 10), 0, 255))def save_img(image, img_name, output_path=None):# 保存图片cv.imwrite(path.join(output_path, img_name), image, [int(cv.IMWRITE_JPEG_QUALITY), 70])passdef show_img(image):cv.imshow('image', image)cv.waitKey(0)passdef main():data_img_name = 'lenna.png'output_path = "./source"data_path = path.join(output_path, data_img_name)img = cv.imread(data_path)# 修改图片的亮度img_light = light(img)# 修改图片的大小img_resize = resize(img)# 修改图片的对比度img_contrast = contrast(img)# 锐化img_sharp = sharp(img)# 模糊img_blur = blur(img)# 色度增强img_color = enhance_color(Image.open(data_path))# 旋转img_rotate = rotate(img)img_rotate1 = Image.open(data_path).rotate(45)# 两张图片横向合并(便于对比显示)# tmp = np.hstack((img, img_rotate))save_img(img_light, "%s_light.jpg" % data_img_name.split(".")[0], output_path)save_img(img_resize, "%s_resize.jpg" % data_img_name.split(".")[0], output_path)save_img(img_contrast, "%s_contrast.jpg" % data_img_name.split(".")[0], output_path)save_img(img_sharp, "%s_sharp.jpg" % data_img_name.split(".")[0], output_path)save_img(img_blur, "%s_blur.jpg" % data_img_name.split(".")[0], output_path)# save_img(img_rotate, "%s_rotate.jpg" % data_img_name.split(".")[0], output_path)# 色度增强img_color.save(path.join(output_path, "%s_color.jpg" % data_img_name.split(".")[0]))img_rotate1.save(path.join(output_path, "%s_rotate.jpg" % data_img_name.split(".")[0]))show_img(img_rotate)passif __name__ == '__main__':main()

算法比较代码

import cv2
import numpy as np
import time
import os.path as pathdef aHash(img, width=8, high=8):"""均值哈希算法:param img: 图像数据:param width: 图像缩放的宽度:param high: 图像缩放的高度:return:感知哈希序列"""# 缩放为8*8img = cv2.resize(img, (width, high), interpolation=cv2.INTER_CUBIC)# 转换为灰度图gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# s为像素和初值为0,hash_str为hash值初值为''s = 0hash_str = ''# 遍历累加求像素和for i in range(8):for j in range(8):s = s + gray[i, j]# 求平均灰度avg = s / 64# 灰度大于平均值为1相反为0生成图片的hash值for i in range(8):for j in range(8):if gray[i, j] > avg:hash_str = hash_str + '1'else:hash_str = hash_str + '0'return hash_strdef dHash(img, width=9, high=8):"""差值感知算法:param img:图像数据:param width:图像缩放后的宽度:param high: 图像缩放后的高度:return:感知哈希序列"""# 缩放8*8img = cv2.resize(img, (width, high), interpolation=cv2.INTER_CUBIC)# 转换灰度图gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)hash_str = ''# 每行前一个像素大于后一个像素为1,反之置为0,生成感知哈希序列(string)for i in range(high):for j in range(high):if gray[i, j] > gray[i, j + 1]:hash_str = hash_str + '1'else:hash_str = hash_str + '0'return hash_strdef cmp_hash(hash1, hash2):"""Hash值对比:param hash1: 感知哈希序列1:param hash2: 感知哈希序列2:return: 返回相似度"""n = 0# hash长度不同则返回-1代表传参出错if len(hash1) != len(hash2):return -1# 遍历判断for i in range(len(hash1)):# 不相等则n计数+1,n最终为相似度if hash1[i] != hash2[i]:n = n + 1return 1 - n / len(hash2)def pHash(img_file, width=64, high=64):"""感知哈希算法:param img_file: 图像数据:param width: 图像缩放后的宽度:param high:图像缩放后的高度:return:图像感知哈希序列"""# 加载并调整图片为32x32灰度图片img = cv2.imread(img_file, 0)img = cv2.resize(img, (width, high), interpolation=cv2.INTER_CUBIC)# 创建二维列表h, w = img.shape[:2]vis0 = np.zeros((h, w), np.float32)vis0[:h, :w] = img  # 填充数据# 二维Dct变换vis1 = cv2.dct(cv2.dct(vis0))vis1.resize(32, 32)# 把二维list变成一维listimg_list = vis1.flatten()# 计算均值avg = sum(img_list) * 1. / len(img_list)avg_list = ['0' if i > avg else '1' for i in img_list]# 得到哈希值return ''.join(['%x' % int(''.join(avg_list[x:x + 4]), 2) for x in range(0, 32 * 32, 4)])def hamming_dist(s1, s2):return 1 - sum([ch1 != ch2 for ch1, ch2 in zip(s1, s2)]) * 1. / (32 * 32 / 4)def concat_info(type_str, score, time):temp = '%s相似度:%.2f %% -----time=%.4f ms' % (type_str, score * 100, time)print(temp)return tempdef test_diff_hash(img1_path, img2_path, loops=1000):img1 = cv2.imread(img1_path)img2 = cv2.imread(img2_path)start_time = time.time()for _ in range(loops):hash1 = dHash(img1)hash2 = dHash(img2)cmp_hash(hash1, hash2)print(">>> 执行%s次耗费的时间为%.4f s." % (loops, time.time() - start_time))def test_aHash(img1, img2):time1 = time.time()hash1 = aHash(img1)hash2 = aHash(img2)n = cmp_hash(hash1, hash2)return concat_info("均值哈希算法", n, time.time() - time1) + "\n"def test_dHash(img1, img2):time1 = time.time()hash1 = dHash(img1)hash2 = dHash(img2)n = cmp_hash(hash1, hash2)return concat_info("差值哈希算法", n, time.time() - time1) + "\n"def test_pHash(img1_path, img2_path):time1 = time.time()hash1 = pHash(img1_path)hash2 = pHash(img2_path)n = hamming_dist(hash1, hash2)return concat_info("感知哈希算法", n, time.time() - time1) + "\n"def deal(img1_path, img2_path):info = ''img1 = cv2.imread(img1_path)img2 = cv2.imread(img2_path)# 计算图像哈希相似度info = info + test_aHash(img1, img2)info = info + test_dHash(img1, img2)info = info + test_pHash(img1_path, img2_path)return infodef contact_path(file_name):output_path = "./source"return path.join(output_path, file_name)def main():data_img_name = 'lenna.png'data_img_name_base = data_img_name.split(".")[0]base = contact_path(data_img_name)light = contact_path("%s_light.jpg" % data_img_name_base)resize = contact_path("%s_resize.jpg" % data_img_name_base)contrast = contact_path("%s_contrast.jpg" % data_img_name_base)sharp = contact_path("%s_sharp.jpg" % data_img_name_base)blur = contact_path("%s_blur.jpg" % data_img_name_base)color = contact_path("%s_color.jpg" % data_img_name_base)rotate = contact_path("%s_rotate.jpg" % data_img_name_base)# 测试算法的效率test_diff_hash(base, base)test_diff_hash(base, light)test_diff_hash(base, resize)test_diff_hash(base, contrast)test_diff_hash(base, sharp)test_diff_hash(base, blur)test_diff_hash(base, color)test_diff_hash(base, rotate)# 测试算法的精度(以base和light为例)deal(base, light)if __name__ == '__main__':main()

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

拓展–离散余弦变换DCT

  • 离散余弦变换(Discrete Cosine Transform),主要用于将数据或图像的压缩,能够将空域的信号转换 到频域上,具有良好的去相关性的性能。
  • DCT变换本身是无损的,同时,由于DCT变换是对称的,所以,我们可以在量化编码后利用DCT反变 换,在接收端恢复原始的图像信息。
  • DCT变换在当前的图像分析以及压缩领域有着极为广大的用途,我们常见的JPEG静态图像编码以及 MJPEG、MPEG动态编码等标准中都使用了DCT变换。
    在这里插入图片描述
    其中,
  • F(u,v)是输出的变换结果;
  • N为原始信号的点数。
  • f(i,j)是原图像中像素点(i,j)的像素值;
  • c(u), c(v)是DCT系数。

http://www.ppmy.cn/news/400585.html

相关文章

简单易懂的装修流程图

1前期设计 ↓ 2主体拆改 ↓ 3水电改造 ↓ 4木工 ↓ 5贴砖 ↓ 6刷墙面漆 ↓ 7厨卫吊顶 ↓ 8橱柜安装 ↓ 9木门安装 ↓ 10地板安装 ↓ 11铺贴壁纸 ↓ 12散热器安装 ↓ 13开关插座安装 ↓ 14灯具安装 ↓ 15五金洁具安装 ↓ 16窗帘杆安装 ↓ 17拓荒保洁 ↓ 18家具进场 ↓ 19家电安装…

家里装修越来越好,一定不要忘记安装智能家居

智能家居这个产品现在已经进入到了千家万户&#xff0c;前往不要有感觉这种产品太时髦不实用的感觉。那是你没有了解过它的主观判断。 我们家里的装修&#xff0c;从最早先的刮腻子刷白墙&#xff0c;到后来的贴壁纸铺地板&#xff0c;再到现在的全屋实木整装&#xff0c;都在…

软装和硬装又有哪些不同?

所谓软装&#xff0c;是相对硬装而言的&#xff0c;硬装一般指的室内装修中固定的、不能移动的装饰物如吊顶、墙面造型、地面装饰、门窗工程等&#xff0c;软装是除掉硬装&#xff0c;进行家居的二度装饰&#xff0c;它是可以移动的、易于更换的饰物&#xff0c;如顶部灯具、墙…

墙面有几种装修方法_墙面有几种装修方法

墙面装饰的方法比较多&#xff0c;个人可以根据想要的风格和预算&#xff0c;挑选适合自家的装饰材料&#xff0c;不过也要注意装修细节&#xff0c;避免影响使用。那么墙面有几种装修方法&#xff0c;以及墙面装饰需要注意什么&#xff0c;各位是否了解呢。现在我们一起来看看…

墙面有几种装修方法_常见墙面装修方式有哪些 有几种装饰的方式

在装修过程中&#xff0c;除了地板之外&#xff0c;最重要的方面是墙壁&#xff0c;这是一个比较最大区域&#xff0c;并且对家居装修整体效果的影响也是最大的&#xff0c;因此&#xff0c;墙壁也成为许多消费者家居装饰的焦点&#xff0c;那么常见墙面装修方式有哪些&#xf…

墙面有几种装修方法_墙面怎么装?四种装修方式总有一款适合你

‍墙面作为家庭装修必须考虑的一部分&#xff0c;其装修的效果直接影响整个家的整体氛围&#xff0c;墙面就好比我们照相时的背景图&#xff0c;如果背景选择的不好&#xff0c;即使人长得再美&#xff0c;也会影响整个画面的效果&#xff0c;在家居中&#xff0c;若忽略墙面的…

集成墙面和瓷砖,装修到底该选谁?

快装集成墙板效果视频 瓷砖是家居装修中常用的装饰材料&#xff0c;具有防水、防腐、耐磨、装饰美观等优点。集成墙面是新型环保装饰材料&#xff0c;具有更加环保的性能&#xff0c;在防潮、保温、隔热等方面优势突出。在选择墙面装饰材料时&#xff0c;是选择传统的瓷砖还是新…

微信小店二次开发_怎么吧微信小店装修的更好看

A&#xff1a;1.现代简约风格 强调功能性设计&#xff0c;线条简约流畅&#xff0c;色彩对比强烈&#xff0c;这是现代风格家具的特点。此外&#xff0c;大量使用钢化玻璃、不锈钢等新型材料作为辅材&#xff0c;也是现代风格家具的常见装饰手法&#xff0c;能给人带来前卫、不…