LimbusPower / LimbusAutoRun

边狱公司LimbusCompany自动下镜本工具
3 stars 0 forks source link

自动读图功能? #1

Open LucunJi opened 1 year ago

LucunJi commented 1 year ago

学着用 opencv 做了一下读图功能,现在大致可以匹配出地牢节点之间的连线了。 流程是:二值化——匹配圆来找出并删除有干扰的高亮节点——拟合直线——直线聚类——移除小线段 直线聚类的代码抄的 stackoverflow,感觉要实际使用还要改改。

其实还有一种办法是先用 sift 特征匹配找出地牢全部节点,然后聚类,再把直线给找到。

第三层地牢的图可能缩到最小都看不全,先要上个图像拼接才行。

我是第一次做这个,没什么经验。如果有更成熟的方案就好了。

代码

展开代码 ``` py import cv2 as cv import numpy as np from matplotlib import pyplot as plt import time # ref https://stackoverflow.com/questions/45322630/how-to-detect-lines-in-opencv # https://stackoverflow.com/questions/45531074/how-to-merge-lines-after-houghlinesp begin = time.time() __subplot = plt.subplot def _subplot(*args, **kwargs): print('time elapsed:', time.time() - begin) return __subplot(*args, **kwargs) plt.subplot = _subplot full = cv.imread("/home/wired/Repositories/LimbusAutoRun/testimgs/8.jpg") cut = int(full.shape[0] * 0.17) full = full[cut:-cut] Hfull, Wfull, _ = full.shape gray = cv.cvtColor(full, cv.COLOR_BGR2GRAY) gray = cv.inRange(gray, 110, 255) gray = cv.morphologyEx(gray, cv.MORPH_OPEN, np.ones((3, 3), dtype=np.uint8)) plt.subplot(3, 2, 1).imshow(gray) blurred = cv.GaussianBlur(gray, (7, 7), 0) edges = cv.Canny(blurred, 50, 200) circles = cv.HoughCircles(edges, cv.HOUGH_GRADIENT, 1, 10, param1=100, param2=28, minRadius=5, maxRadius=50) circ_image = edges.copy() if circles is not None: circles = np.uint16(np.around(circles)) for i in circles[0, :]: i[2] *= 1.3 cv.circle(circ_image, (i[0], i[1]), i[2], (255, 0, 255), thickness=2) cv.circle(edges, (i[0], i[1]), i[2], 0, thickness=cv.FILLED) plt.subplot(3, 2, 2).imshow(circ_image) lines = cv.HoughLinesP(edges, 1, # distance resolution in pixels of the Hough grid np.pi / 180, # angular resolution in radians of the Hough grid 15, # minimum number of votes (intersections in Hough grid cell) np.array([]), 10, # minimum number of pixels making up a line 10 # maximum gap in pixels between connectable line segments ) line_image = np.copy(full) for line in lines: for x1, y1, x2, y2 in line: cv.line(line_image, (x1, y1), (x2, y2), (255, 0, 0), 5) plt.subplot(3, 2, 3).imshow(line_image) class HoughBundler: def __init__(self, min_distance=5, min_angle=2): self.min_distance = min_distance self.min_angle = min_angle def get_orientation(self, line): orientation = np.arctan2(abs((line[3] - line[1])), abs((line[2] - line[0]))) return np.degrees(orientation) def check_is_line_different(self, line_1, groups, min_distance_to_merge, min_angle_to_merge): for group in groups: for line_2 in group: if self.get_distance(line_2, line_1) < min_distance_to_merge: orientation_1 = self.get_orientation(line_1) orientation_2 = self.get_orientation(line_2) if abs(orientation_1 - orientation_2) < min_angle_to_merge: group.append(line_1) return False return True def distance_point_to_line(self, point, line): px, py = point x1, y1, x2, y2 = line def line_magnitude(x1, y1, x2, y2): line_magnitude = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) return line_magnitude lmag = line_magnitude(x1, y1, x2, y2) if lmag < 0.00000001: distance_point_to_line = 9999 return distance_point_to_line u1 = (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1))) u = u1 / (lmag * lmag) if (u < 0.00001) or (u > 1): # // the closest point does not fall within the line segment, take the shorter distance # // to an endpoint ix = line_magnitude(px, py, x1, y1) iy = line_magnitude(px, py, x2, y2) if ix > iy: distance_point_to_line = iy else: distance_point_to_line = ix else: # Intersecting point is on the line, use the formula ix = x1 + u * (x2 - x1) iy = y1 + u * (y2 - y1) distance_point_to_line = line_magnitude(px, py, ix, iy) return distance_point_to_line def get_distance(self, a_line, b_line): dist1 = self.distance_point_to_line(a_line[:2], b_line) dist2 = self.distance_point_to_line(a_line[2:], b_line) dist3 = self.distance_point_to_line(b_line[:2], a_line) dist4 = self.distance_point_to_line(b_line[2:], a_line) return min(dist1, dist2, dist3, dist4) def merge_lines_into_groups(self, lines): groups = [] # all lines groups are here # first line will create new group every time groups.append([lines[0]]) # if line is different from existing gropus, create a new group for line_new in lines[1:]: if self.check_is_line_different(line_new, groups, self.min_distance, self.min_angle): groups.append([line_new]) return groups def merge_line_segments(self, lines): orientation = self.get_orientation(lines[0]) if (len(lines) == 1): return np.block([[lines[0][:2], lines[0][2:]]]) points = [] for line in lines: points.append(line[:2]) points.append(line[2:]) if 45 < orientation <= 90: # sort by y points = sorted(points, key=lambda point: point[1]) else: # sort by x points = sorted(points, key=lambda point: point[0]) return np.block([[points[0], points[-1]]]) def process_lines(self, lines): lines_horizontal = [] lines_vertical = [] for line_i in [l[0] for l in lines]: orientation = self.get_orientation(line_i) # if vertical if 45 < orientation <= 90: lines_vertical.append(line_i) else: lines_horizontal.append(line_i) lines_vertical = sorted(lines_vertical, key=lambda line: line[1]) lines_horizontal = sorted(lines_horizontal, key=lambda line: line[0]) merged_lines_all = [] # for each cluster in vertical and horizantal lines leave only one line for i in [lines_horizontal, lines_vertical]: if len(i) > 0: groups = self.merge_lines_into_groups(i) merged_lines = [] for group in groups: merged_lines.append(self.merge_line_segments(group)) merged_lines_all.extend(merged_lines) return np.asarray(merged_lines_all) bundler = HoughBundler(min_distance=30, min_angle=5) lines = bundler.process_lines(lines) line_image = np.copy(full) for line in lines: for x1, y1, x2, y2 in line: cv.line(line_image, (x1, y1), (x2, y2), (255, 0, 0), 5) plt.subplot(3, 2, 4).imshow(line_image) lines = sorted(lines, key=lambda x: ((x[0, :2] - x[0, 2:]) ** 2).sum()) lengths = np.array(list(map(lambda x: ((x[0, :2] - x[0, 2:]) ** 2).sum()**0.5, lines))) # split_idx = len(lengths) - 1 # while split_idx > 0 and lengths[split_idx-1] > lengths[split_idx] / 1.6: # split_idx -= 1 theta = np.arctan((lengths[-1] - lengths[0]) / len(lengths)) split_idx = np.argmax(np.abs(np.cos(theta) * (lengths[0] - lengths) + np.arange(len(lengths)))) lines = lines[split_idx + 1:] ax = plt.subplot(3, 2, 5) ax.plot(range(0, len(lengths)), lengths) line_image = np.copy(full) for line in lines: for x1, y1, x2, y2 in line: cv.line(line_image, (x1, y1), (x2, y2), (255, 0, 0), 5) plt.subplot(3, 2, 6).imshow(line_image) plt.show() ```

一层和二层的效果

Figure_1 Figure_2

LucunJi commented 1 year ago

https://colab.research.google.com/drive/1Lhmm6GpyVk9EKuojLliZW8Vt8OarodgS?usp=sharing