【点选验证码】生成点选验证码图片

news2025/6/28 17:02:04

生成点选验证码图片
在这里插入图片描述
参考博客:https://blog.csdn.net/sinat_39629323/article/details/121989609

from tqdm import tqdm
from PIL import Image, ImageDraw, ImageFont, ImageOps
import shutil,os
import numpy as np
import cv2
import math
import random
file_path = "/data/lh123/lh/verification_code/generate_data/ppocr_keys_v1.txt"#字典

#返回字外面矩形的4个框
def rotate_rectangle(top_left, bottom_right, angle_degrees):
    # 转换角度为弧度
    angle_rad = math.radians(angle_degrees)
    
    # 矩形的四个角的坐标
    top_right = (bottom_right[0], top_left[1])
    bottom_left = (top_left[0], bottom_right[1])

    # 找到矩形的中心
    center = ((top_left[0]+bottom_right[0])/2, (top_left[1]+bottom_right[1])/2)

    # 定义一个函数来旋转一个点
    def rotate_point(point):
        # 移动到以中心为原点的坐标系
        x = point[0] - center[0]
        y = center[1] - point[1]  # 注意我们在这里翻转y轴,因为图像的原点在左上角

        # 在新的坐标系中进行旋转
        new_x = x * math.cos(angle_rad) - y * math.sin(angle_rad)
        new_y = x * math.sin(angle_rad) + y * math.cos(angle_rad)

        # 再次翻转y轴并加上旋转中心的坐标
        return new_x + center[0], center[1] - new_y

    # 这里我们直接返回没有旋转的矩形的四个角的坐标
    points = [top_left, top_right, bottom_right, bottom_left]

    # 展开点列表并返回
    return [coord for point in points for coord in point]
#---=---img为PIL 对象,将这个图片转为数组
def img_to_array(img,x1, y1, x2, y2, x3, y3, x4, y4):
    width, height = img.size
    pixel_data = list(img.getdata())
    return [pixel_data[n:n+width] for n in range(0, width*height, width)]


