图像增强 目标检测 仿射变换 图像处理 扭曲图像

news2024/11/26 14:41:59

1.背景

在目标检测中,需要进行图像增强。这里的代码模拟了旋转、扭曲图像的功能,并且在扭曲的时候,能够同时把标注的结果也进行扭曲。

这里忽略了读取xml的过程,假设图像IMG存在对应的标注框,且坐标为左上、右下两个点,这立刻哟把IMG进行扭曲,并且尽可能保证标签的坐标点也随之扭曲。

2.效果

我有一张铁塔的照片(原图略,原图是一张角度非常正确的图片),处理后效果如下图所示,可以看到图像出现了扭曲(出现了黑边),并且图像右侧中间部分的红色绝缘子旁边出现了两个小白点(为了便于观察而添加的),说明起到了扭曲的作用,并且大概率能保证坐标也被正确变化。
在这里插入图片描述

3.动机

设计这种数据增强策略,主要出于以下两种考虑:

  1. 在实际使用中,我发现即便是yolov8这样的模型,对于扭曲的图像(例如扭曲的笔记本),识别效果会有非常严重的下降,而真实情况中非常有可能出现扭曲(例如有人把笔记本半合上),模型需要识别。在电力场景中,也可能出现拍摄角度异常等情况。
  2. 在训练中,将不同角度的目标展现给模型,也可能提高模型的效果。例如上图中的绝缘子,比原图要“矮胖”一些,因此可以算作“新样本”,为模型提供更丰富的数据来源。

4.具体实现

第一步是图像扭曲。在这一步中,代码里的trapezoid_vertices表示你希望把图像的哪一部分进行扭曲,target_trapezoid_vertices表示你希望把trapezoid_vertices扭曲到什么位置。你可以把代码中相关位置的generate_random_cut和generate_random_perspective都去掉,然后就能明白了。

第二步是转化原始标注的框。在使用gpt生成代码的时候,提示词将任务划分为了5个步骤,提示词如下

假设原始图像PIC宽度为W,高度为H。在图像标注场景中,我会告诉你两个点special_points,分别代表原始的标注框的左上角和右下角。你需要执行以下步骤:
【步骤1】根据special_points,恢复原始的标注框的四个坐标。注意保留这四个点的顺序关系。
【步骤2】:利用自定义函数fun1计算出变化之后的四个点的新坐标。
【步骤3】依次判断四个点所连成的线段是否与原始图像PIC的边缘有交点,如果有,就将这些交点的坐标存到point_list中。
【步骤4】判断变换后的四个点有哪些位于原始图像PIC的范围内,如果有,则将这些点的坐标存到point_list中。
【步骤5】分析point_list中的所有点,找出一个能完整包含这些点的矩形R,返回这个矩形R的左上角和右下角。

辅助代码包含了前面提到的5个步骤,具体如下:

import random
import cv2
import numpy as np

# 原始图像剪裁的范围
max_cut = 0.1
min_cut = 0.05

# 扭曲的最大和最小程度
max_perspective = 0.1
min_perspective = 0.05


# 制定初始剪裁范围,你可以选择从某些地方开始剪裁你的图像
def generate_random_cut(x):
    # 计算 x 的 10% 和 20%
    min_value = min_perspective * x
    max_value = max_perspective * x

    # 生成一个在 10% 到 20% 范围内的随机数
    random_value = random.uniform(min_value, max_value)

    # 随机决定这个数是正数还是负数
    sign = random.choice([-1, 1])

    return sign * random_value


# 制定目标梯形的范围
def generate_random_perspective(x):
    # 计算 x 的 10% 和 20%
    min_value = min_perspective * x
    max_value = max_perspective * x

    random_value = random.uniform(min_value, max_value)

    # 随机决定这个数是正数还是负数
    sign = random.choice([-1, 1])

    return sign * random_value


# 根据输入的两个点的坐标,复原出连续的矩形框的四个点坐标
def get_bbox_corners(special_points):
    top_left = special_points[0][0]
    bottom_right = special_points[0][1]
    top_right = [bottom_right[0], top_left[1]]
    bottom_left = [top_left[0], bottom_right[1]]
    return np.array([top_left, top_right, bottom_right, bottom_left])


# line_intersection所需要的辅助函数
def on_segment(p, q, r):
    if (q[0] <= max(p[0], r[0]) and q[0] >= min(p[0], r[0]) and
            q[1] <= max(p[1], r[1]) and q[1] >= min(p[1], r[1])):
        return True
    return False

