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


    转载请注明出处:小锋学长生活大爆炸[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()

  • 相关阅读:
    ikd-Tree:增量KD树在机器人中的应用
    候选键的确定方法-如何判断属性集U的子集K是否为候选键、如何找到关系模式的候选键
    vue-主题切换
    【Linux】Linux常用命令—文件管理(下)
    linux脚本执行docker容器命令
    LeetCode每日一题 分发糖果
    react中路由版本问题
    链表OJ
    C#调用RabbitMQ实现消息队列
    【JavaEE】Java的文件IO
  • 原文地址:https://blog.csdn.net/sxf1061700625/article/details/134300561