(第三原版)均值哈希-旋转图片验证码代码

我的目录是这样的

在这里插入图片描述

测试的源码

我知道的有两种种方式【相似图片-均值哈希】:

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())
        }
    }
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

迷心兔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值