某象差异点击

声明:本教程只能用于教学目的,如果用于非法目的与本人无关

视频详解地址:https://www.bilibili.com/video/BV1gysuewEKc/?vd_source=4e60c6c3075c23c4aef0f545b39459b8

教程素材网址:https://github.com/tomysky/diff_click

1.分析

在这里插入图片描述

​ 这个图案中是四个图标或者文字。文字可能的不同有颜色(镂空),角度,字体;图案不同。因为涉及到文字,用图像分类那收集数据,训练数据就太麻烦了。

2.解决办法

原理:

​ 每个图像的特征向量可以看作是该图像在高维特征空间中的一个点。如果一个图像与其他图像在视觉上有显著的不同,比如背景、颜色、结构等,那么它的特征向量会与其他图像的特征向量在高维空间中距离较远。这种“距离”用余弦相似度衡量时,会表现为较低的相似度。因此,通过这种方法可以找到与其他图像特征差异最大的那个,即“最不同”的图像。

​ 1.先用训练一个yolo模型,找到每个图形或者文字,截取目标图像,防止其他背景干扰

​ 2.使用ResNet50提取图像特征,结合余弦相似度分析,可以有效识别出一组图像中最不同的图像,因为这个图像的特征在高维空间中与其他图像的特征有最大的差异。

3.具体代码

#1.get_images.py

import time

from playwright.sync_api import sync_playwright
num = 0
with sync_playwright() as p:
    browser = p.firefox.launch(headless=True)
    page = browser.new_page()
    page.goto('')
    page.locator('xpath=/html/body/div[1]/div[2]/div/div[6]/div/div[2]/div[1]/div/ul/li[11]').click()
    time.sleep(2)
    page.locator('xpath=/html/body/div[1]/div[2]/div/div[6]/div/div[2]/div[2]/div[2]/ul/li[11]/div[2]/div/div[1]/div[3]').click()
    time.sleep(2)
    while True:
        try:
            for i in range(5):
                print("抓取抓取的图片数量:{}".format(str(num)))
                page.locator('xpath=//*[@id="dx_captcha_clickword_hits_4"]').screenshot(path="./images/{}.png".format(str(num)))
                #/html/body/div[11]/div/div[6]/div[2]/span[2]/img
                page.locator('xpath=/html/body/div[11]/div/div[6]/div[2]/span[2]/img').click()
                num += 1
                time.sleep(2)
            time.sleep(2)
            page.reload()
            time.sleep(4)
            page.locator('xpath=/html/body/div[1]/div[2]/div/div[6]/div/div[2]/div[1]/div/ul/li[11]').click()
            time.sleep(2)
            page.locator(
                'xpath=/html/body/div[1]/div[2]/div/div[6]/div/div[2]/div[2]/div[2]/ul/li[11]/div[2]/div/div[1]/div[3]').click()
        except Exception as e:
            print("err:", e)
            print("出错了,重新启动浏览器!")
            time.sleep(5)
            browser.close()
            browser = p.firefox.launch(headless=True)
            page = browser.new_page()
            page.goto('')
            page.locator('xpath=/html/body/div[1]/div[2]/div/div[6]/div/div[2]/div[1]/div/ul/li[11]').click()
            time.sleep(2)
            page.locator(
                'xpath=/html/body/div[1]/div[2]/div/div[6]/div/div[2]/div[2]/div[2]/ul/li[11]/div[2]/div/div[1]/div[3]').click()
            time.sleep(2)
# 2.json2txt.py
import json
import os

#输出目录
output_dir = './labels'
#输入目录
input_dir = './labels_json/'

# 标签到类别编号的映射
label_to_id = {
    'target': 0
}

def convert_labelme_to_yolo(json_file_path, output_dir, label_to_id):
    # 打开Labelme的JSON文件
    with open(json_file_path, 'r', encoding='utf-8') as f:
        data = json.load(f)

    # 获取图片的宽度和高度
    image_width = data['imageWidth']
    image_height = data['imageHeight']

    # 获取标注对象
    annotations = data['shapes']

    # 输出TXT文件的路径
    output_file_path = os.path.join(output_dir, os.path.splitext(os.path.basename(json_file_path))[0] + '.txt')

    with open(output_file_path, 'w', encoding='utf-8') as out_file:
        for annotation in annotations:
            label = annotation['label']
            points = annotation['points']

            # 计算标注框的中心点、宽度和高度
            x_min = min([p[0] for p in points])
            x_max = max([p[0] for p in points])
            y_min = min([p[1] for p in points])
            y_max = max([p[1] for p in points])

            x_center = (x_min + x_max) / 2 / image_width
            y_center = (y_min + y_max) / 2 / image_height
            width = (x_max - x_min) / image_width
            height = (y_max - y_min) / image_height

            # 获取类别编号
            class_id = label_to_id.get(label, -1)

            # 检查标签是否在映射中
            if class_id == -1:
                print(f"标签 '{label}' 未在映射中找到。")
                continue

            # 写入YOLO格式
            out_file.write(f"{class_id} {x_center} {y_center} {width} {height}\n")