# line_intersection所需要的辅助函数
def orientation(p, q, r):
    val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1])
    if val == 0:
        return 0  # collinear
    elif val > 0:
        return 1  # clockwise
    else:
        return 2  # counterclockwise

# line_intersection所需要的辅助函数
def segments_intersect(p1, q1, p2, q2):
    o1 = orientation(p1, q1, p2)
    o2 = orientation(p1, q1, q2)
    o3 = orientation(p2, q2, p1)
    o4 = orientation(p2, q2, q1)

    if o1 != o2 and o3 != o4:
        return True

    if o1 == 0 and on_segment(p1, p2, q1):
        return True

    if o2 == 0 and on_segment(p1, q2, q1):
        return True

    if o3 == 0 and on_segment(p2, p1, q2):
        return True

    if o4 == 0 and on_segment(p2, q1, q2):
        return True

    return False


# 判断四个点所连成的线段是否与原始图像的边缘有交点
def line_intersection(p1, p2, edge_start, edge_end):
    if not segments_intersect(p1, p2, edge_start, edge_end):
        return None

    xdiff = (p1[0] - p2[0] + 0.01, edge_start[0] - edge_end[0] + 0.01)
    ydiff = (p1[1] - p2[1] + 0.01, edge_start[1] - edge_end[1] + 0.01)

    def det(a, b):
        return a[0] * b[1] - a[1] * b[0]

    div = det(xdiff, ydiff)
    if div == 0:
        return None

    d = (det(p1, p2), det(edge_start, edge_end))
    x = det(d, xdiff) / div
    y = det(d, ydiff) / div
    return x, y


# 判断四个点所连成的线段是否与原始图像的边缘有交点
def check_intersections(bbox_corners, w, h):
    edges = [
        ((0.01, 0), (w, 0.01)),
        ((w, 0.01), (w, h)),
        ((w, h), (0.01, h)),
        ((0.01, h), (0.01, 0.01))
    ]
    point_list = []
    for i in range(len(bbox_corners)):
        p1 = bbox_corners[i]
        p2 = bbox_corners[(i + 1) % len(bbox_corners)]

        for edge in edges:
            intersection = line_intersection(p1, p2, edge[0], edge[1])
            if intersection:
                point_list.append(intersection)
    return point_list


# 判断变换后的四个点是否位于原始图像范围内
def check_points_within_image(bbox_corners, w, h):
    point_list = []
    for point in bbox_corners:
        if 0 <= point[0] <= w and 0 <= point[1] <= h:
            point_list.append(point)
    return point_list


# 分析所有点,找出一个能完整包含这些点的矩形
def get_bounding_box(point_list):
    min_x = min(point[0] for point in point_list)
    max_x = max(point[0] for point in point_list)
    min_y = min(point[1] for point in point_list)
    max_y = max(point[1] for point in point_list)
    return (min_x, min_y), (max_x, max_y)

使用方法如下:

# 数据增强 - 拉伸
if __name__ == "__main__":

    # 定义两个特殊点,即原始标注框的左上和右下
    special_points = [
        [3400, 1655],  # Example points, replace with your own
        [4550, 2350]
    ]

    # 读取图片
    image_path = r'D:\data\拉伸原始图像.jpg'
    image = cv2.imread(image_path)

    # 获取图像宽度和高度
    height, width = image.shape[:2]

    # 定义顶点坐标,你会保留这四个点之内的图像,以便进行后续步骤
    trapezoid_vertices = np.array([[0 + generate_random_cut(width), 0 + generate_random_cut(height)],
                                   [width + generate_random_cut((width)), 0 + generate_random_cut((height))],
                                   [width + generate_random_cut(width), height + generate_random_cut(height)],
                                   [0 + generate_random_cut(width), height + generate_random_cut(height)]],
                                   dtype=np.float32)

    # 定义目标图像的顶点坐标,会将trapezoid_vertices所保留的图像拉伸,拉伸到target_trapezoid_vertices所对应的范围内
    target_trapezoid_vertices = np.array(
                            [[0 + generate_random_perspective(width), 0 + generate_random_perspective(height)],
                                   [width + generate_random_perspective(width), 0 + generate_random_perspective(height)],
                                   [width + generate_random_perspective(width),
                                   height + generate_random_perspective(height)],
                                   [0 + generate_random_perspective(width), height + generate_random_perspective(height)]],
                                   dtype=np.float32)

    # 计算透视变换矩阵,用于将image进行拉伸
    perspective_matrix = cv2.getPerspectiveTransform(trapezoid_vertices, target_trapezoid_vertices)

    # 进行透视变换
    trapezoid_image = cv2.warpPerspective(image, perspective_matrix, (width, height))

    # 将坐标框进行处理,利用perspective_matrix得到新的坐标框的左上角和右下角
    special_points = np.array(special_points, dtype='float32')
    special_points = np.array([special_points])

    transformed_special_points = cv2.perspectiveTransform(special_points, perspective_matrix)

    # 得到新的标注的框的四个顶点的坐标
    bbox_corners = get_bbox_corners(transformed_special_points)

    # 如果有超出图像边界的点,就计算与图像边界的交点,保存到point_list中。
    point_list = check_intersections(bbox_corners, width, height)

    # 把留在图像范围内的点也加到point_list中
    point_list.extend(check_points_within_image(bbox_corners, width, height))

    # 得出新的理想中的标注框的坐标
    if point_list:
        rect = get_bounding_box(point_list)
    else:
        rect = None
    print(len(rect))
    # 就爱那个新的标注框的左上角和右下角绘制出来,以便判断是否正确
    for point in rect:
        x = tuple([int(i) for i in point])
        cv2.circle(trapezoid_image, x, 15, (256, 256, 256), 10)

    # 保存变换后的图像
    save_path = r'D:\data\拉伸结果.jpg'
    cv2.imwrite(save_path, trapezoid_image)

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

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

