【工具】旋转图片-数据集制作工具, 开源!

转载请注明出处:小锋学长生活大爆炸[xfxuezhang.cn]

Github:https://github.com/1061700625/small_tools_v2

        之前做了一个下载百度的旋转图片验证码的工具(多进程下载百度旋转验证码图片-制作数据集),那么拿到了图片数据,就需要手动校正调整来制作数据集,所以出了这个工具。

        效果演示:

        源码:(双击调试信息输出栏有惊喜)

import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk
import os
import pyperclip
import cv2
import numpy as npdef img_crop_process(img):try:img_cv = np.array(img.convert('L'))img_blur = cv2.GaussianBlur(img_cv, (9, 9), 2)circles = cv2.HoughCircles(img_blur, cv2.HOUGH_GRADIENT, dp=1, minDist=20,param1=100, param2=40, minRadius=0, maxRadius=0)if circles is not None:circles = np.uint16(np.around(circles))max_circle = max(circles[0, :], key=lambda x: x[2])  # Select the largest circle by radiusx, y, r = max_circleleft = max(x - r, 0)right = min(x + r, img_cv.shape[1])top = max(y - r, 0)bottom = min(y + r, img_cv.shape[0])# Load the original image in colorcropped_color_img = img.crop((left, top, right, bottom))mask = np.zeros_like(cropped_color_img)cv2.circle(mask, (r, r), r, (255, 255, 255), -1)cropped_color_img_with_white_bg = np.where(mask == (255, 255, 255), cropped_color_img, (255, 255, 255))final_img = Image.fromarray(np.uint8(cropped_color_img_with_white_bg))return final_imgexcept Exception:return imgclass ImageRotatorApp(tk.Tk):def __init__(self):super().__init__()self.title("旋转图像器 by 小锋学长生活大爆炸")self.resizable(False, False)width = 400height = 600self.geometry(f'{width}x{height}')# 计算屏幕中心坐标screen_width = self.winfo_screenwidth()  screen_height = self.winfo_screenheight()x = (screen_width/2) - (width/2)y = (screen_height/2) - (height/2)# 设置窗口中心坐标self.geometry('{}x{}+{}+{}'.format(width, height, int(x), int(y)))# Initialize source and result directoriesself.source_directory = 'images'self.result_directory = 'result'self.original_image = Noneself.rotate_value = 0# Frame for source directory selection and displayself.source_frame = tk.Frame(self)self.source_frame.pack(fill='x')self.select_source_button = tk.Button(self.source_frame, text="选择图像目录", command=self.select_source_directory)self.select_source_button.pack(side='left', padx=10, pady=5)self.source_directory_label = tk.Label(self.source_frame, text=self.source_directory)self.source_directory_label.pack(side='left')# Frame for result directory selection and displayself.result_frame = tk.Frame(self)self.result_frame.pack(fill='x')self.select_result_button = tk.Button(self.result_frame, text="选择保存目录", command=self.select_result_directory)self.select_result_button.pack(side='left', padx=10, pady=5)self.result_directory_label = tk.Label(self.result_frame, text=self.result_directory)self.result_directory_label.pack(side='left')# Image display frameself.img_display = tk.Label(self, width=400, height=400)self.img_display.pack(expand=True, fill='both')# Slider for rotationself.rotation_slider = tk.Scale(self, from_=0, to=360, length=400, orient="horizontal", command=self.rotate_image)self.rotation_slider.pack(fill='x')self.button_frame = tk.Frame(self)self.button_frame.pack(side='bottom', fill='x')self.prev_button = tk.Button(self.button_frame, text="上一张", command=self.load_prev_image)self.prev_button.config(height=2, width=10)  self.prev_button.pack(expand=True, fill='x', side='left', padx=10, pady=5)self.crop_button = tk.Button(self.button_frame, text="自动裁剪", command=self.crop_image)self.crop_button.config(height=2, width=10)  # Make the button largerself.crop_button.pack(expand=True, fill='x', side='left', padx=10, pady=5)# Button to save the imageself.save_button = tk.Button(self.button_frame, text="保存图片", command=self.save_image)self.save_button.config(height=2, width=10)  # Make the button largerself.save_button.pack(expand=True, fill='x', side='left', padx=10, pady=5)# Next image button self.next_button = tk.Button(self.button_frame, text="下一张", command=self.load_next_image) self.next_button.config(height=2, width=10)  # Make the button largerself.next_button.pack(expand=True, fill='x', side='left', padx=10, pady=5)# 信息显示标签  self.info_label = tk.Label(self, text='')self.info_label.config(height=1, width=400, anchor='w', wraplength=0)self.info_label.pack(side='bottom', fill='x')self.info_label.bind('<Double-Button-1>', self.copy_info_label)# Update the display with the first image from the source directoryself.load_first_image_from_source()def show_debug_msg(self, msg):self.info_label.config(text=msg)def copy_info_label(self, event):pyperclip.copy(self.info_label['text'])self.show_debug_msg(">> 双击成功,该内容已复制到剪切板 <<")def select_source_directory(self):directory = filedialog.askdirectory()if directory:  # Update the directory only if a choice was madeself.source_directory = directoryself.source_directory_label.config(text=self.source_directory)self.load_first_image_from_source()def select_result_directory(self):directory = filedialog.askdirectory()if directory:  # Update the directory only if a choice was madeself.result_directory = directoryself.result_directory_label.config(text=self.result_directory)def load_next_image(self):if self.original_image:img_files = [f for f in os.listdir(self.source_directory) if f.endswith(('.png', '.jpg', '.jpeg'))]curr_idx = img_files.index(self.img_name)next_idx = curr_idx + 1 if curr_idx < len(img_files) - 1 else 0next_img_name = img_files[next_idx]self.original_image = Image.open(os.path.join(self.source_directory, next_img_name))self.show_debug_msg(f"当前图片[{next_idx+1}/{len(img_files)}]: {os.path.join(self.source_directory, next_img_name)}")self.image = self.original_imageself.img_name = next_img_nameself.rotation_slider.set(0)self.update_image_display()def load_prev_image(self):if self.original_image:img_files = [f for f in os.listdir(self.source_directory) if f.endswith(('.png', '.jpg', '.jpeg'))]curr_idx = img_files.index(self.img_name)prev_idx = curr_idx - 1 if curr_idx > 0 else len(img_files) - 1prev_img_name = img_files[prev_idx]self.original_image = Image.open(os.path.join(self.source_directory, prev_img_name))self.show_debug_msg(f"当前图片[{prev_idx+1}/{len(img_files)}]: {os.path.join(self.source_directory, prev_img_name)}")self.image = self.original_imageself.img_name = prev_img_nameself.rotation_slider.set(0)self.update_image_display()def load_first_image_from_source(self):try:img_lists = [f for f in os.listdir(self.source_directory) if f.endswith(('.png', '.jpg', '.jpeg'))]self.img_name = img_lists[0] if img_lists else Noneif self.img_name:self.original_image = Image.open(os.path.join(self.source_directory, self.img_name))self.image = self.original_imageself.show_debug_msg(f"当前图片[{1}/{len(img_lists)}]: {os.path.join(self.source_directory, self.img_name)}")else:self.original_image = Noneself.image = Noneself.update_image_display()except FileNotFoundError:self.original_image = Noneself.image = Noneself.img_display.config(image='')self.show_debug_msg(f"'{self.source_directory}'路径下没有图片.")def rotate_image(self, value):if self.original_image:angle = int(value)self.rotate_value = angleself.image = self.original_image.rotate(-angle, resample=Image.Resampling.BILINEAR, fillcolor=(255, 255, 255))self.update_image_display()def crop_image(self):if self.original_image:self.original_image = img_crop_process(self.original_image)self.rotation_slider.set(0)self.update_image_display(self.original_image)def update_image_display(self, src_img=None):src_img = src_img or self.imageif src_img:# 缩放图片image_w, image_h = src_img.sizeif image_w > 400 or image_h > 400: src_img.thumbnail((400,400))self.tk_image = ImageTk.PhotoImage(src_img)self.img_display.config(image=self.tk_image)else:self.img_display.config(image='')def save_image(self):if self.image:# Save the rotated image to the selected result directorysave_path = os.path.join(self.result_directory, self.img_name.split('.')[0] + f'_{self.rotate_value}.' + self.img_name.split('.')[1])self.image.save(save_path)self.show_debug_msg(f"图片保存成功: {save_path}")else:self.show_debug_msg("没有图片要保存.")# Create the 'images' and 'result' directories if they don't exist
os.makedirs('images', exist_ok=True)
os.makedirs('result', exist_ok=True)# Run the app
app = ImageRotatorApp()
app.mainloop()

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flink集群的搭建

