我的目录是这样的
测试的源码
我知道的有两种种方式【相似图片-均值哈希】:
1.从网站拿到原图 - 进行每张旋转拿哈希值【主要麻烦是每张图片都要提前拿哈希值进行存储】
2.从网站拿到原图 - 每次判断角度时都要遍历原图拿到角度【主要是遍历图片的性能开销】
裁剪圆图这里不建议返回img_new进行哈希运算,试了会发现不是圆图(保存的是圆图)
%%time
# https://blog.csdn.net/san1156/article/details/76691841
import cv2
import os
import sys
import pickledb
import numpy as np
from PIL import Image
from functools import partial
from matplotlib import pyplot as plt
# 从文件反序列化,auto_dump 是每次写入操作都会写到文件,可以设置为False避免多次io
db = pickledb.load('./images/cv.db', False)
# 图像处理,获取图片最大内接圆,其他区域置为透明
def circle(img_path,show=False):
try:
img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED)
rows, cols = img.shape[:2]
# 创建一张4通道的新图片,包含透明通道,初始化是透明的
img_new = np.zeros((rows,cols,4), np.uint8)
img_new[:,:,0:3] = img[:,:,0:3]
# 创建一张单通道的图片,设置最大内接圆为不透明,注意圆心的坐标设置,cols是x坐标,rows是y坐标
img_circle = np.zeros((rows,cols,1),np.uint8)
img_circle[:,:,:] = 0 # 设置为全透明
# 设置最大内接圆为不透明
x,y,radius = rows/2,cols/2,int(min(rows, cols)/2)
img_circle = cv2.circle(
img=img_circle, # 画圆的图像
center=(int(x),int(y)), # 圆心
radius=radius-2, # 圆的半径
color=(255), # 圆圈颜色
thickness=-1,# 圆形轮廓的粗细(如果为正)。负值(如FILLED)表示要绘制一个实心圆。
lineType=cv2.LINE_AA,# 圆边界的类型
shift=0,
)
# 图片融合
img_new[:,:,3] = img_circle[:,:,0]
# 显示图片,调用matplotlib.pyplot展示
plt.subplot(121), plt.imshow(img_convert(img), cmap='gray'), plt.title('IMG')
plt.subplot(122), plt.imshow(img_convert(img_new), cmap='gray'), plt.title('IMG_NEW')
plt.show()
if show:
cv2.imwrite('./temp.png', img_new)
return True
else:
cv2.imwrite(r'./images/1/1.png', img_new)
return True
except Exception as e:
print(f"错误:{e}")
return False
# cv2与matplotlib的图像转换,cv2是bgr格式,matplotlib是rgb格式
def img_convert(cv2_img):
# 灰度图片直接返回
if len(cv2_img.shape) == 2:
return cv2_img
# 3通道的BGR图片
elif len(cv2_img.shape) == 3 and cv2_img.shape[2] == 3:
b, g, r = cv2.split(cv2_img)
return cv2.merge((r, g, b))
# 4通道的BGR图片
elif len(cv2_img.shape) == 3 and cv2_img.shape[2] == 4:
b, g, r, a = cv2.split(cv2_img)
return cv2.merge((r, g, b, a))
# 未知图片格式
else:
return cv2_img
def Get_hash(image_path='',im=None):
if im is None:
im = Image.open(image_path)
# im.show(image_path)
# 1.缩小尺寸。将图片缩小到8×8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。
# 2.简化色彩。将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。
im = im.resize((8, 8), Image.ANTIALIAS).convert('L')
# 3.计算平均值。计算所有64个像素的灰度平均值。
avg = sum(list(im.getdata())) / 64.0
# 4.比较像素的灰度。将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。
# 5.计算哈希值。将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。
str1 = ''.join(map(lambda i: '0' if i < avg else '1', im.getdata())) # 得到哈希字符串
# 6.将64位哈希值序列4个4个分割,转为十六进制。
str2 = ''.join(map(lambda x: '%x' % int(str1[x: x + 4], 2), range(0, 64, 4))) # %x:转换无符号十六进制
return str2
# 得到汉明距离
def Get_Hamming(str1, str2=''):
# 如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。
Hamming = 0
for i in range(16):
if str1[i] != str2[i]:
Hamming += 1
return Hamming
# 逆时针旋转
def rotate(angle,img):
height, width = img.shape[:2]
matRotation = cv2.getRotationMatrix2D((width / 2, height / 2), angle, 1)
imgRotation = cv2.warpAffine(img, matRotation, (width, height),)
cv2.imwrite(r'./images/1/%s.png'%(angle), imgRotation)
"""
print(f"开始训练--------------------------------------------------------------------------------------✨")
filename = '4'
# 1.创建存放模型图片的文件
path = os.path.join(os.getcwd(), 'images', '1')
if not os.path.exists(path):
os.mkdir(path)
# 2.创建模型圆形图案【算是第二版:解决图片边缘】
ok = circle(r'./images/%s-yuan.png'%filename)
pHash = Get_hash(r'./images/1/1.png')
print(f"临时哈希Key:{pHash}")
ok = circle(r'D:/jupyter_working/mi.png',True)
hash_str = Get_hash('D:/jupyter_working/temp.png')
similar = partial(Get_Hamming, str2=hash_str)
print(f"临时哈希Key:{hash_str}")
if ok:
dome = set() # 保存图片哈希Key
# 3.创建模型【解决图片旋转时失真】
# COLOR_GRAY2RGB:只读取彩色像素【解决了默认底色为黑并且图片不会失真】
img = cv2.imread(r'./images/1/1.png',cv2.COLOR_GRAY2RGB)
tick = cv2.TickMeter()
# 4.存储图片的均值哈希密文
with open("./images/%s.txt"%filename,'w+',encoding='utf-8') as f:
for angle in range(1,361):
tick.reset()
tick.start()
rotate(angle, img) # 旋转图片
info = Get_hash(r'./images/1/%s.png'%(angle))
dome.add(info)
# f.write(f"{info},{angle}\n") # 写入txt文件
db.set(str(info),angle)
tick.stop()
print(f'哈希:{info} \t度数:{angle} \t误差数:{similar(info)} \t{tick.getTimeMilli():.3f} ms')
# 序列化到文件【写入db文件】
db.dump()
print(f"360张图片去掉相似的数量:{len(dome)} 哈希键数:{len(db.getall())}")
print(f"db存储成功 ✨")
"""
print(f"开始测试---------------------------------------------------------------------------------------✨")
ok = circle(r'D:/jupyter_working/mi.png',True)
if ok:
hash_str = Get_hash('D:/jupyter_working/temp.png')
print(f"临时哈希Key:{hash_str}")
if db.exists(hash_str):
angle = db.get(hash_str)
print(f"\t✨哈希Key:{hash_str} 原图逆旋转的角度:{angle}")
else:
try:
# 以偏函数传递参数【hash:找出相似的位置】
similar = partial(Get_Hamming, str2=hash_str)
res= {i:similar(i) for i in db.getall() if similar(i) < 4}
print(f"\t✨哈希键数:{len(db.getall())} \n\t✨哈希值与误差数:{res}")
hash_str = min(res, key=res.get)
angle = db.get(hash_str)
print(f"\t✨哈希Key:{hash_str} \t原图逆旋转的角度:{angle}")
except Exception as e:
print(f"错误:{e}")
print('执行结束...')
cv2.waitKey(0)
cv2.destroyAllWindows()
开始测试---------------------------------------------------------------------------------------✨
临时哈希Key:004e6f1f0f476030
✨哈希键数:823
✨哈希值与误差数:{'006e2f1f0f476020': 3, '004e2f1f0f476020': 2, '004e2f1f0f476030': 1, '00ce6f1f0f476030': 1, '00ce6f3f0f476030': 2, '00ce6f3e0f476030': 3}
✨哈希Key:004e2f1f0f476030 原图逆旋转的角度:311
执行结束...
Wall time: 1.04 s
puppeteer 代码
console.log('Running...✨')
// 创建一个新的隐身浏览器上下文
const context = await browser.createIncognitoBrowserContext();
// 在上下文中创建一个新页面。
const page = await context.newPage();
appUrl = 'https://www.moyublog.com/code/5de692b9c93a4/index.html'
await page.goto(appUrl)
console.log(`测试开始. ✨`)
let errNum = 0
while (errNum < 100) {
try {
console.log(`1.点击测试按钮. ✨`)
await page.waitForSelector('body > .btn')
await page.click('body > .btn')
console.log(`2.获取旋转图片. ✨`)
let img_name = 'C:/Users/lenovo/Desktop/mi.png'
const iframe_element = await page.waitForSelector('#rotateWrap1 > div > div.rotate-can-wrap > canvas')
if (iframe_element) {
console.log('对象存在')
await iframe_element.screenshot({ 'path': img_name })
await page.waitForTimeout(200)
} else {
console.log('对象不存在')
await page.screenshot({ 'path': img_name })
}
console.log(`3.获取旋转的位移和角度. ✨`)
let datas = { 'name': 'moyublog', 'bg': img_name }
let data = await axios.post(
'http://127.0.0.1:6868/whirling', datas
).then(res => {
return res.data
}).catch(error => {
console.error(error)
})
console.log(data)
let angle = data['angle']
let move = data['move']
// let move = 160
console.log(`4.验证缺口位置. ✨`)
await page.waitForSelector('.box > #rotateWrap1 > .rotateverify-contaniner > .control-wrap > .control-btn')
let dragButton = await page.$('.box > #rotateWrap1 > .rotateverify-contaniner > .control-wrap > .control-btn')
let box = await dragButton.boundingBox();
// 获取滑动按钮中心点位置
let x = box.x + (box.width / 2);
let y = box.y + (box.height / 2);
// 鼠标滑动至滑动按钮中心点
await page.mouse.move(x, y);
// 按下鼠标
await page.mouse.down();
// 慢慢滑动至缺口位置,终点值为 x + move
for (let i = x; i <= x + move; i = i + 5) {
// 滑动鼠标
let steps = Math.ceil(Math.random() * 10)
await page.mouse.move(i, y, { 'steps': steps });
}
await page.mouse.move(x + move, y, { 'steps': 2 });
// 假装有个停顿,看起来更像是人为操作
await page.waitForTimeout(200);
// 放开鼠标
await page.mouse.up();
errNum++
await page.waitForTimeout(1000);
await page.goto(appUrl)
await page.reload(appUrl);
window.location.reload()
} catch (e) {
// errNum++
// console.log(await page.content())
}
}