相关文章

前后端的学习框架

前后端的学习框架 视频链接&#xff1a;零基础AI全栈开发系列教程&#xff08;一&#xff09;_哔哩哔哩_bilibili

C++第一弹 -- C++基础语法上(命名空间 输入输出 缺省参数 函数重载 引用)

目录 前言一. C关键字(C98)二. 命名空间1.命名空间的定义2.命名空间的使用3.其它部分 三. C输入&输出四. 缺省参数1. 缺省参数的概念2.缺省参数的分类 五. 函数重载1.函数重载的概念2. 为什么C支持函数重载, 而C语言不支持重载呢? 六. 引用1.引用的概念2.引用的特性3.常引…

汽车报价资讯app小程序模板源码

蓝色实用的汽车报价&#xff0c;汽车新闻资讯&#xff0c;最新上市汽车资讯类小程序前端模板。包含&#xff1a;选车、资讯列表、榜单、我的主页、报价详情、资讯详情、询底价、登录、注册、车贷&#xff0c;油耗、意见反馈、关于我们等等。这是一款非常全的汽车报价小程序模板…

ThreadPoolExecutor - 管理线程池的核心类

下面是使用给定的初始参数创建一个新的 ThreadPoolExecutor &#xff08;构造方法&#xff09;。 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,…

缓冲器的重要性,谈谈PostgreSQL

目录 一、PostgreSQL是什么二、缓冲区管理器介绍三、缓冲区管理器的应用场景四、如何定义缓冲区管理器 一、PostgreSQL是什么 PostgreSQL是一种高级的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它以其稳定性、可靠性和高度可扩展性而闻名。它最初由加…

Static关键字的用法详解

Static关键字的用法详解 1、Static修饰内部类2、Static修饰方法3、Static修饰变量4、Static修饰代码块5、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java编程语言中&#xff0c;static是一个关键字&#xff0c;它可以用于多种上…

【Java探索之旅】初识多态_概念_实现条件

文章目录 &#x1f4d1;前言一、多态1.1 概念1.2 多态的实现条件 &#x1f324;️全篇总结 &#x1f4d1;前言 多态作为面向对象编程中的重要概念&#xff0c;为我们提供了一种灵活而强大的编程方式。通过多态&#xff0c;同一种操作可以应用于不同的对象&#xff0c;并根据对象…

数据库系统原理 | 查询作业2

整理自博主本科《数据库系统原理》专业课自己完成的实验课查询作业&#xff0c;以便各位学习数据库系统概论的小伙伴们参考、学习。 *文中若存在书写不合理的地方&#xff0c;欢迎各位斧正。 专业课本&#xff1a; ​ ​ ———— 本次实验使用到的图形化工具&#xff1a;Heidi…

深入分析SSL/TLS服务器的证书(C/C++代码实现)

SSL&#xff08;Secure Sockets Layer&#xff09;和TLS&#xff08;Transport Layer Security&#xff09;是网络安全领域的重要协议&#xff0c;它们在保护网络通信中发挥着至关重要的作用。这些协议通过加密和身份验证机制&#xff0c;确保数据在传输过程中的机密性和完整性…