1、Flink独立集群模式 1、首先Flink的独立集群模式是不依赖于Hadoop集群。 2、上传压缩包&#xff0c;配置环境&#xff1a; 1、解压&#xff1a; tar -zxvf flink-1.15.2-bin-scala_2.12.tgz2、配置环境变量&#xff1a;vim /etc/profileexport FLINK_HOME/usr/local/soft/fl…

基于机器学习的 ICU 脑血管疾病死亡风险智能预测系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 Wechat / QQ 名片 :) 1. 项目简介 重症患者或重大手术后的患者在重症监护室&#xff08;ICU&#xff09;内通过多种生命支持系统以维持生理功能。患者在ICU 内会被频繁持续的记录生命体征和实验室测量等多种数据。由于高频…

SpringBoot系列之集成Redission入门与实践教程

Redisson是一款基于java开发的开源项目&#xff0c;提供了很多企业级实践&#xff0c;比如分布式锁、消息队列、异步执行等功能。本文基于Springboot2版本集成redisson-spring-boot-starter实现redisson的基本应用 软件环境&#xff1a; JDK 1.8 SpringBoot 2.2.1 Maven 3.2…

CATIA环境编辑器用不了时创建项目快捷方式

CATIA环境编辑器用不了时创建项目快捷方式 一、参考适用情况示例二、 解决步骤(一) 先正确放置winb_64部署包(二) 添加环境文件(三) 修改加入的环境文件(四) 复制本机CATIA快捷方式后重命名(五) 修改快捷方式目标的值 一、参考适用情况示例 二、 解决步骤 (一) 先正确放置winb…