def create_classes_file(output_dir, label_to_id):
    # classes.txt的路径
    classes_file_path = os.path.join(output_dir, 'classes.txt')

    # 根据label_to_id字典生成classes.txt
    with open(classes_file_path, 'w', encoding='utf-8') as f:
        for label in sorted(label_to_id, key=label_to_id.get):
            f.write(f"{label}\n")


def main():
    files = os.listdir(input_dir)
    for file in files:
        json_file_path = input_dir+file
        print(json_file_path)
        convert_labelme_to_yolo(json_file_path, output_dir, label_to_id)

    create_classes_file(output_dir, label_to_id)

if __name__ == '__main__':
    main()

#3.train.py

from ultralytics import YOLO

def train_model():
    # 加载YOLOv8模型
    model = YOLO('./models/yolov8m.pt')  # 你可以选择不同的预训练模型,如'yolov8n.pt', 'yolov8m.pt', 'yolov8l.pt', 'yolov8x.pt'

    # 开始训练
    model.train(
        data='./target.yaml',  # 数据集的配置文件
        epochs=1200,                        # 训练的轮数
        imgsz=640,                         # 输入图像的尺寸
        batch=16,                          # 批处理大小
        device='0',                        # 使用的GPU设备编号
        patience=1200
          )

    # 评估模型
    model.val()

    # 导出模型
    model.export(format='onnx')  # 你可以选择其他格式,如'onnx', 'coreml', 'tflite', 等等

if __name__ == '__main__':
    import multiprocessing
    multiprocessing.freeze_support()  # 确保在Windows系统上正确启动多进程
    train_model()
#4.detect_images.py
import os
from PIL import Image
import cv2
import torch
import torch.nn as nn
from torchvision import models, transforms
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from ultralytics import YOLO

# 加载预训练的ResNet50模型(去掉最后的全连接层)
resnet_model = models.resnet50(pretrained=True)
resnet_model = nn.Sequential(*list(resnet_model.children())[:-1])  # 去掉最后的全连接层,保留特征提取部分
resnet_model.eval()  # 设定为评估模式,不进行反向传播

# 图像预处理管道:调整大小、中心裁剪、转换为Tensor并标准化
preprocess = transforms.Compose([
    transforms.Resize(256),  # 调整图像大小
    transforms.CenterCrop(224),  # 裁剪图像到224x224的大小
    transforms.ToTensor(),  # 转换为Tensor
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),  # 标准化处理
])


def preprocess_image_cv2(img):
    """
    使用OpenCV对图像进行预处理,调整大小和中心裁剪。

    Args:
        img: 输入的OpenCV图像。

    Returns:
        处理后的图像Tensor。
    """
    # 调整图像大小到256x256
    img_resized = cv2.resize(img, (256, 256))
    # 中心裁剪到224x224
    img_cropped = img_resized[16:240, 16:240]

    # 检查图像是否为灰度图像,如果是,则转换为3通道
    if len(img_cropped.shape) == 2:  # 如果是灰度图像(只有height和width两个维度)
        img_cropped = cv2.cvtColor(img_cropped, cv2.COLOR_GRAY2BGR)

    # 将NumPy数组转换为PIL图像
    img_pil = Image.fromarray(cv2.cvtColor(img_cropped, cv2.COLOR_BGR2RGB))

    # 转换为Tensor并进行归一化处理
    img_tensor = preprocess(img_pil)
    img_tensor = img_tensor.unsqueeze(0)  # 添加批次维度
    return img_tensor


def extract_features_cv2(img):
    """
    提取图像的深度特征。

    Args:
        img: 输入的OpenCV图像。

    Returns:
        图像特征的NumPy数组。
    """
    # 预处理图像
    img_tensor = preprocess_image_cv2(img)
    # 提取特征
    with torch.no_grad():  # 关闭梯度计算,加快推理速度
        features = resnet_model(img_tensor)
    # 展平特征向量
    features = features.view(features.size(0), -1)
    return features.numpy()


