Loading... 例如图片:![nQAXglITUPEI.gif](http://type.zimopy.com/usr/uploads/2024/09/1220571118.gif) 像这种图片,打码就是真的恶心,我尝试了市面上开源的打码都不行,如果用付费平台,gif图片打码很贵,而且我没找到有gif的打码,所以咋整呢?请看下面。 ## 思路 gif图片没办法直接识别,而且看上面的图片,有规律的,第一次是后面四位,第二次是第一位和最后三位,最后一次是前面四位,虽然每次变动位置和大小都有变化,但是排列都是正常的。 所有要识别这种验证码,**就是吧gif先转成png,解析完发现一共有五张png图片,我们只需要第一张和最后一张,再把识别到的内容合并,那就是一个整体了**。 ## 代码如下: ### 第一种使用:StupidOCR 这里的本地打码项目使用的是:`https://github.com/81NewArk/StupidOCR` ```python #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2024/9/12 09:33 # @Author : huoyu # @email : 2319899766@qq.com # @File : ai_gif.py # @Project : verifi_number # @Software: PyCharm import requests from PIL import Image import io import re import base64 from concurrent.futures import ThreadPoolExecutor def down_gif(url): res = requests.get(url).content return res # Return GIF binary content def extract_frames(gif_content): # Handle GIF content in memory using io.BytesIO gif = Image.open(io.BytesIO(gif_content)) frames = [] try: while True: frame_image = gif.copy() # Copy the current frame frames.append(frame_image) gif.seek(gif.tell() + 1) # Move to the next frame except EOFError: pass # print(f"GIF extraction completed, {len(frames)} frames extracted.") return frames def process_frame(frame): # Convert frame to base64 without saving to disk with io.BytesIO() as output: frame.save(output, format="PNG") image_data = output.getvalue() # Get in-memory image data img_base64 = base64.b64encode(image_data).decode('utf-8') return img_base64 # 传递图片的base64进行本地打码 def test_get(img_base64): # Flask application's address and port url = "http://192.168.2.39:6688/api/ocr/image" headers = { 'accept': 'application/json', 'Content-Type': 'application/json', } json_data = { 'img_base64': img_base64, } response = requests.post(url, headers=headers, json=json_data) return response.json()["result"] #合并字符串 def merge_strings(str1, str2): # 去除中文字符和符号 str1 = re.sub(r'[\u4e00-\u9fff\W]+', '', str1) str2 = re.sub(r'[\u4e00-\u9fff\W]+', '', str2) lower_str1 = str1.lower() lower_str2 = str2.lower() for i in range(min(len(lower_str1), len(lower_str2)), 0, -1): if lower_str1.endswith(lower_str2[:i]): return (str1 + str2[i:]).lower() return (str1 + str2).lower() # Entry point def get_code(gif_content): # gif_content = down_gif(url) # 下载图片 frames = extract_frames(gif_content) # Only select the first and last frame selected_frames = [frames[0], frames[-1]] # Use multithreading to process the selected frames with ThreadPoolExecutor() as executor: future_to_frame = {executor.submit(process_frame, frame): frame for frame in selected_frames} result = [] for future in future_to_frame: try: img_base64 = future.result() ress = test_get(img_base64) # Submit each frame as base64 result.append(ress) except Exception as e: print(f"Error processing frame: {e}") print(result) return merge_strings(result[1], result[0]) ``` ### 第二种使用ddddocr: ```python #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2024/9/13 17:22 # @Author : huoyu # @email : 2319899766@qq.com # @File : giffff.py # @Project : verifi_number # @Software: PyCharm import requests from PIL import Image import os import re import ddddocr import cv2 import numpy as np def down_gif(url): # url = "https://d11x481pn35wh6.cloudfront.net/TMdgfxyd/1dBdijbBPsWY.gif" res = requests.get(url).content open("hhh.gif", "wb").write(res) def split_gif(): # 打开GIF文件 gif_path = 'hhh.gif' # 替换为你的GIF文件路径 gif = Image.open(gif_path) # 创建输出目录 output_dir = 'output_frames' if not os.path.exists(output_dir): os.makedirs(output_dir) # 遍历每一帧并保存 frame = 0 while True: gif.seek(frame) frame_image = gif.copy() frame_image.save(f"{output_dir}/frame_{frame}.png") # 保存为PNG格式 frame += 1 try: gif.seek(frame) except EOFError: break print(f"GIF分解完成,共保存了{frame}张图片") # 合并字符串 def merge_strings(str1, str2): # 将输入的字符串转换为小写,找到第一个字符串的后缀和第二个字符串的前缀公共部分 lower_str1 = str1.lower() lower_str2 = str2.lower() for i in range(min(len(lower_str1), len(lower_str2)), 0, -1): if lower_str1.endswith(lower_str2[:i]): return (str1 + str2[i:]).lower() # 如果没有公共部分,直接合并并转换为小写 return (str1 + str2).lower() # 将图片二值化处理,只保留想识别的验证码颜色,其余为黑色 def img_cv2(): # 读取图片 image = cv2.imread('./output_frames/frame_1.png', cv2.IMREAD_COLOR) # 定义目标颜色的范围,设置一个阈值以匹配近似的 #ECEBE4 lower_bound = np.array([220, 220, 220]) # 近似 #ECEBE4 的下限 upper_bound = np.array([236, 236, 236]) # 近似 #ECEBE4 的上限 # 定义其他颜色为 #121212 (BGR格式) color_to_change = [18, 18, 18] # #121212 转换为 [18, 18, 18] (BGR) # 创建掩码,只保留颜色范围内的部分 mask = cv2.inRange(image, lower_bound, upper_bound) # 创建输出图像,将原图像的内容复制到新的图像 result = image.copy() # 将掩码外的像素设为指定的颜色 (#121212) result[np.where(mask == 0)] = color_to_change # 保存和显示结果 cv2.imwrite('output_frames/processed_image.jpg', result) # 显示处理后的图片 cv2.imshow('Processed Image', result) cv2.waitKey(0) cv2.destroyAllWindows() def dddocr(): # 定义图片文件夹路径 # output_dir = 'output_frames' output_dir = 'output_frames' # 提取文件名中的数字,并返回排序后的文件列表 def sort_key(filename): match = re.search(r'_(\d+)\.png', filename) # 提取文件名中的数字部分 if match: return int(match.group(1)) # 返回数字部分作为排序依据 return float('inf') # 如果没有数字,则将该文件放在最后 # 获取所有 .png 文件,并按数字顺序排序 sorted_files = sorted([f for f in os.listdir(output_dir) if f.endswith('.png')], key=sort_key) results = [] # sorted_file = [sorted_files[0]] + [sorted_files[-1]]#取前后两张图片 sorted_file = sorted_files#去所有图片 # 遍历排序后的文件列表 for filename in sorted_file: file_path = os.path.join(output_dir, filename) print(f"找到图片文件: {file_path}") file = open(file_path, "rb").read() ocr = ddddocr.DdddOcr(show_ad=False, beta=True) result = ocr.classification(file) print(result) results.append(result) return merge_strings(results[1], results[0]) # down_gif("https://d11x481pn35wh6.cloudfront.net/RRVCGery/gqBQWAPJXKYq.gif") # split_gif() img_cv2() print(dddocr()) ``` ### 第三种 YOLO训练 首先收集100张图片进行手动打标签,训练图片,再用训练集批量打码500张图片,检查后再次训练,通过率基本能在95%以上,如果精度不够,那就在获取图片训练。 调用图片打码接口(本地自己搭的) ```python while 1: jg = '\r\n' t1 = time.time() # mbckjb = "2038346" #填目标窗口句柄则识别该窗口返回结果;传入大于20字符长度无效句柄则全屏识别 image = cv2.imread("frame_0.png") ret, buffer = cv2.imencode('.png', image) img_bytes = buffer.tobytes() ret = requests.post("http://127.0.0.1:7700", data=img_bytes) t2 = time.time() list = ret.text.split("|") for i in list: list2 = i.split(",") if len(list2) >= 7: id = list2[0] x = list2[1] y = list2[2] w = list2[3] h = list2[4] prob = list2[5] name = list2[6] jg = jg + 'id:' + id + " x:" + x + " y:" + y + " w:" + w + " h:" + h + " prob:" + prob + " name:" + name + '\r\n' print('耗时' + str(round((t2 - t1) * 1000)) + "ms 共" + str(len(list) - 1) + '个目标', jg) time.sleep(2) ``` 完整代码 ```python #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2024/9/12 09:33 # @Author : huoyu # @email : 2319899766@qq.com # @File : ai_gif.py # @Project : verifi_number # @Software: PyCharm import requests from PIL import Image import cv2 import numpy as np import io import re import time from concurrent.futures import ThreadPoolExecutor def down_gif(url): res = requests.get(url).content return res # Return GIF binary content def extract_frames(gif_content): # Handle GIF content in memory using io.BytesIO gif = Image.open(io.BytesIO(gif_content)) frames = [] try: while True: frame_image = gif.copy() # Copy the current frame frames.append(frame_image) gif.seek(gif.tell() + 1) # Move to the next frame except EOFError: pass return frames def process_frame(frame): # 将帧图像转换为 PNG 格式的字节数据 with io.BytesIO() as output: frame.save(output, format="PNG") image_data = output.getvalue() # 获取内存中的图像数据 return image_data # 返回图像字节数据 def xunlian(image_data): jg = '\r\n' t1 = time.time() # 直接从传入的内存数据中解码 image = cv2.imdecode(np.frombuffer(image_data, np.uint8), cv2.IMREAD_COLOR) ret, buffer = cv2.imencode('.png', image) img_bytes = buffer.tobytes() response = requests.post("http://127.0.0.1:7700", data=img_bytes) t2 = time.time() # 分割返回结果 result_list = response.text.split("|") filtered_results = [] # 处理每个目标 for item in result_list: fields = item.split(",") if len(fields) >= 7: prob = float(fields[5]) if prob > 0.5: # 过滤掉 prob <= 0.5 的项 id = fields[0] x = int(fields[1]) y = fields[2] w = fields[3] h = fields[4] name = fields[6] filtered_results.append({ "id": id, "x": x, "y": y, "w": w, "h": h, "prob": prob, "name": name }) # jg = jg + 'id:' + id + " x:" + x + " y:" + y + " w:" + w + " h:" + h + " prob:" + prob + " name:" + name + '\r\n' # print('耗时' + str(round((t2 - t1) * 1000)) + "ms 共" + str(len(list) - 1) + '个目标', jg) # 按 x 值排序 sorted_results = sorted(filtered_results, key=lambda item: item['x']) # 提取排序后的 name 值 sorted_names = [item['name'] for item in sorted_results] # 输出结果 # print('耗时' + str(round((t2 - t1) * 1000)) + "ms 共" + str(len(sorted_results)) + '个目标', jg) # print("排序后的 name 值: ", sorted_names) return sorted_names # 返回识别结果 def merge_strings(str1, str2): str1 = re.sub(r'[\u4e00-\u9fff\W]+', '', str1) str2 = re.sub(r'[\u4e00-\u9fff\W]+', '', str2) lower_str1 = str1.lower() lower_str2 = str2.lower() for i in range(min(len(lower_str1), len(lower_str2)), 0, -1): if lower_str1.endswith(lower_str2[:i]): return (str1 + str2[i:]).lower() return (str1 + str2).lower() # Entry point def get_code(gif_content): frames = extract_frames(gif_content) selected_frames = [frames[0], frames[-1]] results = [] with ThreadPoolExecutor() as executor: future_to_frame = {executor.submit(process_frame, frame): frame for frame in selected_frames} for future in future_to_frame: try: processed_image_data = future.result() result = xunlian(processed_image_data) # 使用处理后的内存图片数据 results.append(result) # 将结果添加到列表中 except Exception as e: print(f"Error processing frame: {e}") # 返回两个结果的合并 if len(results) == 2: print(''.join(results[1]), ''.join(results[0])) return merge_strings(''.join(results[1]), ''.join(results[0])) return "处理失败" ``` ## 总结 三种方法都是线程中处理图片,也不和本地图片进行交互,直接将图片在内存里就处理完成,提高效率。如果你的验证码需要获取gif里面的每一张图片就修改`selected_frames = [frames[0], frames[-1]]`这里,直接使用`selected_frames`进行循环即可 第一种是找了一个github项目进行对接 第二种方法,使用`ddddocr`打码,直接传递图片就能打码,里面也可以对图片先进行二值化处理在打码,至于你用的什么打码,变通一下就行 第三种是基于`yolo`训练,然后部署服务端进行打码,这种比较有针对性,如果市面上的都不行,那就用这种方式 最后修改:2024 年 09 月 23 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