一文掌握 Apache SkyWalking

Apache SkyWalking SkyWalking是一个开源可观测平台&#xff0c;用于收集、分析、聚合和可视化来自服务和云原生基础设施的数据。SkyWalking 提供了一种简单的方法来保持分布式系统的清晰视图&#xff0c;甚至跨云。它是一种现代APM&#xff0c;专为云原生、基于容器的分布式系…

几个月大的幼猫怎么喂?性价比高的幼猫主食罐头推荐

我这些年在经营宠物店的过程中发现&#xff0c;许多铲屎官在猫咪约2个月大时会选择带它们回家喂养。然而&#xff0c;他们可能不知道如何为这个阶段的幼猫提供适当的营养。实际上&#xff0c;幼猫所需的营养成分与成年猫相似&#xff0c;都是高蛋白、中高脂肪和低碳水。而且在这…

【JavaScript】window 对象、location 对象、navigator 对象和 history 对象

1. window 对象 BOM (Browser Object Model ) 是浏览器对象模型&#xff1a; window对象是一个全局对象&#xff0c;也可以说是JavaScript中的顶级对象像document、alert()、console.log()这些都是window的属性&#xff0c;基本BOM的属性和方法都是window的所有通过var定义在全…

AI+BI行业数字化转型研讨会 - 总结精华回顾

带您一起观看研讨会精彩内容回顾&#xff01; || 导语 AIBI行业数字化转型研讨会—引领未来&#xff0c;智慧转型 德昂信息技术(北京)有限公司于2023年10月26日成功举办了AIBI行业数字化转型研讨会。此次盛会汇聚了产业精英、企业领袖以及技术专家&#xff0c;共同探讨在快速…

pycharm pro v2023.2.4(Python开发)

PyCharm是一种Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;PyCharm提供了强大的功能&#xff0c;包括语法突出显示、智能代码完成、代码检查、自动重构和调试等特性&#xff0c;这些都可以帮助Python开发人员更加高效地编写代码。 PyCharm Pro是PyCharm的高级版…

性能测试怎么入门?一文7个知识点带你成功入门!

