本文章所有内容仅供学习和研究使用,不提供具体模型和源码。若有侵权,请联系我立即删除

某盾空间推理点选训练

开始

整体思路

  1. 目标检测:识别图片中小图的坐标

  2. 切割图片:根据坐标切割小图

  3. 分类训练:识别每个小图的方向颜色朝向

1、样本准备

  1. 批量存储对应图片(200-300)

  2. 图片存于 image 文件中,提示文本换行存在 text.txt

图片爬去过程省略,主要记录点选训练思路,不涉及逆向。也可以用自动化获取样本

2、分词分类

对抓去的txt.txt 进行语义分析统计

tools.py 分词,统计词频,删除无用词
def split_jieba():
    # 1. 读取分词
    prompt_path = "text.txt"
    prompt_list = []
    with open(prompt_path, 'r', encoding = 'utf-8') as f:
        for line in f.readlines():
            print(line.strip().replace('请点击', ''))
            prompt_list.append(line.strip().replace('请点击', ''))

    # 2.分词,统计词频
    word_dict = {}
    for line in prompt_list:
        words = jieba.cut(line)  # 分词
        for word in words:
            if word in word_dict.keys():
                word_dict[word] += 1
            else:
                word_dict[word] = 1
    print(word_dict)

    # 删除掉无用词
    delete_list = ["的", "大写", "小写", "一样", "朝向", "数字", "颜色"]
    for word in delete_list:
        del word_dict[word]

    # 排序
    word_dict = sorted(word_dict.items(), key = lambda x:x[0], reverse = False)
    print(word_dict)

2、分析分类

# 大写字母
upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
# 小写字母
lower = "abcdefghijklmnopqrstuvwxyz"
# 数字
number = "0123456789"
# 三维物体
three = ["圆柱", "圆锥", "球", "立方体"]


# 颜色
color = ["红色", "绿色", "黄色", "蓝色", "灰色"]


# 朝向
orientation = ["侧向", "正向"]

经过分类,观察一共3个维度。

大小写数字物体(下文统称【名称分类】) 4种为一个分类,颜色为一个分类,朝向为一个

很多类似的字母无法区分,全部视为大写,后期逻辑统一处理,比如:V v Ww Xx等...

小写字母(26)

大写(26)

数字(10)

物体(4)

66

颜色

5

朝向

2

660

当我们增加分类时,就意味着我们需要增加打标签的样本!借鉴吾爱大佬[1] 的一个思路,我们可以将图片的颜色剥离,然后根据名称分类和朝向来进行132种分类。颜色部分则单独训练。这样做的好处是显而易见的,分类的数量减少,意味着我们打标签的成本大大降低。但本文在这个思路基础上将三个维度分别进行训练,具体思路如下:

tools.py 分类代码 生成classes.txt
def combine_prompt():
    # 大写字母
    upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    # 小写字母
    lower = "abcdefghijklmnopqrstuvwxyz"
    # 数字
    number = "0123456789"
    # 三维物体
    three = ["圆柱", "圆锥", "球", "立方体"]
    # 颜色
    color = ["红色", "绿色", "黄色", "蓝色", "灰色"]
    # 朝向
    orientation = ["侧向", "正向"]

    # 生成所有结果组合
    result = []

    # upper、lower、number、three为同类型,只能选一个,格式 [upper, lower, number, three]_color_orientation
    for a in [upper, lower, number, three]:
        for b in a:
            for d in orientation:
                # for c in color:
                #     result.append(f'{b}_{c}_{d}')
                result.append(f'{b}_{d}')

    print(len(result))
    print(result)
    # 写入文件
    with open("./classes.txt", "w", encoding="utf-8") as f:
        for line in result:
            f.write(line + "\n")

3、训练思路

  1. 标注大约100到200张图片,训练一个正向和侧向的模型。

  2. 标注大约100到200张图片,训练一个颜色模型。

  3. 利用上述模型定位并切割出小图,训练一个分类模型。

这样每张图片经过这三个模型处理后就能得到结果。同时,我们也可以再拿一批新的图片经过这三个模型,并自动生成方向、颜色、分类的样本,然后再训练一个综合模型。这样一来,我们就可以更有效地处理和分类大量的图片数据。

我标记了200张 标记两个维度 名称分类_朝向 有不同思路的欢迎留言交流

4、打标训练样本

详细情看另外一篇文章 labelimg标注工具,不过多解释。

使用 labelimg 进行打标

pip install labelimg

