当前位置:   article > 正文

python lama-cleaner批量去水印_lama 批量处理

lama 批量处理

用lama-cleaner进行批量去水印,去除视频水印

lama-cleaner移除图片水印大概流程:先在本地创建一个服务,然后通过浏览器标注需要处理的水印,然后发送请求,本地进行处理,并返回。因此,视频去水印的原理就是先提取视频的每一帧,然后依次发送给lama-cleaner进行处理,最终再合成一个视频。(个人理解)

创作灵感来自:

https://github.com/david419kr/video-watermark-removal-script-for-lama-cleaner

一、安装lama-cleaner

pip install lama-cleaner

一、代码思路

1.启动服务,在pycharm的终端里输入以下内容

lama-cleaner --model=lama --device=cpu --port=8080

2.判断文件是类型,图片、视频、其他 

file_type = is_video_or_image(self.file_path)

3.获取视频中的一张图片,用来标注水印的位置

  1. def get_mask_img(self):
  2. cap = cv2.VideoCapture(self.file_path)
  3. # 设置视频帧指针到第100帧
  4. cap.set(cv2.CAP_PROP_POS_FRAMES, 99) # 注意:帧计数从0开始,所以第100帧是第99索引
  5. success, frame = cap.read() # 读取当前帧(第100帧)
  6. if success: # 确保成功读取帧
  7. cv2.imwrite('mask_frame.png', frame)
  8. else:
  9. print("Failed to retrieve frame.")
  10. cap.release()
  11. input('mask修改好了,请回车')
  12. self.mask = open('mask_frame.png', "rb").read()

mask_frame.png获取到后,用ps或其他图片软件将需要去除的水印区域用白色填充,图片的其他部分用黑色填充。

原图

处理后的

4.将视频提取为每一帧

  1. def video_to_frames(self):
  2. # 确保存放帧的目录存在
  3. if not os.path.exists(self.frames_path):
  4. os.makedirs(self.frames_path)
  5. else:
  6. recreate_folder(self.frames_path)
  7. if not os.path.exists(self.frames_out_folder):
  8. os.makedirs(self.frames_out_folder)
  9. # 使用OpenCV读取视频
  10. vidcap = cv2.VideoCapture(self.file_path)
  11. # 获取视频的总帧数
  12. self.fps = int(vidcap.get(cv2.CAP_PROP_FPS))
  13. success, image = vidcap.read()
  14. count = 0
  15. while success:
  16. # 保存当前帧图片
  17. frame_path = os.path.join(self.frames_path, f"{str(count).zfill(8)}.jpg")
  18. #cv2.imwrite(frame_path, image)不能保存中文路径
  19. cv2.imencode('.jpg', image)[1].tofile(frame_path) # 存储成功
  20. success, image = vidcap.read() # 读取下一帧
  21. count += 1
  22. print(f"Finished! Extracted {count} frames.")

5.提取音频

  1. def extract_audio_from_video(self):
  2. """
  3. 从视频文件中提取音频并保存。
  4. 参数:
  5. file_path (str): 视频文件的路径。
  6. audio_path (str): 要保存的音频文件的路径。
  7. """
  8. if not os.path.exists(self.audio_path):
  9. os.makedirs(self.audio_path)
  10. video_clip = VideoFileClip(self.file_path)
  11. audio_clip = video_clip.audio
  12. audio_clip.write_audiofile(os.path.join(self.audio_path, 'out.mp3'))
  13. audio_clip.close()
  14. video_clip.close()

6.开始处理水印

  1. for i in tqdm(os.listdir(self.frames_path), desc="Processing frames"):
  2. self.removeWatermark(img_path=os.path.join(self.frames_path, i), mask=self.mask, image_name=i)
  1. def removeWatermark(self, img_path, mask, image_name):
  2. url = "http://127.0.0.1:8080/inpaint"
  3. # print(img_path)
  4. image = open(img_path, "rb").read()
  5. response = requests.post(
  6. url,
  7. files={"image": image, "mask": mask},
  8. data={
  9. "ldmSteps": 25,
  10. "ldmSampler": "plms",
  11. "hdStrategy": "Crop",
  12. "zitsWireframe": True,
  13. "hdStrategyCropMargin": 196,
  14. "hdStrategyCropTrigerSize": 800,
  15. "hdStrategyResizeLimit": 2048,
  16. "prompt": "",
  17. "negativePrompt": "",
  18. "useCroper": False,
  19. "croperX": 64,
  20. "croperY": -16,
  21. "croperHeight": 512,
  22. "croperWidth": 512,
  23. "sdScale": 1.0,
  24. "sdMaskBlur": 5,
  25. "sdStrength": 0.75,
  26. "sdSteps": 50,
  27. "sdGuidanceScale": 7.5,
  28. "sdSampler": "uni_pc",
  29. "sdSeed": -1,
  30. "sdMatchHistograms": False,
  31. "cv2Flag": "INPAINT_NS",
  32. "cv2Radius": 5,
  33. "paintByExampleSteps": 50,
  34. "paintByExampleGuidanceScale": 7.5,
  35. "paintByExampleMaskBlur": 5,
  36. "paintByExampleSeed": -1,
  37. "paintByExampleMatchHistograms": False,
  38. "paintByExampleExampleImage": None,
  39. "p2pSteps": 50,
  40. "p2pImageGuidanceScale": 1.5,
  41. "p2pGuidanceScale": 7.5,
  42. "controlnet_conditioning_scale": 0.4,
  43. "controlnet_method": "control_v11p_sd15_canny",
  44. "paint_by_example_example_image": None,
  45. },
  46. )
  47. with open(f'{self.frames_out_folder}/{image_name}', "wb") as f:
  48. f.write(response.content)
  49. # print(image_name)