一、相关概念 1、性能测试相关&#xff1a;负载测试&#xff0c;性能测试&#xff0c;压力测试&#xff0c;稳定性测试&#xff0c;全链路测试等 2、性能指标&#xff1a;吞吐率&#xff0c;tps&#xff0c;并发用户数&#xff0c;吞吐量&#xff0c;响应时间等 二、性能测试…

word办公小技巧:方框打勾、上下标、横隔线、排序

Word文件制作过程中&#xff0c;需要了解一些可以提高效率的小技巧帮助我们能够更快的完成工作&#xff0c;今天分享四个提高效率的小技巧 技巧一&#xff1a;方框内打√ 想要在word文件中设置出方框内√&#xff0c;的效果&#xff0c;在word文件中输入&#xff1a; ☑&…

cortex-A7核IIC实验--采集温湿度传感器的值

1.IIC总线---同步串行半双工 1&#xff09;一根数据线SDA--PF15&#xff0c;一根时钟线SCL--PF14 2&#xff09;传输速率&#xff1a;低速&#xff1a;100K 中速&#xff1a;400K 全速&#xff1a;3.4M 3&#xff09;外接两个上拉电阻的作用&#xff1a;总线空闲状态时&am…

图神经网络(GNN)性能优化方案汇总,附37个配套算法模型和代码

图神经网络的表达能力对其性能和应用范围有着重要的影响&#xff0c;是GNN研究的核心问题和发展方向。增强表达能力是扩展GNN应用范围、提高性能的关键所在。 目前GNN的表达能力受特征表示和拓扑结构这两个因素的影响&#xff0c;其中GNN在学习和保持图拓扑方面的缺陷是限制表…

esxi 6.7下安装黑裙

esxi上创建一个黑裙系统的虚拟机&#xff0c;用来存资料 一、工具 硬件&#xff1a; 工控机&#xff1a;装有esxi6.7系统&#xff08;192.168.100.2&#xff09;&#xff0c;配置&#xff1a;3865U&#xff0c;16G内存&#xff0c;120Gmsata120sata硬盘&#xff0c;6个网口 主…

JavaEE-博客系统3(功能设计)

本部分内容为&#xff1a;实现登录功能&#xff1b;强制要求用户登录&#xff1b;实现显示用户信息&#xff1b;退出登录&#xff1b;发布博客 该部分的后端代码如下&#xff1a; Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws Ser…

Harmony OS—UIAbility的使用

概述 UIAbility是一种包含用户界面的应用组件&#xff0c;主要用于和用户进行交互。UIAbility也是系统调度的单元&#xff0c;为应用提供窗口在其中绘制界面。一个应用可以有一个UIAbility&#xff0c;也可以有多个UIAbility&#xff0c;类似于Android 的 Activity&#xff0c…

微服务-grpc

微服务 一、微服务&#xff08;microservices&#xff09; 近几年,微服这个词闯入了我们的视线范围。在百度与谷歌中随便搜一搜也有几千万条的结果。那么&#xff0c;什么是微服务 呢&#xff1f;微服务的概念是怎么产生的呢&#xff1f; 我们就来了解一下Go语言与微服务的千丝…

ConcurrentHashMap 源码解析

目录 一. 前言 二. 源码解析 2.1. 类结构 2.2. 基本属性 2.3. 构造方法 2.4. 增加元素 2.4.1. initTable() 2.4.2. helpTransfer() 2.4.3. transfer() 2.4.4. treeifyBin() 2.4.5. addCount() 2.5. 获取元素 2.6. remove() & replace() 2.7. clear() 2.8. s…

Leetcode—2578.最小和分割【简单】

2023每日刷题&#xff08;二十三&#xff09; Leetcode—2578.最小和分割 实现代码 class Solution { public:int splitNum(int num) {vector<int> a;while(num) {a.push_back(num % 10);num / 10;}int n a.size();sort(a.begin(), a.begin() n);int num1 0;int num…

AI全栈大模型工程师(十九)Semantic Kernel

文章目录 Semantic KernelSK 的开发进展SK 的生态位SK 基础架构后记 Semantic Kernel 先比较下 Semantic Kernel 和 LangChain。 Semantic KernelLangChain出品公司微软LangChain AI支持语言Python、C#、Java、TypeScriptPython、TypeScript开源协议MITMIT被应用在Microsoft …