# images 达标图片目录  classes.txt 类别
labelimg ./images ./classes.txt

5、切割图片

import cv2
from PIL import ImageFont

class Crop(object):
    def __init__(self, bigImgPath: str, save_paths: str):
        self.save_paths = save_paths
        self.image = cv2.imread(bigImgPath)

    def crop_image_from_coordinates(self, coordinatesList: list):
		# coordinatesList 打标后面的那个左边
        result = []
        for ind, coordinates in enumerate(coordinatesList):
            data = {}
            x1, y1, x2, y2 = coordinates[:4]
            x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
            cropped_image = self.image[y1:y2, x1:x2]  # 裁切的小图
            save_paths = fr"{self.save_paths}\{ind}.jpg"
            print(save_paths)
            cv2.imwrite(save_paths, cropped_image)
 
            data['xyxy'] = x1, y1, x2, y2
            data['savePath'] = save_paths
            result.append(data)
        return result

6、分类训练(YOLO v8)

train.py 对切割的颜色,名称分类,朝向进行训练

from ultralytics import YOLO

model = YOLO('yolov8n-cls.pt', task='classify') # 分类训练

results = model.train(data='[切割图片路径]', epochs=50, imgsz=320, workers=0, cache=True, device='0')

三个维度训练结果

val_batch2_labels.jpg

val.py 验证代码

from ultralytics import YOLO

model = YOLO('[训练的模型]', task='classify')

results = model.predict(source=r'[预测路径,或图片]', show=True, save=True, imgsz=320, device='0')
for result in results:
    cls_dict = result[0].names
    cls_id = result.probs.top1
    cls_result = cls_dict[cls_id]
    print(f"当前图片的结果是:{'侧' if cls_result == 'ce' else '正'}")

7、目标检测

我们可以利用前面3个模型的任意一个模型做目标检测

train2.py 目标检测

import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"

from ultralytics import YOLO


model = YOLO('yolov8n.pt', task='detect')

if __name__ == '__main__':
    # [train, val, predict, export, track, benchmark] 训练、验证、预测、模型转换、追踪、基准模型评估
    model.train(data=fr'./slider.yml', epochs=100, cache=True, imgsz=320, batch=16, workers=0, device='0')
    """
    data 数据集来源配置
    epochs 训练论数v
    cache 
    imgsz ai训练图片的size,大图片建议640 小320 越大越吃性能
    task 【detect、segment、classify、pose】 四种任务类型 检测、分割、分类、姿态
    """

slider.yml 文件 配置文件

path: D:\yfl_50\box_200
train: ./images/train
val: ./images/val
test: # TEST IMAGES (OPTIONAL)

nc: 1
names: ["box"]

val2.py 进行模型检测,返回坐标值

from ultralytics import YOLO

model = YOLO(r"best_mu.pt", task="detect")   # build a new model from scratch
results = model.predict(source=r"D:\yfl_50\img\00abb37393c443fec2865ddfef64fbf2.jpg", show=False,save=False)
# 输出坐标信息
print(f"标签名字:{results[0].names}, \n目标类:{ results[0].boxes.cls.tolist()}")

print(results)

cls_dict = results[0].names
cls_all = results[0].boxes.cls.tolist()
xyxy_all = results[0].boxes.xyxy.tolist()
for i in range(len(cls_all)):
    label_name = cls_dict[int(cls_all[i])]
    box_xyxy = xyxy_all[i]
    box_mid_xy = [(box_xyxy[0] + box_xyxy[2])/2, (box_xyxy[1] + box_xyxy[3])/2]
    print(F"目标点{i} :标签名字:{label_name}, 中心坐标:{box_mid_xy}, xyxy坐标: {box_xyxy}")

目标检测结果

8、三个模型合并

过程很简单就是一些逻辑,不在啰嗦了....

下面是一下结果识别图

9、语义逻辑分析

暂时还没有写,尽快更新

10、其他

提过一些我用到的代码

tools.py 自动分类