cs231n作业1——SVM

参考文章&#xff1a;cs231n assignment1——SVM SVM 训练阶段&#xff0c;我们的目的是为了得到合适的 &#x1d44a; 和 &#x1d44f; &#xff0c;为实现这一目的&#xff0c;我们需要引进损失函数&#xff0c;然后再通过梯度下降来训练模型。 def svm_loss_naive(W, …

python基础篇(8):异常处理

在Python编程中&#xff0c;异常是程序运行时发生的错误&#xff0c;它会中断程序的正常执行流程。异常处理机制使得程序能够捕获这些错误&#xff0c;并进行适当的处理&#xff0c;从而避免程序崩溃。 1 错误类型 代码的错误一般会有语法错误和异常错误两种&#xff0c;语法错…

最新整理的机器人相关数据合集(1993-2022年不等 具体看数据类型)

机器人安装数据是指记录全球或特定区域内工业机器人新安装数量的信息&#xff0c;这一数据由国际机器人联合会(IFR)等权威机构定期发布。这些数据不仅揭示了机器人技术的市场需求趋势&#xff0c;还反映了各国和地区自动化水平及产业升级的步伐。例如&#xff0c;数据显示中国在…

nginx相关概念(反向代理、负载均衡)

1 Nginx 是什么 Nginx是一款轻量级的Web 服务器&#xff0c;其特点是占有内存少&#xff0c;并发能力强 2 Nginx 反向代理 正向代理代替客户端去发送请求反向代理代替服务端接受请求 2.1 正向代理 若客户端无法直接访问到目标服务器 server 则客户端需要配置代理服务器 pr…

云渲染技术对电影24帧和游戏60帧渲染需求及时间效率的影响

随着云计算技术的飞速发展&#xff0c;云渲染正重塑着影视和游戏产业的制作流程。它如何影响传统电影24帧和现代游戏60帧的渲染需求与时间效率&#xff1f;本文将深入探讨云渲染带来的变革。 一、电影24帧和游戏60帧作用 电影通常以24帧每秒&#xff08;fps&#xff09;的标准…

数字化精益生产系统--IFS财务管理系统

IFS财务管理系统是一款功能丰富、高效且灵活的企业财务管理软件&#xff0c;广泛应用于多个行业和不同规模的企业中。以下是对IFS财务管理系统的功能设计&#xff1a;

Linux shell编程学习笔记63:free命令 获取内存使用信息

0 前言 在系统安全检查中&#xff0c;内存使用情况也是一块可以关注的内容。Linux提供了多个获取内存信息的命令很多。今天我们先研究free命令。 1 free命令的功能、用法和选项说明 1.1 free命令的功能 free 命令可以显示系统内存的使用情况&#xff0c;包括物理内存、交换…

SSM高校教师教学质量评估系统-计算机毕业设计源码03344

摘要 在高等教育中&#xff0c;教学质量是培养优秀人才的关键。为了提高教学质量&#xff0c;高校需要建立一套科学、有效的教师教学质量评估系统。本研究采用 SSM技术框架&#xff0c;旨在开发一款高校教师教学质量评估系统。 SSM框架作为一种成熟的Java开发框架&#xff0c;具…

入门PHP就来我这(高级)11 ~ MySQL

有胆量你就来跟着路老师卷起来&#xff01; -- 纯干货&#xff0c;技术知识分享 路老师给大家分享PHP语言的知识了&#xff0c;旨在想让大家入门PHP&#xff0c;并深入了解PHP语言。 1 PHP操作MySQL数据库的方法 PHP操作数据库现在用的多的是mysqli拓展库&#xff0c;mysqli扩…

【Python机器学习】模型评估与改进——多分类指标

多分类问题的所有指标基本是上都来自于二分类问题&#xff0c;但是要对所有类别进行平均。多分类的精度被定义为正确分类的样本所占的比例。同样&#xff0c;如果类别是不平衡的&#xff0c;精度并不是很好的评估度量。 想象一个三分类问题&#xff0c;其中85%的数据点属于类别…

可视化作品集(07):网格化管理领域

网格化管理可视化大屏是指利用大屏幕显示设备&#xff0c;通过数据可视化的方式展示网格化管理的相关信息和指标。网格化管理是一种以网格为基础的城市管理模式&#xff0c;通过将城市划分为不同的网格单元&#xff0c;实现对城市各项管理工作的全覆盖、全时空监控和全过程管理…