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


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

    Githubhttps://github.com/1061700625/small_tools_v2

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

            效果演示:

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

    1. import tkinter as tk
    2. from tkinter import filedialog
    3. from PIL import Image, ImageTk
    4. import os
    5. import pyperclip
    6. import cv2
    7. import numpy as np
    8. def img_crop_process(img):
    9. try:
    10. img_cv = np.array(img.convert('L'))
    11. img_blur = cv2.GaussianBlur(img_cv, (9, 9), 2)
    12. circles = cv2.HoughCircles(img_blur, cv2.HOUGH_GRADIENT, dp=1, minDist=20,
    13. param1=100, param2=40, minRadius=0, maxRadius=0)
    14. if circles is not None:
    15. circles = np.uint16(np.around(circles))
    16. max_circle = max(circles[0, :], key=lambda x: x[2]) # Select the largest circle by radius
    17. x, y, r = max_circle
    18. left = max(x - r, 0)
    19. right = min(x + r, img_cv.shape[1])
    20. top = max(y - r, 0)
    21. bottom = min(y + r, img_cv.shape[0])
    22. # Load the original image in color
    23. cropped_color_img = img.crop((left, top, right, bottom))
    24. mask = np.zeros_like(cropped_color_img)
    25. cv2.circle(mask, (r, r), r, (255, 255, 255), -1)
    26. cropped_color_img_with_white_bg = np.where(mask == (255, 255, 255), cropped_color_img, (255, 255, 255))
    27. final_img = Image.fromarray(np.uint8(cropped_color_img_with_white_bg))
    28. return final_img
    29. except Exception:
    30. return img
    31. class ImageRotatorApp(tk.Tk):
    32. def __init__(self):
    33. super().__init__()
    34. self.title("旋转图像器 by 小锋学长生活大爆炸")
    35. self.resizable(False, False)
    36. width = 400
    37. height = 600
    38. self.geometry(f'{width}x{height}')
    39. # 计算屏幕中心坐标
    40. screen_width = self.winfo_screenwidth()
    41. screen_height = self.winfo_screenheight()
    42. x = (screen_width/2) - (width/2)
    43. y = (screen_height/2) - (height/2)
    44. # 设置窗口中心坐标
    45. self.geometry('{}x{}+{}+{}'.format(width, height, int(x), int(y)))
    46. # Initialize source and result directories
    47. self.source_directory = 'images'
    48. self.result_directory = 'result'
    49. self.original_image = None
    50. self.rotate_value = 0
    51. # Frame for source directory selection and display
    52. self.source_frame = tk.Frame(self)
    53. self.source_frame.pack(fill='x')
    54. self.select_source_button = tk.Button(self.source_frame, text="选择图像目录", command=self.select_source_directory)
    55. self.select_source_button.pack(side='left', padx=10, pady=5)
    56. self.source_directory_label = tk.Label(self.source_frame, text=self.source_directory)
    57. self.source_directory_label.pack(side='left')
    58. # Frame for result directory selection and display
    59. self.result_frame = tk.Frame(self)
    60. self.result_frame.pack(fill='x')
    61. self.select_result_button = tk.Button(self.result_frame, text="选择保存目录", command=self.select_result_directory)
    62. self.select_result_button.pack(side='left', padx=10, pady=5)
    63. self.result_directory_label = tk.Label(self.result_frame, text=self.result_directory)
    64. self.result_directory_label.pack(side='left')
    65. # Image display frame
    66. self.img_display = tk.Label(self, width=400, height=400)
    67. self.img_display.pack(expand=True, fill='both')
    68. # Slider for rotation
    69. self.rotation_slider = tk.Scale(self, from_=0, to=360, length=400, orient="horizontal", command=self.rotate_image)
    70. self.rotation_slider.pack(fill='x')
    71. self.button_frame = tk.Frame(self)
    72. self.button_frame.pack(side='bottom', fill='x')
    73. self.prev_button = tk.Button(self.button_frame, text="上一张", command=self.load_prev_image)
    74. self.prev_button.config(height=2, width=10)
    75. self.prev_button.pack(expand=True, fill='x', side='left', padx=10, pady=5)
    76. self.crop_button = tk.Button(self.button_frame, text="自动裁剪", command=self.crop_image)
    77. self.crop_button.config(height=2, width=10) # Make the button larger
    78. self.crop_button.pack(expand=True, fill='x', side='left', padx=10, pady=5)
    79. # Button to save the image
    80. self.save_button = tk.Button(self.button_frame, text="保存图片", command=self.save_image)
    81. self.save_button.config(height=2, width=10) # Make the button larger
    82. self.save_button.pack(expand=True, fill='x', side='left', padx=10, pady=5)
    83. # Next image button
    84. self.next_button = tk.Button(self.button_frame, text="下一张", command=self.load_next_image)
    85. self.next_button.config(height=2, width=10) # Make the button larger
    86. self.next_button.pack(expand=True, fill='x', side='left', padx=10, pady=5)
    87. # 信息显示标签
    88. self.info_label = tk.Label(self, text='')
    89. self.info_label.config(height=1, width=400, anchor='w', wraplength=0)
    90. self.info_label.pack(side='bottom', fill='x')
    91. self.info_label.bind('', self.copy_info_label)
    92. # Update the display with the first image from the source directory
    93. self.load_first_image_from_source()
    94. def show_debug_msg(self, msg):
    95. self.info_label.config(text=msg)
    96. def copy_info_label(self, event):
    97. pyperclip.copy(self.info_label['text'])
    98. self.show_debug_msg(">> 双击成功,该内容已复制到剪切板 <<")
    99. def select_source_directory(self):
    100. directory = filedialog.askdirectory()
    101. if directory: # Update the directory only if a choice was made
    102. self.source_directory = directory
    103. self.source_directory_label.config(text=self.source_directory)
    104. self.load_first_image_from_source()
    105. def select_result_directory(self):
    106. directory = filedialog.askdirectory()
    107. if directory: # Update the directory only if a choice was made
    108. self.result_directory = directory
    109. self.result_directory_label.config(text=self.result_directory)
    110. def load_next_image(self):
    111. if self.original_image:
    112. img_files = [f for f in os.listdir(self.source_directory) if f.endswith(('.png', '.jpg', '.jpeg'))]
    113. curr_idx = img_files.index(self.img_name)
    114. next_idx = curr_idx + 1 if curr_idx < len(img_files) - 1 else 0
    115. next_img_name = img_files[next_idx]
    116. self.original_image = Image.open(os.path.join(self.source_directory, next_img_name))
    117. self.show_debug_msg(f"当前图片[{next_idx+1}/{len(img_files)}]: {os.path.join(self.source_directory, next_img_name)}")
    118. self.image = self.original_image
    119. self.img_name = next_img_name
    120. self.rotation_slider.set(0)
    121. self.update_image_display()
    122. def load_prev_image(self):
    123. if self.original_image:
    124. img_files = [f for f in os.listdir(self.source_directory) if f.endswith(('.png', '.jpg', '.jpeg'))]
    125. curr_idx = img_files.index(self.img_name)
    126. prev_idx = curr_idx - 1 if curr_idx > 0 else len(img_files) - 1
    127. prev_img_name = img_files[prev_idx]
    128. self.original_image = Image.open(os.path.join(self.source_directory, prev_img_name))
    129. self.show_debug_msg(f"当前图片[{prev_idx+1}/{len(img_files)}]: {os.path.join(self.source_directory, prev_img_name)}")
    130. self.image = self.original_image
    131. self.img_name = prev_img_name
    132. self.rotation_slider.set(0)
    133. self.update_image_display()
    134. def load_first_image_from_source(self):
    135. try:
    136. img_lists = [f for f in os.listdir(self.source_directory) if f.endswith(('.png', '.jpg', '.jpeg'))]
    137. self.img_name = img_lists[0] if img_lists else None
    138. if self.img_name:
    139. self.original_image = Image.open(os.path.join(self.source_directory, self.img_name))
    140. self.image = self.original_image
    141. self.show_debug_msg(f"当前图片[{1}/{len(img_lists)}]: {os.path.join(self.source_directory, self.img_name)}")
    142. else:
    143. self.original_image = None
    144. self.image = None
    145. self.update_image_display()
    146. except FileNotFoundError:
    147. self.original_image = None
    148. self.image = None
    149. self.img_display.config(image='')
    150. self.show_debug_msg(f"'{self.source_directory}'路径下没有图片.")
    151. def rotate_image(self, value):
    152. if self.original_image:
    153. angle = int(value)
    154. self.rotate_value = angle
    155. self.image = self.original_image.rotate(-angle, resample=Image.Resampling.BILINEAR, fillcolor=(255, 255, 255))
    156. self.update_image_display()
    157. def crop_image(self):
    158. if self.original_image:
    159. self.original_image = img_crop_process(self.original_image)
    160. self.rotation_slider.set(0)
    161. self.update_image_display(self.original_image)
    162. def update_image_display(self, src_img=None):
    163. src_img = src_img or self.image
    164. if src_img:
    165. # 缩放图片
    166. image_w, image_h = src_img.size
    167. if image_w > 400 or image_h > 400:
    168. src_img.thumbnail((400,400))
    169. self.tk_image = ImageTk.PhotoImage(src_img)
    170. self.img_display.config(image=self.tk_image)
    171. else:
    172. self.img_display.config(image='')
    173. def save_image(self):
    174. if self.image:
    175. # Save the rotated image to the selected result directory
    176. save_path = os.path.join(self.result_directory, self.img_name.split('.')[0] + f'_{self.rotate_value}.' + self.img_name.split('.')[1])
    177. self.image.save(save_path)
    178. self.show_debug_msg(f"图片保存成功: {save_path}")
    179. else:
    180. self.show_debug_msg("没有图片要保存.")
    181. # Create the 'images' and 'result' directories if they don't exist
    182. os.makedirs('images', exist_ok=True)
    183. os.makedirs('result', exist_ok=True)
    184. # Run the app
    185. app = ImageRotatorApp()
    186. app.mainloop()

  • 相关阅读:
    尚硅谷 谷粒学院 毕业设计 在线教育 部署文档
    力扣热题100_二叉树_98_验证二叉搜索树
    智能计量插座多角色定位:用电监测&资产管理的智能化强大运用
    ComponentAce FlexCompress强大功能
    动手学深度学习(pytorch)2
    一招教你控制python多线程的线程数量
    如果手机被偷了,里面的微信和支付宝绑定了银行卡,该怎么办?
    如何在Git中修改远程仓库地址
    那些你不知道入过万风口的项目,抖音小店到底有多靠谱,有多挣钱
    第五章. 可视化数据分析分析图表—常用图表的绘制2—直方图,饼形图
  • 原文地址:https://blog.csdn.net/sxf1061700625/article/details/134300561