7.合成视频

  1. def frames_to_video(self):
  2. """
  3. 将指定文件夹内的图像帧合并成视频文件。
  4. 参数:
  5. - input_folder: 包含图像帧的文件夹路径。
  6. - output_video_file: 输出视频文件的路径。
  7. - fps: 视频的帧率(每秒帧数)。
  8. """
  9. output_video_file = os.path.join(self.video_folder, 'out.mp4')
  10. if not os.path.exists(self.video_folder):
  11. os.makedirs(self.video_folder)
  12. # 获取文件夹内所有文件的列表,并排序(确保顺序正确)
  13. frame_files = sorted([os.path.join(self.frames_out_folder, f) for f in os.listdir(self.frames_out_folder) if
  14. os.path.isfile(os.path.join(self.frames_out_folder, f))])
  15. # 读取第一帧以获取视频尺寸
  16. frame = cv2.imread(frame_files[0])
  17. height, width, layers = frame.shape
  18. # 定义视频编码器和创建VideoWriter对象
  19. fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 或者使用 'XVID',根据输出格式而定
  20. video = cv2.VideoWriter(output_video_file, fourcc, self.fps, (width, height))
  21. print(output_video_file)
  22. # 遍历所有帧,添加到视频中
  23. for file in frame_files:
  24. frame = cv2.imread(file)
  25. video.write(frame)
  26. # 释放VideoWriter对象
  27. video.release()

8.音频和视频合并

  1. def merge_audio_video(self):
  2. """
  3. 合并音频和视频文件。
  4. 参数:
  5. video_file_path (str): 视频文件的路径。
  6. audio_file_path (str): 音频文件的路径。
  7. output_file_path (str): 输出视频文件的路径。
  8. """
  9. video_file_path = os.path.join(self.video_folder, 'out.mp4')
  10. audio_file_path = os.path.join(self.audio_path, 'out.mp3')
  11. name = os.path.basename(self.file_path)
  12. output_file_path = os.path.join(self.result_folder, name)
  13. if not os.path.exists(self.result_folder):
  14. os.makedirs(self.result_folder)
  15. # 加载视频文件
  16. video_clip = VideoFileClip(video_file_path)
  17. # 加载音频文件
  18. audio_clip = AudioFileClip(audio_file_path)
  19. # 设置视频的音频为加载的音频文件
  20. final_clip = video_clip.set_audio(audio_clip)
  21. # 导出合并后的视频文件
  22. print(output_file_path)
  23. final_clip.write_videofile(output_file_path, codec='libx264', audio_codec='aac')