# 计算区域内的平均颜色
def calculate_average_color(img_array,x1, y1, x2, y2, x3, y3, x4, y4):
    x1, y1, x2, y2, x3, y3, x4, y4=x1, y1, x2, y2, x3, y3, x4, y4
    total_color = [0, 0, 0, 0]
    count = 0
    for y in range(min(y1, y2, y3, y4), max(y1, y2, y3, y4)):
        for x in range(min(x1, x2, x3, x4), max(x1, x2, x3, x4)):
            total_color = [total_color[i] + img_array[y][x][i] for i in range(3)]
            count += 1
    return [total // count for total in total_color]

# 生成与给定颜色相差较大的颜色
def generate_distinct_colors(avg_color, num_colors,x1, y1, x2, y2, x3, y3, x4, y4):
    colors = []
    for i in range(num_colors):
        random_shift = random.randint(100, 200) + i * 15  # 这里可以调整以获取不同的颜色
        # 对RGB进行更改,保持alpha不变
        rgb = tuple((avg_color[j] + random_shift) % 256 for j in range(3))
        # 将原始的alpha添加到rgb中
        color = rgb 
        colors.append(color)
    return colors

#图像不同连通域填充不同颜色(根据读取字体原来的颜色,遍历每一个像素,不同连通域填充不同的颜色)
def color_regions(img_array, color, colors,x1, y1, x2, y2, x3, y3, x4, y4):
    x1, y1, x2, y2, x3, y3, x4, y4=x1, y1, x2, y2, x3, y3, x4, y4
    directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
    marked = set()
    region_count = 0
    threshold = 0  # 设置一个阈值

    def color_distance(c1, c2):
        return ((c1[0] - c2[0]) ** 2 + (c1[1] - c2[1]) ** 2 + (c1[2] - c2[2]) ** 2) ** 0.5

    def dfs(x, y, new_color):
        stack = [(x, y)]
        while stack:
            x, y = stack.pop()
            # 判断像素位置是否在指定区域内
            if x < min(x1, x2, x3, x4) or x > max(x1, x2, x3, x4) or y < min(y1, y2, y3, y4) or y > max(y1, y2, y3, y4):
                continue
            if (x, y) in marked or color_distance(img_array[y][x], color) > threshold:
                continue
            marked.add((x, y))
            img_array[y][x] = new_color
            for dx, dy in directions:
                nx, ny = x + dx, y + dy
                if nx >= 0 and ny >= 0 and ny < len(img_array) and nx < len(img_array[0]):
                    stack.append((nx, ny))

    for y in range(len(img_array)):
        for x in range(len(img_array[0])):
            if color_distance(img_array[y][x], color) <= threshold and (x, y) not in marked:
                dfs(x, y, colors[region_count % len(colors)])
                region_count += 1

    return img_array

#---------因为上面函数不能解决字的轮框是一个渐变的颜色,看起来有框,所以这里在起外围的框也填充为字体颜色
def replace_color(img_array, target_color, replace_color, x1, y1, x2, y2, x3, y3, x4, y4):
    img_array = np.array(img_array)
    x1, y1, x2, y2, x3, y3, x4, y4=x1, y1, x2, y2, x3, y3, x4, y4
    a = np.zeros_like(img_array) 
   
    for y in range(y1, y3):
        for x in range(x1, x2):
            
            if np.array_equal(img_array[y][x], target_color):
                img_array[y][x] = replace_color
                
                if (a[y][x] == 1).all():
                    continue
                if y > y1 and not np.array_equal(img_array[y-1][x], target_color):
                 
                    img_array[y-1][x] = replace_color
                    a[y-1][x] = 1
                if y < y3 and y+1<688 and not np.array_equal(img_array[y+1][x], target_color) :
                    img_array[y+1][x] = replace_color
                    a[y+1][x] = 1
                if x > x1 and not np.array_equal(img_array[y][x-1], target_color):
                    img_array[y][x-1] = replace_color
                    a[y][x-1] = 1
                if x < x2 and x<1103 and not np.array_equal(img_array[y][x+1], target_color):
                    img_array[y][x+1] = replace_color
                    a[y][x+1] = 1
                    
    return img_array


class CreateData:
    def __init__(self,create_num):
        self.jay_img_paths=['/data/lh123/lh/verification_code/generate_data/背景图_最终20/' + i for i in os.listdir('/data/lh123/lh/verification_code/generate_data/背景图_最终20/')] # 背景图片路径
        self.img_save_path='/data/lh123/lh/verification_code/generate_data/train_2/' # 生成训练集图片的路径

        # 读取文件内容
        with open(file_path, "r", encoding="utf-8") as file:
            content = file.read()

        # 将单个字保存在列表中
        self.songs = list(content)
    
        self.song2label={song:i for i,song in enumerate(self.songs)}#得到标签,第一个
        self.label2song={i:song for i,song in enumerate(self.songs)}#得到内容
        self.create_num=create_num
        self.image_w=1104  #图片大小
        self.image_h=688#图片大小
        self.max_iou=0.01  # 每首歌名的boxes的iou不能超过0.1,#控制重叠
    #文件夹不存在就造一个,并且文件夹存在会初始化文件夹
    def create_folder(self):
        while True:
            try:
                for path in [self.img_save_path,self.label_save_path,self.test_path]:
                    shutil.rmtree(path,ignore_errors=True)
                    os.makedirs(path,exist_ok=True)
                break
            except:
                pass
    #计算重叠比例
    def bbox_iou(self,box2):
        '''两两计算iou'''
        for box1 in self.tmp_boxes_boxs1:
            inter_x1=max([box1[0],box2[0]])
            inter_y1=max([box1[1],box2[1]])
            inter_x2=min([box1[2],box2[2]])
            inter_y2=min([box1[3],box2[3]])
            inter_area=(inter_x2-inter_x1+1) * (inter_y2-inter_y1+1)
            box1_area=(box1[2]-box1[0]+1) * (box1[3]-box1[1]+1)
            box2_area=(box2[2]-box2[0]+1) * (box2[3]-box2[1]+1)
            iou=inter_area / (box1_area + box2_area - inter_area + 1e-16)
            if iou > self.max_iou:
                # 只要有一个与之的iou大于阈值则重新来过
                return iou
        else:
            return 0
#---------原来的
    def draw_text(self, image, image_draw, song,font_path):
        self.font_path=font_path
        iou = np.inf
        num=0
        while iou > self.max_iou:#判断是否重叠
            if num >= 3000:#最多循环3000防止无线循环,超多就会使得重叠
                break
            random_font_size = np.random.randint(110, 240)#字大小
            random_rotate = np.random.randint(-60, 60)#旋转角度
            random_x = np.random.randint(1, 1104, 1)#写字地址随机数
            random_y = np.random.randint(1, 688, 1)

            font = ImageFont.truetype(self.font_path, random_font_size)
            label = self.song2label[song]#第几个字
            size_wh = font.getsize(song)  #得到字的大小

            img = Image.new('L', size_wh)
            img_draw = ImageDraw.Draw(img)
            img_draw.text((0, 0), song, font=font, fill=255)#写字
            img_rotate = img.rotate(random_rotate, resample=2, expand=True)

            background_color = image.getpixel((int(random_x), int(random_y)))#背景颜色,这里是背景颜色像素随便找一个
            font_color = tuple((np.array(background_color) + np.array([128, 128, 128])) % 256)#得到与背景相差较大的颜色
            img_color = ImageOps.colorize(img_rotate, (0, 0, 0), font_color)
            
            w, h = img_color.size
            xmin = int(random_x)#左上角坐标
            ymin = int(random_y)

            if random_x + w > self.image_w:#判断越界
                xmin = self.image_w - w - 2
            if random_y + h > self.image_h:
                ymin = self.image_h - h - 2
            xmax = xmin + w#右下角坐标
            ymax = ymin + h
            a=rotate_rectangle((xmin, ymin), (xmax, ymax), random_rotate)#得到坐标
            boxes = (a[0], a[1], a[2], a[3],a[4],a[5],a[6],a[7])#整个框的坐标,左上角、右上角、右下角,左下角
            boxes1 = (xmin, ymin,xmax,ymax)
            #----判断重叠是否大
            iou = self.bbox_iou(boxes1)
            #-----判断字体和字是否匹配
            # 对于每个文件,初始化相应的字体对象
            
            fnt = ImageFont.truetype(self.font_path, 15)
           
        
            #检查字是否与字体匹配,如果不匹配就重新那字体
            if not fnt.getmask(song):
               
                #随机字体
                font_directory = '/data/lh123/lh/verification_code/generate_data/fonts'
                font_files = [f for f in os.listdir(font_directory) if f.endswith('.ttf') or f.endswith('.otf') or f.endswith('.ttc') or f.endswith('.TTF') or f.endswith('.OTF') or f.endswith('.TTC')]
                # font_files = [f for f in os.listdir(font_directory) if f.endswith('.otf') ]
                
                random_font_file = random.choice(font_files)
                self.font_path = os.path.join(font_directory, random_font_file)
                iou=1#这为1意思还得执行一下
            num += 1
           
        image.paste(img_color, box=(xmin, ymin), mask=img_rotate)#写字
        return image, boxes, label,boxes1,font_color,song


    def process(self,boxes):   #归一化处理----没用这个---这个是弄成比例
        '''
        将xmin,ymin,xmax,ymax转为x,y,w,h
        以及归一化坐标,生成label
        '''

        x1,y1,x2,y2=boxes
        x=((x1+x2)/2)/self.image_w
        y=((y1+y2)/2)/self.image_h
        w=(x2-x1)/self.image_w
        h=(y2-y1)/self.image_h
        return [x,y,w,h]
    def main(self):
        '''主函数'''
        self.create_folder() # 重置所需文件夹,如果多线程的话,需要删除这个代码
        txt_file='/data/lh123/lh/verification_code/generate_data/train_2.txt'
        with open(txt_file,'w') as f:#在外面速度更快,但是如果多线程会存在数据写串行的问题,但这种数据不多,可以进行数据清理
            num=1
            for i in tqdm(range(self.create_num)):
                self.font_color_list=[]
                random_song_num=np.random.randint(4,6) # 一张图片中4-5个字
                random_jay_img_path=np.random.choice(self.jay_img_paths) # 随机背景
                image=Image.open(random_jay_img_path).convert('RGB').resize((self.image_w,self.image_h))
                image_draw=ImageDraw.Draw(image)
                boxes_list=[]
                label_list=[]
                self.tmp_boxes=[] # 用于计算两两boxes的iou    
                self.tmp_boxes_boxs1=[] #
                self.song_list=[]
                for j in range(random_song_num):
                    song=np.random.choice(self.songs)
                    # song=self.songs[5998]
                    #随机字体
                    font_directory = '/data/lh123/lh/verification_code/generate_data/fonts'
                    font_files = [f for f in os.listdir(font_directory) if f.endswith('.ttf') or f.endswith('.otf') or f.endswith('.ttc') or f.endswith('.TTF') or f.endswith('.OTF') or f.endswith('.TTC')]
                    # font_files = [f for f in os.listdir(font_directory) if f.endswith('.otf') ]
                    random_font_file = random.choice(font_files)
                    self.font_path = os.path.join(font_directory, random_font_file)
    
                    image,boxes,label,boxes1,font_color,self.song=self.draw_text(image,image_draw,song,self.font_path)#图片,框,字体
                    self.font_color_list.append(font_color)
                  
                    self.tmp_boxes.append(boxes)
                    self.tmp_boxes_boxs1.append(boxes1)
                    self.song_list.append(song)
                    # boxes_list.append(self.process(boxes))#存储框对应的字
                    boxes_list.append(boxes)#存储框对应的字

                    label_list.append(label)
                    
                #图片名
                image_filename=self.img_save_path+f'image{num}.jpg' if i < self.create_num else self.test_path+f'test{i}.png'#保存文件
                label_filename=self.label_save_path+f'image{num}.txt' if i < self.create_num else self.test_path+f'test{i}.txt'
                num=num+1
                #输入坐标,图像位置,图像颜色,随机数(0-2)、image
                # 要求不同于图像的每一个颜色,并且存在部分差异,最后返回image 
                
                random_num = random.randint(0, 2) #一张图片中可能有0个、1、2个彩色字
                if(random_num!=0):
                    
                    for i in range(random_num):#主要实现连通域不同字不同
                        x1, y1, x2, y2, x3, y3, x4, y4 = boxes_list[i][0],boxes_list[i][1],boxes_list[i][2],boxes_list[i][3],boxes_list[i][4],boxes_list[i][5],boxes_list[i][6],boxes_list[i][7]
                        img_array = img_to_array(image,x1, y1, x2, y2, x3, y3, x4, y4)
                        # 计算平均颜色
                        avg_color = calculate_average_color(img_array,x1, y1, x2, y2, x3, y3, x4, y4)  
                        # 生成与平均颜色相差较大的颜色
                        colors = generate_distinct_colors(avg_color, 6,x1, y1, x2, y2, x3, y3, x4, y4)
                        

                        # 字的颜色
                        f_color=self.font_color_list[i]+(255,)
                        f_color_list = list(self.font_color_list[i])
                        f_color_nup = np.array(f_color_list)
                        #颜色预处理膨胀
                        img_array=replace_color(img_array, f_color_nup, f_color_nup,x1, y1, x2, y2, x3, y3, x4, y4)
                        img_array=replace_color(img_array, f_color_nup, f_color_nup,x1, y1, x2, y2, x3, y3, x4, y4)


                        # 查找和标记所有的连通区域,并改变每个区域的颜色
                        new_img_array = color_regions(img_array, f_color, colors,x1, y1, x2, y2, x3, y3, x4, y4)
                        image = Image.fromarray(np.uint8(new_img_array))
                    


                image.save(image_filename,format='JPEG')
                #写入便签文件txt文件内容
                f.write(f'labels/{image_filename}\t[')
                number=0
                for k in range(len(label_list)):
                    # label x y w h
                    # f.write(f'{self.song_list[k]} {boxes_list[k][0]} {boxes_list[k][1]} {boxes_list[k][2]} {boxes_list[k][3]} {boxes_list[k][4]} {boxes_list[k][5]} {boxes_list[k][6]} {boxes_list[k][7]}\n')
                    f.write(f'{{"transcription":"{self.song_list[k]}","points":[[{int(boxes_list[k][0])},{int(boxes_list[k][1])}],[{int(boxes_list[k][2])},{int(boxes_list[k][3])}],[{int(boxes_list[k][4])},{int(boxes_list[k][5])}],[{int(boxes_list[k][6])},{int(boxes_list[k][7])}]]}}')
                    if(number!=(len(label_list)-1)):
                        f.write(f',')
                    number=number+1
                f.write(f']\n')
                            
if __name__ == '__main__':
    creator=CreateData(500000)#这里是生成50万张的意思
    creator.main()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/802546.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python-异常处理

# try : # print(name) # except NameError: # print("有问题")# # try: # 1 / 0 # except ZeroDivisionError as ze: # print("xxxx", ze) # except NameError as ne: # print("666",ne) # else: # print("123&qu…

Domain Cluster Model

Introduction Domain Cluster Model 3.0 Background of Domain Management  领域管理背景 Common Framework 通用框架Architecture & Synergy Analysis 体系结构与协同分析Demand & Project Support 需求和项目支持Same Understanding between BU & IT BU和IT之间…

2 halcon ROI技术

文章目录 ROI 技术代码 ROI 技术 ROI 技术允许你在图像中定义一个特定的区域&#xff0c;并将该区域应用于后续的图像处理和分析任务。以下是使用 Halcon 实现 ROI 技术的一般步骤&#xff1a; 创建 ROI 对象&#xff1a;首先&#xff0c;你需要创建一个 ROI 对象来定义感兴趣…

使用 Python 自动备份 SQL 数据库

◆ 背景 在本文中&#xff0c;我们将深入探讨如何利用Python实现自动备份SQL数据库&#xff0c;以达到备份过程更加高效、简便和低出错率的目的。借助Python自动备份SQL数据库的方法&#xff0c;您能够节省时间、降低错误风险&#xff0c;并且保障数据始终得到有效保护。 定期…

超导体的概念及其用处

目录 1.什么是超导体 2.研究超导体的意义 3.常温超导体 4.韩国超导 1.什么是超导体 超导体是指在低温下具有零电阻和完全排斥磁场的材料。当超导体被冷却到其临界温度以下&#xff0c;电流可以在其内部无阻碍地流动&#xff0c;且磁场被完全排斥出超导体的表面&#xff0c;这…

移动端开发框架mui之源代码及 examples 下载

MUI版本号&#xff1a; v3.7.3 下载地址1&#xff1a; 百度网盘&#xff08;备份&#xff09; 链接&#xff1a;https://pan.baidu.com/s/1jqBNQFNHPbzqUYLJ2Hjgmw?pwdafk9 提取码&#xff1a;afk9 下载地址2&#xff1a; mui.js 及 examples 下载 - 码农之家

STM32 SPI学习

SPI 串行外设设备接口&#xff08;Serial Peripheral Interface&#xff09;&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线。 SCK时钟信号由主机发出。 SPI接口主要应用在存储芯片。 SPI相关引脚&#xff1a;MOSI&#xff08;输出数据线&#xff…

myBatis报错:Parameter index out of range (3 > number of parameters, which is 2).

【问题描述】&#xff1a; 今天在写mybatis的动态判断语句时&#xff0c;出现报错信息&#xff1a;&#xff08;报错信息从下往上看&#xff09; Parameter index out of range (3 &#xff1e; number of parameters, which is 2). 我们在mapper.xml文件中写动态SQL&#x…

【Terraform学习】Terraform配置变量(Terraform配置语言学习)

配置变量 实验步骤 创建 EC2 IAM 角色 导航到IAM 在左侧菜单中&#xff0c;单击角色 。单击创建角色该按钮以创建新的 IAM 角色。 在创建角色部分&#xff0c;为角色选择可信实体类型&#xff1a; AWS 服务 使用案例:EC2 单击下一步 添加权限&#xff1a;现在&#xff0c…

Rviz2的自定义插件开发基础知识

1. 简介 Rviz中有不同类型的插件&#xff0c;每个插件都必须具有相应的基本类型&#xff0c;才能被RViz识别 plugin typebase typeDisplayrviz_common::DisplayPanelrviz_common::PanelToolrviz_common::ToolFrames transformation libraryrviz_common::transformation::Fram…

有哪些虚拟机好用?并且对电脑没有伤害

在当今数字化时代&#xff0c;虚拟化技术越来越受到人们的关注和使用。虚拟机作为一种强大的工具&#xff0c;可以在一台计算机上模拟多个独立的操作系统环境&#xff0c;为用户提供了灵活性和便利性。然而&#xff0c;有人担心使用虚拟机可能会对电脑造成损害&#xff0c;尤其…

多普勒气泡监测与分级方法

Ultrasound detection of vascular decompression bubbles the influence of new technology and considerations on bubble load 多普勒气泡检测方法&#xff1a; 听觉多普勒超声监测法视觉二维超声成像谐波超声成像&#xff08;2的变种&#xff09; 多普勒气泡分级方法&…

objectarx编译Debug版arx遇到的问题

错误一链接错误 1>pietdoginterface_s.lib(Msg.obj) : error LNK2001: 无法解析的外部符号 “class ATL::CTraceCategory ATL::atlTraceException” (?atlTraceExceptionATL3VCTraceCategory1A) 1>pietdoginterface_s.lib(Msg.obj) : error LNK2001: 无法解析的外部符号…

【洁洁送书第二期】Python机器学习:基于PyTorch和Scikit-Learn

前言 近年来&#xff0c;机器学习方法凭借其理解海量数据和自主决策的能力&#xff0c;已在医疗保健、 机器人、生物学、物理学、大众消费和互联网服务等行业得到了广泛的应用。自从AlexNet模型在2012年ImageNet大赛被提出以来&#xff0c;机器学习和深度学习迅猛发展&#xf…

Element-UI 10个技巧

el-scrollbar 滚动条 看到这个组件是不是有点陌生&#xff0c;陌生就对了&#xff0c;因为它从来没有出现在 element 官网上&#xff08;估计是性能问题&#xff09;&#xff0c;但好东西怎么能藏着掖着&#xff0c;来上效果图。 是不是比原生的滚动条美观多了&#xff0c;使用…

【2】-第一个Locust脚本

创建&执行第一个locustfile.py 在ide中创建我们的loucst项目&#xff0c;并创建第一个测试脚本&#xff0c;代码如下 ## /mylocust/locustfiles/first_locustfile.py import os from locust import HttpUser, task# 继承locust的HttpUser类&#xff0c;这样我们就可以使用…

argo workflows archived 日志设置

由于工作需要配置argo workflows archived 归档日志&#xff0c;介绍一下大致步骤: 文章目录 1.没有设置前&#xff0c;归档日志是不能展示的2.编辑configmap3 配置数据库用户名和密码&#xff1a;4.把workflow这个pod删掉&#xff0c;让他重新生成一个解决用户权限问题 1.没有…

[VRTK4.0]将Unity输入系统与VRTKv4结合使用

学习目标&#xff1a; 展示了如何在Unity项目中设置Unity输入系统&#xff0c;以及如何导入输入系统Tilia包以支持VRTKTilia包与新的Unity输入系统操作一起工作。 流程&#xff1a; 步骤一&#xff1a; 首先我们需要再次检查项目设置是否具有新的Unity输入系统。通过Project S…

浅谈现代通信技术

目录 1.传统通信方法 2.传统通信方式的缺点 3.现代通信技术 4.现代通信技术给人类带来的福利 1.传统通信方法 传统通信方法指的是在数字化通信之前使用的传统的通信方式。以下是一些常见的传统通信方法&#xff1a; 1. 书信&#xff1a;通过邮件或快递等方式发送纸质信件。这…

JVM调优详解

SneakyThrowspublic static void main(String[] args) {HashMap hashMap new HashMap();ArrayList arrayList new ArrayList();Byte[] e new Byte[1024];while (true) {Thread.sleep(10);arrayList.add(e);}} java执行代码 C:\WINDOWS\System32>jps -l 26576 org/netbea…