def calc_diff(imgs):
    """
    计算一组图像之间的余弦相似度,并找出最不同的图像。

    Args:
        imgs: 包含多个OpenCV图像的列表。

    Returns:
        最不同图像的索引。
    """
    # 对每张图像提取特征
    features = [extract_features_cv2(img) for img in imgs]

    # 计算图像之间的余弦相似度矩阵
    similarity_matrix = cosine_similarity(np.array(features).squeeze())

    # 输出相似度矩阵
    # print("Similarity Matrix:\n", similarity_matrix)

    # 寻找相似度最低的图像
    min_similarity = float('inf')  # 初始化最小相似度为无穷大
    diff_image_index = -1  # 初始化最不同图像的索引

    for i in range(len(similarity_matrix)):
        # 计算图像与其他图像之间的平均相似度
        avg_similarity = np.mean([similarity_matrix[i][j] for j in range(len(similarity_matrix)) if i != j])
        # print(f"Image {i} Average Similarity with others: {avg_similarity:.4f}")
        if avg_similarity < min_similarity:  # 如果当前图像的平均相似度更小,则更新
            min_similarity = avg_similarity
            diff_image_index = i

    print(
        f"\nImage {diff_image_index} is the most different from the others with an average similarity of {min_similarity:.4f}.")
    return diff_image_index


def predict_and_draw_boxes(image_path, yolo_model):
    """
    使用YOLO模型预测图像中的目标,找到最不同的目标并绘制其位置。

    Args:
        image_path: 输入图像的路径。
        yolo_model: 预加载的YOLO模型。
    """
    # 读取原始图像
    image_origin = cv2.imread(image_path)
    # 转换为灰度图像
    image = cv2.cvtColor(image_origin, cv2.COLOR_BGR2GRAY)

    # 如果图像是灰度图像,转换为三通道
    if len(image.shape) == 2:  # 只有height和width两个维度
        image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)

    # 使用YOLO模型进行预测
    results = yolo_model(image)

    # 解析预测结果
    boxes = results[0].boxes.xyxy  # 获取所有预测框的坐标
    classes = results[0].boxes.cls  # 获取所有预测框的类别
    confidences = results[0].boxes.conf  # 获取所有预测框的置信度
    pos = []  # 存储目标的中心坐标
    imgs = []  # 存储裁剪的目标图像

    for index, box in enumerate(boxes):
        x1, y1, x2, y2 = map(int, box)  # 转换为整数坐标
        class_id = int(classes[index].item())  # 类别ID
        if class_id == 1:  # 如果类别为1,则跳过
            continue

        confidence = confidences[index].item()  # 获取置信度
        if confidence < 0.7:  # 置信度小于0.7则跳过
            continue

        # 计算目标中心点坐标
        centre_x = int((x1 + x2) // 2)
        centre_y = int((y1 + y2) // 2)
        # print("Centre point:", (centre_x, centre_y))
        pos.append((centre_x, centre_y))

        # 裁剪目标区域并保存
        cropped_img = image[y1:y2, x1:x2]
        imgs.append(cropped_img)

    if not imgs:  # 如果没有找到有效图像
        print("No valid images found.")
        return

    # 计算最不同的图像
    res_index = calc_diff(imgs)
    res_pos = pos[res_index]  # 最不同图像的位置
    print("Most different image position:", res_pos)

    # 在原始图像上绘制最不同目标的位置
    cv2.circle(image_origin, res_pos, 10, (0, 255, 0), 2)

    # 显示结果图像
    cv2.imshow('Prediction', image_origin)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


if __name__ == '__main__':
    model_path = 'models/best_s.pt'  # 替换为你的YOLO模型路径
    yolo_model = YOLO(model_path)  # 加载YOLO模型
    base_dir = "./images"  # 输入图像文件夹路径

    # 获取文件夹中所有图像文件并排序
    files = os.listdir(base_dir)
    files.sort(key=lambda x: int(os.path.splitext(x)[0]))  # 按文件名中的数字排序

    # 对每个图像文件进行预测和结果绘制
    for file in files:
        filename = os.path.join(base_dir, file)
        print(filename)
        predict_and_draw_boxes(filename, yolo_model)

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值