文本主要解析在传统机器学习当中一些小的算法与思想,只是传统机器学习算法当中的一小部分,更多传统机器学习算法可参考我的另外几篇博客
链接1: PCA主成分分析
链接2: Canny边缘检测算法
链接3: K-Means聚类算法
链接4: SIFT算法分析
因为opencv的底层实现是由C++写的,C++的最大优势在于高效,相同类型的·函数在不同的库当中运行速度是不一样的。
opencv大坑之BGR
opencv对于读进来的图片的通道排列是BGR,而不是主流的RGB!谨记!
#opencv读入的矩阵是BGR,如果想转为RGB,可以这么转
img4 = cv2.imread(‘1.jpg’)
img4 = cv2.cvtColor(img4,cv2.COLOR_BGR2RGB)
注意点
- 除了opencv读入的彩色图片以BGR顺序存储外,其他所有图像库读入彩色图片都以RGB存储。
- 除了PIL读入的图片是img类之外,其他库读进来的图片都是以numpy 矩阵。
- 各大图像库的性能,最好的OpenCv,无论是速度还是图片操作的全面性,都属于碾压的存在, 毕竟他是一个巨大的cv专用库。
下面是一组实验数据,一张3120*4160的图像,跑100次所花费的时间
什么是线性回归?
举个例子,某商品的利润在售价为2元、5元、10元时分别为4元、10元、20元, 我们很容易得出商品的利润与售价的关系符合直线:y=2x. 在上面这个简单的一元线性回归方程中,我们称“2”为回归系数,即斜率为其回归系数。 回归系数表示商品的售价(x)每变动一个单位,其利润(y)与之对应的变动关系。
线性回归表示这些离散的点总体上“最逼近”哪条直线。类似于均值的概念。
最小二乘法
这是一个无约束的最优化问题,分别对k和b求偏导,然后令偏导数为0,即可获得极值点。
注意,RANSAC只是一种思想,是用来求已知模型参数的框架,对于已知的模型是没有限定的,它可以是任何的模型,但是是个模型就会有参数,比如y = ax + b。a,b分别等于多少才是RANSAC需要求解的问题,也就是说无论你是y = ax + b,还是y = kx + c,亦或是z = ax + by + c。RANSAC是不管的,你只要按着它的方法走,就能把参数求出来。比如说把大象放在冰箱需要3步,那么我们不管能不能放入,而只关心这三步的动作,因为此时的冰箱相当于一个黑盒。所以不管我们是把大象放进冰箱还是把飞机放进冰箱,RANSAC并不关心,RANSAC只关心这三步动作,而这三步动作不会因为物体的不同而不同。
RANSAC与最小二乘法
观察上面那幅图,如果使用最小二乘法进行拟合,得到的会是红色的那条线,明显与预期效果有很大偏差。这是因为最小二乘法是对噪声点不敏感的,所以最小二乘法只适用于误差较小的情况,而我们利用RANSAC方法就能得到合理的解
RANSAC的步骤
RANSAC算法的输入:
- 一组观测数据(往往含有较大的噪声或无效点)
- 一个用于解释观测数据的参数化模型,比如 y=ax+b(即模型是已知的)
- 一些可信的参数
- 在数据中随机选择几个点设定为内群
- 计算适合内群的模型 e.g. y=ax+b ->y=2x+3 y=4x+5
- 把其它刚才没选到的点带入刚才建立的模型中,计算是否为内群 e.g. hi=2xi+3->ri
- 记下内群数量
- 重复以上步骤
- 比较哪次计算中内群数量最多,内群最多的那次所建的模型就是我们所要求的解
注意:不同问题对应的数学模型不同,因此在计算模型参数时方法必定不同,RANSAC的作用不在于 计算模型参数。(这导致ransac的缺点在于要求数学模型已知)
这里有几个问题:
- 一开始的时候我们要随机选择多少点(n)
- 以及要重复做多少次(k)
RANSAC的参数确定
RANSAC的优缺点
优点:
缺点:
代码实现
import numpy as np
import scipy as sp
import scipy.linalg as sl
def ransac(data, model, n, k, t, d, debug = False, return_all = False):
"""
输入:
data - 样本点
model - 假设模型:事先自己确定
n - 生成模型所需的最少样本点
k - 最大迭代次数
t - 阈值:作为判断点满足模型的条件
d - 拟合较好时,需要的样本点最少的个数,当做阈值看待
输出:
bestfit - 最优拟合解(返回nil,如果未找到)
iterations = 0
bestfit = nil #后面更新
besterr = something really large #后期更新besterr = thiserr
while 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 = bettermodel
besterr = thiserr
}
}
iterations++
}
return bestfit
"""
iterations = 0
bestfit = None
besterr = np.inf #设置默认值
best_inlier_idxs = None
while 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 = bettermodel
besterr = thiserr
best_inlier_idxs = np.concatenate( (maybe_idxs, also_idxs) ) #更新局内点,将新点加入
iterations += 1
if bestfit is None:
raise ValueError("did't meet fit acceptance criteria")
if return_all:
return bestfit,{'inliers':best_inlier_idxs}
else:
return bestfit
def 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, idxs2
class LinearLeastSquareModel:
#最小二乘求线性解,用于RANSAC的输入模型
def __init__(self, input_columns, output_columns, debug = False):
self.input_columns = input_columns
self.output_columns = output_columns
self.debug = debug
def fit(self, data):
#np.vstack按垂直方向(行顺序)堆叠数组构成一个新的数组
A = np.vstack( [data[:,i] for i in self.input_columns] ).T #第一列Xi-->行Xi
B = np.vstack( [data[:,i] for i in self.output_columns] ).T #第二列Yi-->行Yi
x, 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-->行Xi
B = np.vstack( [data[:,i] for i in self.output_columns] ).T #第二列Yi-->行Yi
B_fit = sp.dot(A, model) #计算的y值,B_fit = model.k*A + model.b
err_per_point = np.sum( (B - B_fit) ** 2, axis = 1 ) #sum squared error per row
return err_per_point
def 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行向量,代表Xi
B_noisy = B_exact + np.random.normal( size = B_exact.shape ) #500 * 1行向量,代表Yi
if 1:
#添加"局外点"
n_outliers = 100
all_idxs = np.arange( A_noisy.shape[0] ) #获取索引0-499
np.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) ) #加入噪声和局外点的Xi
B_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:0
output_columns = [n_inputs + i for i in range(n_outputs)] #数组最后一列y:1
debug = False
model = 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 pylab
sort_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()
相似图像搜索的哈希算法有三种:
什么是哈希(Hash)?
- 散列函数(或散列算法,又称哈希函数,英语:Hash Function)是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值(hash values,hash codes,hash sums, 或hashes)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表。
- 通过哈希算法得到的任意长度的二进制值映射为较短的固定长度的二进制值,即哈希值。此外, 哈希值是一段数据唯一且极其紧凑的数值表示形式,如果通过哈希一段明文得到哈希值,哪怕只 更改该段明文中的任意一个字母,随后得到的哈希值都将不同。
- 哈希算法是一个函数,能够把几乎所有的数字文件都转换成一串由数字和字母构成的看似乱码的 字符串。
哈希函数作为一种加密函数,其拥有两个最重要特点:
- 不可逆性。输入信息得出输出的那个看似乱码的字符串(哈希值)非常容易,但是从输出的字符串反推出输入的结果却是却非常非常难
- 输出值唯一性和不可预测性。只要输入的信息有一点点区别,那么根据哈希算法得出来的输出值也相差甚远。
汉明距离
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。
均值哈希算法
步骤
差值哈希算法
差值哈希算法相较于均值哈希算法,前期和后期基本相同,只有中间比较hash有变化。
步骤
感知哈希算法
均值哈希算法过于严格,不够精确,更适合搜索缩略图,为了获得更精确的结果可以选择感知哈希 算法,它采用的是DCT(离散余弦变换)来降低频率的方法。
步骤:
代码实现
均值哈希和差值哈希的实现:
import cv2
import numpy as np
#均值哈希算法
def aHash(img):
#缩放为8*8
img=cv2.resize(img,(8,8),interpolation=cv2.INTER_CUBIC)
#转换为灰度图
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#s为像素和初值为0,hash_str为hash值初值为''
s=0
hash_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*9
img=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+1
return n
img1=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)
三种算法的比较:
素材生成
import cv2 as cv
import numpy as np
from PIL import Image
import os.path as path
from PIL import ImageEnhance
def 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 image
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
# adjust the rotation matrix to take into account translation
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
# perform the actual rotation and return the image
return cv.warpAffine(image, M, (nW, nH))
return rotate_bound(image, 45)
def enhance_color(image):
enh_col = ImageEnhance.Color(image)
color = 1.5
return 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])
pass
def show_img(image):
cv.imshow('image', image)
cv.waitKey(0)
pass
def 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)
pass
if __name__ == '__main__':
main()
算法比较代码
import cv2
import numpy as np
import time
import os.path as path
def aHash(img, width=8, high=8):
"""
均值哈希算法
:param img: 图像数据
:param width: 图像缩放的宽度
:param high: 图像缩放的高度
:return:感知哈希序列
"""
# 缩放为8*8
img = cv2.resize(img, (width, high), interpolation=cv2.INTER_CUBIC)
# 转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# s为像素和初值为0,hash_str为hash值初值为''
s = 0
hash_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, width=9, high=8):
"""
差值感知算法
:param img:图像数据
:param width:图像缩放后的宽度
:param high: 图像缩放后的高度
:return:感知哈希序列
"""
# 缩放8*8
img = 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_str
def 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 + 1
return 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变成一维list
img_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 temp
def 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 info
def 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