mirror of
https://git.oceanpay.cc/danial/kami_jd_ck.git
synced 2025-12-18 22:11:07 +00:00
395 lines
13 KiB
Python
395 lines
13 KiB
Python
import json
|
||
import math
|
||
import random
|
||
|
||
import PIL
|
||
import numpy as np
|
||
import urllib.parse
|
||
from pathlib import Path
|
||
|
||
import cv2
|
||
|
||
|
||
class BezierTrajectory:
|
||
def _bztsg(self, dataTrajectory):
|
||
lengthOfdata = len(dataTrajectory)
|
||
|
||
def staer(x):
|
||
t = ((x - dataTrajectory[0][0]) / (dataTrajectory[-1][0] - dataTrajectory[0][0]))
|
||
y = np.array([0, 0], dtype=np.float64)
|
||
for s in range(len(dataTrajectory)):
|
||
y += dataTrajectory[s] * ((math.factorial(lengthOfdata - 1) / (
|
||
math.factorial(s) * math.factorial(lengthOfdata - 1 - s))) * math.pow(t, s) * math.pow(
|
||
(1 - t), lengthOfdata - 1 - s))
|
||
return y[1]
|
||
|
||
return staer
|
||
|
||
def _type(self, type, x, numberList):
|
||
numberListre = []
|
||
pin = (x[1] - x[0]) / numberList
|
||
if type == 0:
|
||
for i in range(numberList):
|
||
numberListre.append(i * pin)
|
||
if pin >= 0:
|
||
numberListre = numberListre[::-1]
|
||
elif type == 1:
|
||
for i in range(numberList):
|
||
numberListre.append(1 * ((i * pin) ** 2))
|
||
numberListre = numberListre[::-1]
|
||
elif type == 2:
|
||
for i in range(numberList):
|
||
numberListre.append(1 * ((i * pin - x[1]) ** 2))
|
||
|
||
elif type == 3:
|
||
dataTrajectory = [np.array([0, 0]), np.array([(x[1] - x[0]) * 0.8, (x[1] - x[0]) * 0.6]),
|
||
np.array([x[1] - x[0], 0])]
|
||
fun = self._bztsg(dataTrajectory)
|
||
numberListre = [0]
|
||
for i in range(1, numberList):
|
||
numberListre.append(fun(i * pin) + numberListre[-1])
|
||
if pin >= 0:
|
||
numberListre = numberListre[::-1]
|
||
numberListre = np.abs(np.array(numberListre) - max(numberListre))
|
||
biaoNumberList = ((numberListre - numberListre[numberListre.argmin()]) / (
|
||
numberListre[numberListre.argmax()] - numberListre[numberListre.argmin()])) * (x[1] - x[0]) + x[0]
|
||
biaoNumberList[0] = x[0]
|
||
biaoNumberList[-1] = x[1]
|
||
return biaoNumberList
|
||
|
||
def getFun(self, s):
|
||
'''
|
||
|
||
:param s: 传入P点
|
||
:return: 返回公式
|
||
'''
|
||
dataTrajectory = []
|
||
for i in s:
|
||
dataTrajectory.append(np.array(i))
|
||
return self._bztsg(dataTrajectory)
|
||
|
||
def simulation(self, start, end, le=1, deviation=0, bias=0.5):
|
||
start = np.array(start)
|
||
end = np.array(end)
|
||
cbb = []
|
||
if le != 1:
|
||
e = (1 - bias) / (le - 1)
|
||
cbb = [[bias + e * i, bias + e * (i + 1)] for i in range(le - 1)]
|
||
|
||
dataTrajectoryList = [start]
|
||
number = random.uniform(1, 15)
|
||
number_rounded1 = round(number, 2)
|
||
number2 = random.uniform(-15, -1)
|
||
number_rounded2 = round(number2, 2)
|
||
t = random.choice([number_rounded2, number_rounded1])
|
||
w = 0
|
||
for i in cbb:
|
||
px1 = start[0] + (end[0] - start[0]) * (random.random() * (i[1] - i[0]) + (i[0]))
|
||
p = np.array([px1, self._bztsg([start, end])(px1) + t * deviation])
|
||
dataTrajectoryList.append(p)
|
||
w += 1
|
||
if w >= 2:
|
||
w = 0
|
||
t = -1 * t
|
||
|
||
dataTrajectoryList.append(end)
|
||
return {"equation": self._bztsg(dataTrajectoryList), "P": np.array(dataTrajectoryList)}
|
||
|
||
def trackArray(self, start, end, numberList, le=4, deviation=0, bias=0.6, type=0, cbb=0, yhh=10):
|
||
s = []
|
||
fun = self.simulation(start, end, le, deviation, bias)
|
||
w = fun['P']
|
||
fun = fun["equation"]
|
||
if cbb != 0:
|
||
numberListOfcbb = round(numberList * 0.2 / (cbb + 1))
|
||
numberList -= (numberListOfcbb * (cbb + 1))
|
||
|
||
xTrackArray = self._type(type, [start[0], end[0]], numberList)
|
||
for i in xTrackArray:
|
||
s.append([i, fun(i)])
|
||
dq = yhh / cbb
|
||
kg = 0
|
||
ends = np.copy(end)
|
||
for i in range(cbb):
|
||
if kg == 0:
|
||
d = np.array([end[0] + (yhh - dq * i),
|
||
((end[1] - start[1]) / (end[0] - start[0])) * (end[0] + (yhh - dq * i)) + (
|
||
end[1] - ((end[1] - start[1]) / (end[0] - start[0])) * end[0])])
|
||
kg = 1
|
||
else:
|
||
d = np.array([end[0] - (yhh - dq * i),
|
||
((end[1] - start[1]) / (end[0] - start[0])) * (end[0] - (yhh - dq * i)) + (
|
||
end[1] - ((end[1] - start[1]) / (end[0] - start[0])) * end[0])])
|
||
kg = 0
|
||
# print(d)
|
||
y = self.trackArray(ends, d, numberListOfcbb, le=le, deviation=deviation, bias=bias, type=3, cbb=0,
|
||
yhh=yhh)
|
||
s += list(y['trackArray'])
|
||
ends = d
|
||
y = self.trackArray(ends, end, numberListOfcbb, le=le, deviation=deviation, bias=bias, type=2, cbb=0,
|
||
yhh=yhh)
|
||
s += list(y['trackArray'])
|
||
|
||
else:
|
||
xTrackArray = self._type(type, [start[0], end[0]], numberList)
|
||
for i in xTrackArray:
|
||
s.append([i, fun(i)])
|
||
return {"trackArray": np.array(s), "P": w}
|
||
|
||
|
||
def imshow(img, winname='test', delay=0):
|
||
"""cv2展示图片"""
|
||
cv2.imshow(winname, img)
|
||
cv2.waitKey(delay)
|
||
cv2.destroyAllWindows()
|
||
|
||
|
||
def pil_to_cv2(img):
|
||
"""
|
||
pil转cv2图片
|
||
:param img: pil图像, <type 'PIL.JpegImagePlugin.JpegImageFile'>
|
||
:return: cv2图像, <type 'numpy.ndarray'>
|
||
"""
|
||
img = cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
|
||
return img
|
||
|
||
|
||
def bytes_to_cv2(img):
|
||
"""
|
||
二进制图片转cv2
|
||
:param img: 二进制图片数据, <type 'bytes'>
|
||
:return: cv2图像, <type 'numpy.ndarray'>
|
||
"""
|
||
# 将图片字节码bytes, 转换成一维的numpy数组到缓存中
|
||
img_buffer_np = np.frombuffer(img, dtype=np.uint8)
|
||
# 从指定的内存缓存中读取一维numpy数据, 并把数据转换(解码)成图像矩阵格式
|
||
img_np = cv2.imdecode(img_buffer_np, 1)
|
||
return img_np
|
||
|
||
|
||
def cv2_open(img, flag=None):
|
||
"""
|
||
统一输出图片格式为cv2图像, <type 'numpy.ndarray'>
|
||
:param img: <type 'bytes'/'numpy.ndarray'/'str'/'Path'/'PIL.JpegImagePlugin.JpegImageFile'>
|
||
:param flag: 颜色空间转换类型, default: None
|
||
eg: cv2.COLOR_BGR2GRAY(灰度图)
|
||
:return: cv2图像, <numpy.ndarray>
|
||
"""
|
||
if isinstance(img, bytes):
|
||
img = bytes_to_cv2(img)
|
||
elif isinstance(img, (str, Path)):
|
||
img = cv2.imread(str(img))
|
||
elif isinstance(img, np.ndarray):
|
||
img = img
|
||
elif isinstance(img, PIL.Image):
|
||
img = pil_to_cv2(img)
|
||
else:
|
||
raise ValueError(f'输入的图片类型无法解析: {type(img)}')
|
||
if flag is not None:
|
||
img = cv2.cvtColor(img, flag)
|
||
return img
|
||
|
||
|
||
def convert_png_to_jpg(img_data):
|
||
"""
|
||
将透明背景的图片转换为白色背景,使用OpenCV实现。
|
||
|
||
参数:
|
||
input_image_path: str,输入图片的路径
|
||
output_image_path: str,输出图片的路径
|
||
|
||
返回:
|
||
无
|
||
"""
|
||
# 使用BytesIO将数据转换为文件对象
|
||
img_array = np.frombuffer(img_data, np.uint8)
|
||
# 读取图像数据
|
||
img = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED)
|
||
# 检查是否读取成功
|
||
if img is None:
|
||
raise ValueError("Image could not be decoded from base64 data.")
|
||
# 分离RGBA通道
|
||
b, g, r, a = cv2.split(img)
|
||
# 创建一个与Alpha通道大小相同的全白背景
|
||
white_background = np.ones_like(a) * 255
|
||
# 将Alpha通道归一化到0-1范围
|
||
a = a / 255.0
|
||
# 按照Alpha通道的透明度混合原图和白色背景
|
||
r = r * a + white_background * (1 - a)
|
||
g = g * a + white_background * (1 - a)
|
||
b = b * a + white_background * (1 - a)
|
||
# 合并BGR通道
|
||
result = cv2.merge((b, g, r))
|
||
return result
|
||
|
||
|
||
def resize_image_to_width(img, target_width):
|
||
"""将图像调整为指定宽度,同时保持纵横比"""
|
||
height, width = img.shape[:2]
|
||
aspect_ratio = height / width
|
||
new_height = int(target_width * aspect_ratio)
|
||
resized_img = cv2.resize(img, (target_width, new_height))
|
||
return resized_img
|
||
|
||
|
||
def merge_images(img1, img2):
|
||
"""将两张图像垂直合并成一张图像,调整第二张图像的宽度以匹配第一张图像的宽度"""
|
||
# 获取两张图像的高度和宽度
|
||
height1, width1 = img1.shape[:2]
|
||
height2, width2 = img2.shape[:2]
|
||
|
||
# 如果第二张图像的宽度大于第一张图像的宽度,则缩放第二张图像
|
||
if width2 > width1:
|
||
img2 = resize_image_to_width(img2, width1)
|
||
|
||
# 重新获取第二张图像的高度和宽度
|
||
height2, width2 = img2.shape[:2]
|
||
|
||
# 计算新的高度和宽度
|
||
new_height = height1 + height2
|
||
new_width = width1
|
||
|
||
# 创建一个新的图像以容纳两张图像
|
||
merged_img = np.zeros((new_height, new_width, 3), dtype=np.uint8)
|
||
|
||
# 将第一张图像拷贝到新图像中
|
||
merged_img[:height1, :width1] = img1
|
||
|
||
# 将第二张图像拷贝到新图像中
|
||
merged_img[height1:, :width2] = img2
|
||
|
||
# 将合并后的图像编码为 PNG 格式的字节流
|
||
_, img_encoded = cv2.imencode('.png', merged_img)
|
||
img_bytes = img_encoded.tobytes()
|
||
|
||
return img_bytes
|
||
|
||
|
||
def get_distances(bg, tp, im_show=False, save_path=None):
|
||
# 读取图片
|
||
bg_img = cv2_open(bg)
|
||
tp_gray = cv2_open(tp, flag=cv2.COLOR_BGR2GRAY)
|
||
|
||
# 金字塔均值漂移
|
||
bg_shift = cv2.pyrMeanShiftFiltering(bg_img, 5, 50)
|
||
|
||
# 边缘检测
|
||
tp_gray = cv2.Canny(tp_gray, 255, 255)
|
||
bg_gray = cv2.Canny(bg_shift, 255, 255)
|
||
|
||
# 目标匹配
|
||
result = cv2.matchTemplate(bg_gray, tp_gray, cv2.TM_CCOEFF_NORMED)
|
||
# 解析匹配结果
|
||
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
|
||
|
||
distance = max_loc[0]
|
||
if save_path or im_show:
|
||
# 需要绘制的方框高度和宽度
|
||
tp_height, tp_width = tp_gray.shape[:2]
|
||
# 矩形左上角点位置
|
||
x, y = max_loc
|
||
# 矩形右下角点位置
|
||
_x, _y = x + tp_width, y + tp_height
|
||
# 绘制矩形
|
||
bg_img = cv2_open(bg)
|
||
cv2.rectangle(bg_img, (x, y), (_x, _y), (0, 0, 255), 2)
|
||
# 保存缺口识别结果到背景图
|
||
if save_path:
|
||
save_path = Path(save_path).resolve()
|
||
save_path = save_path.parent / f"{save_path.stem}.{distance}{save_path.suffix}"
|
||
save_path = save_path.__str__()
|
||
cv2.imwrite(save_path, bg_img)
|
||
# 显示缺口识别结果
|
||
if im_show:
|
||
imshow(bg_img)
|
||
return distance
|
||
|
||
|
||
def slider_swipe_time(length):
|
||
times = []
|
||
time_interval = None
|
||
_number = 0
|
||
|
||
for i in range(length):
|
||
if i < length - 10:
|
||
time_interval = random.uniform(0, 4)
|
||
else:
|
||
if time_interval and time_interval > 50 and random.random() > 0.80:
|
||
time_interval = random.uniform(time_interval - 40, time_interval)
|
||
else:
|
||
time_interval = random.uniform(1, 100)
|
||
|
||
_number += time_interval
|
||
times.append(round(time_interval))
|
||
return times # 返回时间间隔列表
|
||
|
||
|
||
def get_distance(background, slide):
|
||
'''
|
||
:param1:background是缺口图
|
||
:param2:slide是滑块图
|
||
'''
|
||
distance = get_distances(bg=background, tp=slide, im_show=False)
|
||
distance = distance + 5
|
||
yend = random.randint(random.randint(-60, -1), random.randint(1, 60))
|
||
random_case = random.choice([1, 2, 3])
|
||
baidong = random.randint(1, 50)
|
||
fangwei = random.randint(random.randint(-60, -1), random.randint(1, 60))
|
||
le = random.randint(20, 50)
|
||
if distance > 150:
|
||
number = random.uniform(2.2, 2.4)
|
||
elif 140 < distance < 150:
|
||
number = random.uniform(2.1, 2.2)
|
||
elif 130 < distance < 140:
|
||
number = random.uniform(2.0, 2.1)
|
||
elif 120 < distance < 130:
|
||
number = random.uniform(1.9, 2.0)
|
||
elif 110 < distance < 120:
|
||
number = random.uniform(1.8, 1.9)
|
||
else:
|
||
number = random.uniform(1.7, 1.8)
|
||
number_rounded = round(number, 2)
|
||
a = BezierTrajectory().trackArray(
|
||
start=[0, random.randint(0, 20)],
|
||
end=[distance, yend],
|
||
numberList=int(distance * number_rounded),
|
||
le=le,
|
||
type=random_case,
|
||
cbb=baidong,
|
||
yhh=fangwei,
|
||
)
|
||
|
||
_move_list = a["trackArray"].tolist()
|
||
|
||
move_list = []
|
||
length = len(_move_list)
|
||
swipe_times = slider_swipe_time(length)
|
||
swipe_times[0] = 0
|
||
swipe_times[1] = 0.01
|
||
swipe_times[2] = 0.03
|
||
swipe_times[3] = 0.04
|
||
swipe_times[4] = 0.04
|
||
for index, i in enumerate(_move_list):
|
||
x = round(i[0])
|
||
y = round(i[1])
|
||
move_list.append([x, y, swipe_times[index]])
|
||
return move_list
|
||
|
||
|
||
def save_photo_and_check(quekou, huakuai):
|
||
distance_list = get_distance(quekou, huakuai)
|
||
distance_dict = {}
|
||
distance_dict['6@hH@X'] = "H%io5u"
|
||
distance_dict['P7cH*^'] = "aOr*7C"
|
||
distance_dict['ht'] = 179
|
||
distance_dict['wt'] = 290
|
||
distance_dict['bw'] = 59
|
||
distance_dict['sw'] = 290
|
||
distance_dict['mw'] = 50
|
||
distance_dict['list'] = distance_list
|
||
photo_distance = str(json.dumps(distance_dict))
|
||
photo_distance = photo_distance.replace(" ", '')
|
||
# print(photo_distance)
|
||
track = urllib.parse.quote(photo_distance, safe=':,')
|
||
return track
|