def train_val_mkdir(fileDir):
    """
    自动分类 创建 train val文件,按比例分类
    :param fileDir: 数据集的Path
    :return: None
    """
    TRAIN = 8
    count = len(os.listdir(fileDir))
    count = count // 2

    train_count = int(count * (TRAIN * 0.1))
    val_count = count - train_count
    print(f'当前图片总数为:{train_count}张 | 比例{TRAIN}:{10 - TRAIN} | train【{train_count}】张 val【{val_count}】张')

    cou = 0
    for file in os.listdir(fileDir):
        if file == "classes.txt":
            continue
        if file.split('.')[-1] != 'txt':
            continue
        if cou < train_count:
            fileName = 'train'
        else:
            fileName = 'val'
        print(f'总体比例是train:val{TRAIN}:{10 - TRAIN}|正在处理第{cou}个文件|当前分类为:{fileName}| {file}')
        images_path = fr'{fileDir}/images/{fileName}'
        if not os.path.exists(images_path):
            os.makedirs(images_path)

        labels_path = fr'{fileDir}\labels\{fileName}'
        if not os.path.exists(labels_path):
            os.makedirs(labels_path)

        name = file.split('.')[0]
        txtPath = fr'{fileDir}\{name}.txt'
        imgPath = fr'{fileDir}\{name}.jpg'

        shutil.move(txtPath, labels_path)
        shutil.move(imgPath, images_path)
        cou += 1

小图合并

def add_image_to_background(resultList):
    # 创建一个白色的320x160图片
    background = np.ones((160, 320, 3), dtype=np.uint8) * 255
    # 加载中文字体文件
    font_path = r'D:\yfl_50\simsunb.ttf'
    # 字体的格式
    fontStyle = ImageFont.truetype(font_path, 15, encoding="utf-8")

    for coordinates in resultList:
        print(coordinates)
        # 读取小图
        small_image = cv2.imread(coordinates['savePath'])
        # 获取坐标
        x1, y1, x2, y2 = coordinates['xyxy']
        # 将小图放置在大图上
        background[y1:y2, x1:x2] = small_image
        # 在大图上绘制红色框和标注信息
        result = coordinates['result']
        cv2.rectangle(background, (x1, y1), (x2, y2), (255, 0, 255), 2)
        cv2.putText(background, result, (x1, y1 - 10), cv2.FONT_HERSHEY_COMPLEX , 0.5, (0, 0, 255), 1)
        # print(result)
        # background = cv2AddChineseText(background, result, (x1, y1 - 10), (0, 0, 255), 1)
        # print(background)


    # 保存大图
    cv2.imwrite(r'result.jpg', background)

坐标转换

# 将四个相对位置的坐标转换为 LabelImg 标注工具输出的格
def convert_to_center_width_height(x1, y1, x2, y2, width=320, height=160):
    # 计算中心坐标
    center_x = (x1 + x2) / 2
    center_y = (y1 + y2) / 2

    # 计算宽度和高度
    box_width = x2 - x1
    box_height = y2 - y1

    # 归一化处理
    norm_center_x = center_x / width
    norm_center_y = center_y / height
    norm_box_width = box_width / width
    norm_box_height = box_height / height

    return norm_center_x, norm_center_y, norm_box_width, norm_box_height

绘制边框

from PIL import Image, ImageDraw, ImageFont

# 打开图片
img = Image.open(fname)
draw = ImageDraw.Draw(img)

for i in range(len(cls_all)):
    label_name = cls_dict[int(cls_all[i])]
    box_xyxy = xyxy_all[i]
    box_mid_xy = [(box_xyxy[0] + box_xyxy[2])/2, (box_xyxy[1] + box_xyxy[3])/2]
    print(F"目标点{i} :标签名字:{label_name}, 中心坐标:{box_mid_xy}, xyxy坐标: {box_xyxy}")

    coordinates = [float(i) for i in box_xyxy]

    # 提取坐标信息
    x1, y1, x2, y2 = coordinates

    # 绘制边框
    draw.rectangle([x1, y1, x2, y2], outline='red', width=3)

    # 设置字体和字体大小
    font = ImageFont.truetype("STKAITI.TTF", 13)

    # 计算文本位置
    text_width, text_height = draw.textsize(label_name, font)
    # text_x = x1 + (x2 - x1 - text_width) // 2
    # text_y = y1 + (y2 - y1 - text_height) // 2

    # 绘制文本和背景
    # draw.rectangle([x1 - 17, y1 - 15, x1 + text_width - 12, y1 + text_height - 12], fill='black')
    draw.text((x1 - 15, y1 - 15), label_name, fill='black', font=font)

参考文章和鸣谢

[1]: 参考文章 [吾爱破解] [验证码识别]易盾空间推理验证码识别详细流程 作者:s1lencee

[2]: 参考文章 [知识星球](付费内容不便公开) 作者: 时一姐

特别感谢强哥时一姐,以及我的兄弟飞龙为我提供了帮助,感谢!以此记录!

———— END ————