2020-03-28 17:22:46 +00:00
|
|
|
import numpy as np
|
|
|
|
from PIL import ImageStat
|
|
|
|
|
|
|
|
|
|
|
|
def random_normal_distribution_int(a, b, n=5):
|
|
|
|
"""Generate a normal distribution int within the interval. Use the average value of several random numbers to
|
|
|
|
simulate normal distribution.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
a (int): The minimum of the interval.
|
|
|
|
b (int): The maximum of the interval.
|
|
|
|
n (int): The amount of numbers in simulation. Default to 5.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
int
|
|
|
|
"""
|
|
|
|
if a < b:
|
|
|
|
output = np.mean(np.random.randint(a, b, size=n))
|
|
|
|
return int(output.round())
|
|
|
|
else:
|
|
|
|
return b
|
|
|
|
|
|
|
|
|
|
|
|
def ensure_time(second, n=5, precision=3):
|
|
|
|
"""Ensure to be time.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
second (int, float, tuple): time.
|
|
|
|
n (int): The amount of numbers in simulation. Default to 5.
|
|
|
|
precision (int): Decimals.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
"""
|
|
|
|
if isinstance(second, tuple):
|
|
|
|
multiply = 10 ** precision
|
|
|
|
return random_normal_distribution_int(second[0] * multiply, second[1] * multiply, n) / multiply
|
|
|
|
else:
|
|
|
|
return second
|
|
|
|
|
|
|
|
|
|
|
|
def random_rectangle_point(area):
|
|
|
|
"""Choose a random point in an area.
|
|
|
|
|
|
|
|
Args:
|
2020-04-13 08:07:32 +00:00
|
|
|
area: (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y).
|
2020-03-28 17:22:46 +00:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
int: x
|
|
|
|
int: y
|
|
|
|
"""
|
|
|
|
x = random_normal_distribution_int(area[0], area[2])
|
|
|
|
y = random_normal_distribution_int(area[1], area[3])
|
|
|
|
return x, y
|
|
|
|
|
|
|
|
|
2020-04-13 08:07:32 +00:00
|
|
|
def random_rectangle_vector(vector, box, random_range=(0, 0, 0, 0), padding=15):
|
|
|
|
"""Place a vector in a box randomly.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
vector: (x, y)
|
|
|
|
box: (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y).
|
|
|
|
random_range (tuple): Add a random_range to vector. (x_min, y_min, x_max, y_max).
|
|
|
|
padding (int):
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
tuple(int), tuple(int): start_point, end_point.
|
|
|
|
"""
|
|
|
|
vector = np.array(vector) + random_rectangle_point(random_range)
|
|
|
|
vector = np.round(vector).astype(np.int)
|
|
|
|
half_vector = np.round(vector / 2).astype(np.int)
|
|
|
|
box = np.array(box) + np.append(np.abs(half_vector) + padding, -np.abs(half_vector) - padding)
|
|
|
|
center = random_rectangle_point(box)
|
|
|
|
start_point = center - half_vector
|
|
|
|
end_point = start_point + vector
|
|
|
|
return tuple(start_point), tuple(end_point)
|
|
|
|
|
|
|
|
|
|
|
|
def random_line_segments(p1, p2, n, random_range=(0, 0, 0, 0)):
|
|
|
|
"""Cut a line into multiple segments.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
p1: (x, y).
|
|
|
|
p2: (x, y).
|
|
|
|
n: Number of slice.
|
|
|
|
random_range: Add a random_range to points.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
list[tuple]: [(x0, y0), (x1, y1), (x2, y2)]
|
|
|
|
"""
|
|
|
|
return [tuple((((n - index) * p1 + index * p2) / n).astype(int) + random_rectangle_point(random_range))
|
|
|
|
for index in range(0, n + 1)]
|
|
|
|
|
|
|
|
|
2020-03-28 17:22:46 +00:00
|
|
|
def area_offset(area, offset):
|
|
|
|
"""
|
|
|
|
|
|
|
|
Args:
|
|
|
|
area(tuple): (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y).
|
|
|
|
offset(tuple): (x, y).
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
tuple: (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y).
|
|
|
|
"""
|
|
|
|
return tuple(np.array(area) + np.append(offset, offset))
|
|
|
|
|
|
|
|
|
|
|
|
def area_pad(area, pad=10):
|
|
|
|
"""
|
|
|
|
|
|
|
|
Args:
|
|
|
|
area(tuple): (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y).
|
|
|
|
pad(int):
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
tuple: (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y).
|
|
|
|
"""
|
|
|
|
return tuple(np.array(area) + np.array([pad, pad, -pad, -pad]))
|
|
|
|
|
|
|
|
|
|
|
|
def point_in_area(point, area, threshold=5):
|
|
|
|
"""
|
|
|
|
|
|
|
|
Args:
|
|
|
|
point: (x, y).
|
|
|
|
area: (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y).
|
|
|
|
threshold: int
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool
|
|
|
|
"""
|
|
|
|
return area[0] - threshold < point[0] < area[2] + threshold and area[1] - threshold < point[1] < area[3] + threshold
|
|
|
|
|
|
|
|
|
|
|
|
def area_in_area(area1, area2, threshold=5):
|
|
|
|
"""
|
|
|
|
|
|
|
|
Args:
|
|
|
|
area1: (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y).
|
|
|
|
area2: (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y).
|
|
|
|
threshold: int
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool
|
|
|
|
"""
|
|
|
|
return area2[0] - threshold <= area1[0] \
|
|
|
|
and area2[1] - threshold <= area1[1] \
|
|
|
|
and area1[2] <= area2[2] + threshold \
|
|
|
|
and area1[3] <= area2[3] + threshold
|
|
|
|
|
|
|
|
|
|
|
|
def area_cross_area(area1, area2, threshold=5):
|
|
|
|
"""
|
|
|
|
|
|
|
|
Args:
|
|
|
|
area1: (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y).
|
|
|
|
area2: (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y).
|
|
|
|
threshold: int
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool
|
|
|
|
"""
|
|
|
|
return point_in_area((area1[0], area1[1]), area2, threshold=threshold) \
|
|
|
|
or point_in_area((area1[2], area1[1]), area2, threshold=threshold) \
|
|
|
|
or point_in_area((area1[0], area1[3]), area2, threshold=threshold) \
|
|
|
|
or point_in_area((area1[2], area1[3]), area2, threshold=threshold)
|
|
|
|
|
|
|
|
|
|
|
|
def node2location(node):
|
|
|
|
"""
|
|
|
|
Args:
|
|
|
|
node(str): Example: 'E3'
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
tuple: Example: (6, 4)
|
|
|
|
"""
|
|
|
|
return ord(node[0]) % 32 - 1, int(node[1]) - 1
|
|
|
|
|
|
|
|
|
|
|
|
def location2node(location):
|
|
|
|
"""
|
|
|
|
Args:
|
|
|
|
location(tuple): Example: (6, 4)
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
str: Example: 'E3'
|
|
|
|
"""
|
|
|
|
return chr(location[0] + 64 + 1) + str(location[1] + 1)
|
|
|
|
|
|
|
|
|
|
|
|
def get_color(image, area):
|
|
|
|
"""Calculate the average color of a particular area of the image.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
image (PIL.Image.Image): Screenshot.
|
|
|
|
area (tuple): (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y)
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
tuple: (r, g, b)
|
|
|
|
"""
|
|
|
|
temp = image.crop(area)
|
|
|
|
stat = ImageStat.Stat(temp)
|
|
|
|
return np.array(stat.mean)
|
|
|
|
|
|
|
|
|
|
|
|
def color_similarity(color1, color2):
|
|
|
|
"""
|
|
|
|
Args:
|
|
|
|
color1 (tuple): (r, g, b)
|
|
|
|
color2 (tuple): (r, g, b)
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
int:
|
|
|
|
"""
|
|
|
|
diff = np.array(color1) - np.array(color2)
|
|
|
|
positive, negative = diff, np.abs(diff)
|
|
|
|
positive[diff < 0] = 0
|
|
|
|
negative[diff > 0] = 0
|
|
|
|
diff = np.max(positive) + np.max(negative)
|
|
|
|
return diff
|
|
|
|
|
|
|
|
|
|
|
|
def color_similar(color1, color2, threshold=10):
|
|
|
|
"""Consider two colors are similar, if tolerance lesser or equal threshold.
|
|
|
|
Tolerance = Max(Positive(difference_rgb)) + Max(- Negative(difference_rgb))
|
|
|
|
The same as the tolerance in Photoshop.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
color1 (tuple): (r, g, b)
|
|
|
|
color2 (tuple): (r, g, b)
|
|
|
|
threshold (int): Default to 10.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool: True if two colors are similar.
|
|
|
|
"""
|
|
|
|
# print(color1, color2)
|
|
|
|
diff = np.array(color1) - np.array(color2)
|
|
|
|
positive, negative = diff, np.abs(diff)
|
|
|
|
positive[diff < 0] = 0
|
|
|
|
negative[diff > 0] = 0
|
|
|
|
diff = np.max(positive) + np.max(negative)
|
|
|
|
return diff <= threshold
|
|
|
|
|
|
|
|
|
|
|
|
def color_similar_1d(bar, color, threshold=10):
|
|
|
|
"""
|
|
|
|
Args:
|
|
|
|
bar: 1D array.
|
|
|
|
color: (r, g, b)
|
|
|
|
threshold(int): Default to 10.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
np.ndarray: bool
|
|
|
|
"""
|
|
|
|
diff = np.array(bar) - np.array(color)
|
|
|
|
positive, negative = diff, np.abs(diff)
|
|
|
|
positive[diff < 0] = 0
|
|
|
|
negative[diff > 0] = 0
|
|
|
|
diff = np.max(positive, axis=1) + np.max(negative, axis=1)
|
|
|
|
return diff <= threshold
|
|
|
|
|
|
|
|
|
|
|
|
def color_similarity_2d(image, color):
|
|
|
|
"""
|
|
|
|
Args:
|
|
|
|
image: 2D array.
|
|
|
|
color: (r, g, b)
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
np.ndarray: uint8
|
|
|
|
"""
|
|
|
|
diff = np.array(image) - color
|
|
|
|
positive, negative = diff, np.abs(diff)
|
|
|
|
positive[diff < 0] = 0
|
|
|
|
negative[diff > 0] = 0
|
|
|
|
diff = 255.0 - np.max(positive, axis=2) - np.max(negative, axis=2)
|
|
|
|
diff[diff < 0] = 0
|
|
|
|
image = diff.astype(np.uint8)
|
|
|
|
return image
|
|
|
|
|
|
|
|
|
|
|
|
def extract_letters(image, letter=(255, 255, 255), back=(0, 0, 0)):
|
|
|
|
"""Set letter color to black, set background color to white.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
image: Shape (height, width, channel)
|
|
|
|
letter (tuple): Letter RGB.
|
|
|
|
back (tuple): Background RGB.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
np.ndarray: Shape (height, width)
|
|
|
|
"""
|
|
|
|
image = color_similarity_2d(np.array(image), color=letter)
|
|
|
|
back = color_similarity(back, letter)
|
|
|
|
image = (255.0 - image) * (1 + back / 255)
|
|
|
|
image[image > 255] = 255
|
|
|
|
return image
|
|
|
|
|
|
|
|
|
|
|
|
def red_overlay_transparency(color1, color2, red=247):
|
|
|
|
"""Calculate the transparency of red overlay.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
color1: origin color.
|
|
|
|
color2: changed color.
|
|
|
|
red(int): red color 0-255. Default to 247.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
float: 0-1
|
|
|
|
"""
|
|
|
|
return (color2[0] - color1[0]) / (red - color1[0])
|
|
|
|
|
|
|
|
|
|
|
|
def color_bar_percentage(image, area, prev_color, reverse=False, starter=0, threshold=30):
|
|
|
|
"""
|
|
|
|
Args:
|
|
|
|
image:
|
|
|
|
area:
|
2020-04-13 08:07:32 +00:00
|
|
|
prev_color:
|
2020-03-28 17:22:46 +00:00
|
|
|
reverse: True if bar goes from right to left.
|
|
|
|
starter:
|
2020-04-13 08:07:32 +00:00
|
|
|
threshold:
|
2020-03-28 17:22:46 +00:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
float: 0 to 1.
|
|
|
|
"""
|
|
|
|
bar = np.array(image.crop(area))
|
|
|
|
length = bar.shape[1]
|
|
|
|
bar = np.swapaxes(bar, 0, 1)
|
|
|
|
bar = bar[::-1, :, :] if reverse else bar
|
|
|
|
prev_index = 0
|
|
|
|
for index, color in enumerate(bar):
|
|
|
|
if index < starter:
|
|
|
|
continue
|
|
|
|
mask = color_similar_1d(color, prev_color, threshold=threshold)
|
|
|
|
if np.any(mask):
|
|
|
|
prev_color = color[mask].mean(axis=0)
|
|
|
|
prev_index = index
|
|
|
|
|
|
|
|
return prev_index / length
|