二、完整的批量处理代码

  1. # lama-cleaner --model=lama --device=cpu --port=8080
  2. import cv2
  3. import requests
  4. import shutil
  5. from moviepy.editor import VideoFileClip, AudioFileClip
  6. import os
  7. from tqdm import tqdm
  8. def recreate_folder(folder_path):
  9. try:
  10. shutil.rmtree(folder_path) # 删除文件夹及其所有内容
  11. os.makedirs(folder_path) # 创建新的同名文件夹
  12. except Exception as e:
  13. print('Failed to delete and recreate folder. Reason: %s' % e)
  14. def is_video_or_image(file_path):
  15. video_extensions = ['.mp4', '.mov', '.avi', '.mkv']
  16. image_extensions = ['.jpg', '.jpeg', '.png', '.gif']
  17. _, file_extension = os.path.splitext(file_path)
  18. if file_extension.lower() in video_extensions:
  19. return 'video'
  20. elif file_extension.lower() in image_extensions:
  21. return 'image'
  22. else:
  23. return 'unknown'
  24. class removeVideoWatermark():
  25. def __init__(self, file_path):
  26. self.mask = None
  27. self.file_path = file_path
  28. self.fps = 30
  29. folder_name = os.path.basename(self.file_path).split('.')[0]
  30. self.tmp_folder = os.path.join('C:\\Users\\35785\\Desktop\\tmp', folder_name)
  31. self.frames_path = os.path.join('C:\\Users\\35785\\Desktop\\tmp', folder_name, 'frames')
  32. self.audio_path = os.path.join('C:\\Users\\35785\\Desktop\\tmp', folder_name, 'audio')
  33. self.frames_out_folder = os.path.join('C:\\Users\\35785\\Desktop\\tmp', folder_name, 'frames_out')
  34. self.video_folder = os.path.join('C:\\Users\\35785\\Desktop\\tmp', folder_name, 'video')
  35. self.result_folder = os.path.join('C:\\Users\\35785\\Desktop\\tmp', folder_name, 'result')
  36. def start(self):
  37. file_type = is_video_or_image(self.file_path)
  38. if file_type == 'image':
  39. # 待完善
  40. pass
  41. elif file_type == 'video':
  42. self.get_mask_img()
  43. self.video_to_frames()
  44. self.extract_audio_from_video()
  45. # 不使用多线程
  46. for i in tqdm(os.listdir(self.frames_path), desc="Processing frames"):
  47. self.removeWatermark(img_path=os.path.join(self.frames_path, i), mask=self.mask, image_name=i)
  48. self.frames_to_video()
  49. self.merge_audio_video()
  50. else:
  51. pass
  52. def get_mask_img(self):
  53. cap = cv2.VideoCapture(self.file_path)
  54. # 设置视频帧指针到第100帧
  55. cap.set(cv2.CAP_PROP_POS_FRAMES, 99) # 注意:帧计数从0开始,所以第100帧是第99索引
  56. success, frame = cap.read() # 读取当前帧(第100帧)
  57. if success: # 确保成功读取帧
  58. cv2.imwrite('mask_frame.png', frame)
  59. else:
  60. print("Failed to retrieve frame.")
  61. cap.release()
  62. input('mask修改好了,请回车')
  63. self.mask = open('mask_frame.png', "rb").read()
  64. def merge_audio_video(self):
  65. """
  66. 合并音频和视频文件。
  67. 参数:
  68. video_file_path (str): 视频文件的路径。
  69. audio_file_path (str): 音频文件的路径。
  70. output_file_path (str): 输出视频文件的路径。
  71. """
  72. video_file_path = os.path.join(self.video_folder, 'out.mp4')
  73. audio_file_path = os.path.join(self.audio_path, 'out.mp3')
  74. name = os.path.basename(self.file_path)
  75. output_file_path = os.path.join(self.result_folder, name)
  76. if not os.path.exists(self.result_folder):
  77. os.makedirs(self.result_folder)
  78. # 加载视频文件
  79. video_clip = VideoFileClip(video_file_path)
  80. # 加载音频文件
  81. audio_clip = AudioFileClip(audio_file_path)
  82. # 设置视频的音频为加载的音频文件
  83. final_clip = video_clip.set_audio(audio_clip)
  84. # 导出合并后的视频文件
  85. print(output_file_path)
  86. final_clip.write_videofile(output_file_path, codec='libx264', audio_codec='aac')
  87. def extract_audio_from_video(self):
  88. """
  89. 从视频文件中提取音频并保存。
  90. 参数:
  91. file_path (str): 视频文件的路径。
  92. audio_path (str): 要保存的音频文件的路径。
  93. """
  94. if not os.path.exists(self.audio_path):
  95. os.makedirs(self.audio_path)
  96. video_clip = VideoFileClip(self.file_path)
  97. audio_clip = video_clip.audio
  98. audio_clip.write_audiofile(os.path.join(self.audio_path, 'out.mp3'))
  99. audio_clip.close()
  100. video_clip.close()
  101. def video_to_frames(self):
  102. # 确保存放帧的目录存在
  103. if not os.path.exists(self.frames_path):
  104. os.makedirs(self.frames_path)
  105. else:
  106. recreate_folder(self.frames_path)
  107. if not os.path.exists(self.frames_out_folder):
  108. os.makedirs(self.frames_out_folder)
  109. # 使用OpenCV读取视频
  110. vidcap = cv2.VideoCapture(self.file_path)
  111. # 获取视频的总帧数
  112. self.fps = int(vidcap.get(cv2.CAP_PROP_FPS))
  113. success, image = vidcap.read()
  114. count = 0
  115. while success:
  116. # 保存当前帧图片
  117. frame_path = os.path.join(self.frames_path, f"{str(count).zfill(8)}.jpg")
  118. # cv2.imwrite(frame_path, image)中文路径会保存不了
  119. cv2.imencode('.jpg', image)[1].tofile(frame_path) # 存储成功
  120. success, image = vidcap.read() # 读取下一帧
  121. count += 1
  122. print(f"Finished! Extracted {count} frames.")
  123. def removeWatermark(self, img_path, mask, image_name):
  124. url = "http://127.0.0.1:8080/inpaint"
  125. # print(img_path)
  126. image = open(img_path, "rb").read()
  127. response = requests.post(
  128. url,
  129. files={"image": image, "mask": mask},
  130. data={
  131. "ldmSteps": 25,
  132. "ldmSampler": "plms",
  133. "hdStrategy": "Crop",
  134. "zitsWireframe": True,
  135. "hdStrategyCropMargin": 196,
  136. "hdStrategyCropTrigerSize": 800,
  137. "hdStrategyResizeLimit": 2048,
  138. "prompt": "",
  139. "negativePrompt": "",
  140. "useCroper": False,
  141. "croperX": 64,
  142. "croperY": -16,
  143. "croperHeight": 512,
  144. "croperWidth": 512,
  145. "sdScale": 1.0,
  146. "sdMaskBlur": 5,
  147. "sdStrength": 0.75,
  148. "sdSteps": 50,
  149. "sdGuidanceScale": 7.5,
  150. "sdSampler": "uni_pc",
  151. "sdSeed": -1,
  152. "sdMatchHistograms": False,
  153. "cv2Flag": "INPAINT_NS",
  154. "cv2Radius": 5,
  155. "paintByExampleSteps": 50,
  156. "paintByExampleGuidanceScale": 7.5,
  157. "paintByExampleMaskBlur": 5,
  158. "paintByExampleSeed": -1,
  159. "paintByExampleMatchHistograms": False,
  160. "paintByExampleExampleImage": None,
  161. "p2pSteps": 50,
  162. "p2pImageGuidanceScale": 1.5,
  163. "p2pGuidanceScale": 7.5,
  164. "controlnet_conditioning_scale": 0.4,
  165. "controlnet_method": "control_v11p_sd15_canny",
  166. "paint_by_example_example_image": None,
  167. },
  168. )
  169. with open(f'{self.frames_out_folder}/{image_name}', "wb") as f:
  170. f.write(response.content)
  171. # print(image_name)
  172. def frames_to_video(self):
  173. """
  174. 将指定文件夹内的图像帧合并成视频文件。
  175. 参数:
  176. - input_folder: 包含图像帧的文件夹路径。
  177. - output_video_file: 输出视频文件的路径。
  178. - fps: 视频的帧率(每秒帧数)。
  179. """
  180. output_video_file = os.path.join(self.video_folder, 'out.mp4')
  181. if not os.path.exists(self.video_folder):
  182. os.makedirs(self.video_folder)
  183. # 获取文件夹内所有文件的列表,并排序(确保顺序正确)
  184. frame_files = sorted([os.path.join(self.frames_out_folder, f) for f in os.listdir(self.frames_out_folder) if
  185. os.path.isfile(os.path.join(self.frames_out_folder, f))])
  186. # 读取第一帧以获取视频尺寸
  187. frame = cv2.imread(frame_files[0])
  188. height, width, layers = frame.shape
  189. # 定义视频编码器和创建VideoWriter对象
  190. fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 或者使用 'XVID',根据输出格式而定
  191. video = cv2.VideoWriter(output_video_file, fourcc, self.fps, (width, height))
  192. print(output_video_file)
  193. # 遍历所有帧,添加到视频中
  194. for file in frame_files:
  195. frame = cv2.imread(file)
  196. video.write(frame)
  197. # 释放VideoWriter对象
  198. video.release()
  199. a = removeVideoWatermark(file_path=r'C:\Users\35785\Downloads\Video\tmp.mp4')
  200. a.start()
总结

提示:没有用多线程,处理起来可能慢。可以修改请求参数,选用不同的处理模型。

三、补充

不同模型请求参数的获取。

我用的是比较笨的方法,没有去深究,能用就行

lama-cleaner运行时自动会创建一个网页操作界面的,我是通过网页调试看每次处理图像,他发送的请求得知的参数。

先启动lama-cleaner,在终端运行

lama-cleaner --model=lama --device=cpu --port=8080

访问 http://127.0.0.1:8080,在设置里选择好自己想要的模型,然后随便选择一张图片,打开网页调试中的网络,然后随便在图片上涂抹一下,进行处理,在调试窗口就可以看到相应的请求参数。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/949034
推荐阅读
相关标签
  

闽ICP备14008679号