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


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

  • 相关阅读:
    架构每日一学 15:想要提升协作效率,必须先统一语义
    【Rust日报】2022-08-02 hypher 将30种自然语言塞到1.1M空间内
    《数字经济 科技向善》大咖对谈干货来啦
    高级IO-epoll
    REDIS学习笔记(一):基础与安装
    Axure教程—单色折线图(中继器)
    阿里p9架构专家总结的(OracleRAG:集群+高可用性+备份与恢复)看完就一个字“牛”
    axios传递数组参数,后台接收不到
    企业网站搭建需要多少钱呢?成本差距体现在哪里?
    国民MCU_freertos V10.3.1 使用经验避坑总结
  • 原文地址:https://blog.csdn.net/sxf1061700625/article/details/134300561