mirror of
https://github.com/LmeSzinc/StarRailCopilot.git
synced 2024-11-29 19:08:50 +00:00
195 lines
5.0 KiB
Python
195 lines
5.0 KiB
Python
import cv2
|
|
import numpy as np
|
|
from scipy import signal
|
|
|
|
from module.base.utils import image_size
|
|
|
|
|
|
def map_image_preprocess(image):
|
|
"""
|
|
A shared preprocess method used in ResourceGenerate and _predict_position()
|
|
|
|
Args:
|
|
image (np.ndarray): Screenshot in RGB
|
|
|
|
Returns:
|
|
np.ndarray:
|
|
"""
|
|
# image = rgb2luma(image)
|
|
image = cv2.GaussianBlur(image, (5, 5), 0)
|
|
image = cv2.Canny(image, 15, 50)
|
|
return image
|
|
|
|
|
|
def create_circular_mask(h, w, center=None, radius=None):
|
|
# https://stackoverflow.com/questions/44865023/how-can-i-create-a-circular-mask-for-a-numpy-array
|
|
if center is None: # use the middle of the image
|
|
center = (int(w / 2), int(h / 2))
|
|
if radius is None: # use the smallest distance between the center and image walls
|
|
radius = min(center[0], center[1], w - center[0], h - center[1])
|
|
|
|
y, x = np.ogrid[:h, :w]
|
|
dist_from_center = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)
|
|
|
|
mask = dist_from_center <= radius
|
|
return mask
|
|
|
|
|
|
def rotate_bound(image, angle):
|
|
"""
|
|
Rotate an image with outbound
|
|
|
|
https://blog.csdn.net/qq_37674858/article/details/80708393
|
|
|
|
Args:
|
|
image (np.ndarray):
|
|
angle (int, float):
|
|
|
|
Returns:
|
|
np.ndarray:
|
|
"""
|
|
# grab the dimensions of the image and then determine the
|
|
# center
|
|
(h, w) = image.shape[:2]
|
|
(cX, cY) = (w // 2, h // 2)
|
|
|
|
# grab the rotation matrix (applying the negative of the
|
|
# angle to rotate clockwise), then grab the sine and cosine
|
|
# (i.e., the rotation components of the matrix)
|
|
M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
|
|
cos = np.abs(M[0, 0])
|
|
sin = np.abs(M[0, 1])
|
|
|
|
# compute the new bounding dimensions of the image
|
|
nW = int((h * sin) + (w * cos))
|
|
nH = int((h * cos) + (w * sin))
|
|
|
|
# adjust the rotation matrix to take into account translation
|
|
M[0, 2] += (nW / 2) - cX
|
|
M[1, 2] += (nH / 2) - cY
|
|
|
|
# perform the actual rotation and return the image
|
|
return cv2.warpAffine(image, M, (nW, nH))
|
|
|
|
|
|
def cubic_find_maximum(image, precision=0.05):
|
|
"""
|
|
Using CUBIC resize algorithm to fit a curved surface, find the maximum value and location.
|
|
|
|
Args:
|
|
image (np.ndarray):
|
|
precision (int, float):
|
|
|
|
Returns:
|
|
float: Maximum value on curved surface
|
|
np.ndarray[float, float]: Location of maximum value
|
|
"""
|
|
image = cv2.resize(image, None, fx=1 / precision, fy=1 / precision, interpolation=cv2.INTER_CUBIC)
|
|
_, sim, _, loca = cv2.minMaxLoc(image)
|
|
loca = np.array(loca, dtype=float) * precision
|
|
return sim, loca
|
|
|
|
|
|
def image_center_pad(image, size, value=(0, 0, 0)):
|
|
"""
|
|
Create a new image with given `size`, placing given `image` in the middle.
|
|
|
|
Args:
|
|
image (np.ndarray):
|
|
size: (width, height)
|
|
value: Color of the background.
|
|
|
|
Returns:
|
|
np.ndarray:
|
|
"""
|
|
diff = np.array(size) - image_size(image)
|
|
left, top = int(diff[0] / 2), int(diff[1] / 2)
|
|
right, bottom = diff[0] - left, diff[1] - top
|
|
image = cv2.copyMakeBorder(image, top, bottom, left, right, borderType=cv2.BORDER_CONSTANT, value=value)
|
|
return image
|
|
|
|
|
|
def image_center_crop(image, size):
|
|
"""
|
|
Center crop the given image.
|
|
|
|
Args:
|
|
image (np.ndarray):
|
|
size: Output image shape, (width, height)
|
|
|
|
Returns:
|
|
np.ndarray:
|
|
"""
|
|
diff = image_size(image) - np.array(size)
|
|
left, top = int(diff[0] / 2), int(diff[1] / 2)
|
|
right, bottom = diff[0] - left, diff[1] - top
|
|
image = image[top:-bottom, left:-right]
|
|
return image
|
|
|
|
|
|
def area2corner(area):
|
|
"""
|
|
Args:
|
|
area: (x1, y1, x2, y2)
|
|
|
|
Returns:
|
|
np.ndarray: [upper-left, upper-right, bottom-left, bottom-right]
|
|
"""
|
|
return np.array([[area[0], area[1]], [area[2], area[1]], [area[0], area[3]], [area[2], area[3]]])
|
|
|
|
|
|
def convolve(arr, kernel=3):
|
|
"""
|
|
Args:
|
|
arr (np.ndarray): Shape (N,)
|
|
kernel (int):
|
|
|
|
Returns:
|
|
np.ndarray:
|
|
"""
|
|
return sum(np.roll(arr, i) * (kernel - abs(i)) // kernel for i in range(-kernel + 1, kernel))
|
|
|
|
|
|
def convolve_plain(arr, kernel=3):
|
|
"""
|
|
Args:
|
|
arr (np.ndarray): Shape (N,)
|
|
kernel (int):
|
|
|
|
Returns:
|
|
np.ndarray:
|
|
"""
|
|
return sum(np.roll(arr, i) for i in range(-kernel + 1, kernel))
|
|
|
|
|
|
def peak_confidence(arr, **kwargs):
|
|
"""
|
|
Evaluate the prominence of the highest peak
|
|
|
|
Args:
|
|
arr (np.ndarray): Shape (N,)
|
|
**kwargs: Additional kwargs for signal.find_peaks
|
|
|
|
Returns:
|
|
float: 0-1
|
|
"""
|
|
para = {
|
|
'height': 0,
|
|
'prominence': 10,
|
|
}
|
|
para.update(kwargs)
|
|
length = len(arr)
|
|
peaks, properties = signal.find_peaks(np.concatenate((arr, arr, arr)), **para)
|
|
peaks = [h for p, h in zip(peaks, properties['peak_heights']) if length <= p < length * 2]
|
|
peaks = sorted(peaks, reverse=True)
|
|
|
|
count = len(peaks)
|
|
if count > 1:
|
|
highest, second = peaks[0], peaks[1]
|
|
elif count == 1:
|
|
highest, second = 1, 0
|
|
else:
|
|
highest, second = 1, 0
|
|
confidence = (highest - second) / highest
|
|
return confidence
|