Skip to content

Commit

Permalink
perf: divide image to scale down
Browse files Browse the repository at this point in the history
  • Loading branch information
necusjz committed Dec 17, 2023
1 parent d8f05a6 commit a9eae1b
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 29 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
- Scale down through divide and label image.
- Optimize pathing via TSP.

## [0.3.0] - 2023-12-15
- Provide benchmark scripts.
Expand All @@ -15,7 +17,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support dry run for plotting.

## [0.1.0] - 2023-12-10

- Enable splatbot to start plotting.
- Enable splatbot to generate macro.
- Add controller abstraction for Nintendo Switch.
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ dependencies = [
"matplotlib>=3.6.3",
"numpy>=1.23.2",
"nxbt>=0.1.4",
"scikit-image>=0.19.3",
"tqdm>=4.64.0",
"tsp-solver2>=0.4.1",
]

[project.optional-dependencies]
Expand Down
70 changes: 45 additions & 25 deletions src/splatbot/macro.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,34 @@
from tsp_solver.greedy_numpy import solve_tsp


def label_routes(matrix):
def divide_image(matrix):
submatrices = []
for i in range(0, 120, 40):
if not (i // 40) & 1: # zigzag
for j in range(0, 320, 40):
submatrices.append(((i, j), matrix[i:i+40, j:j+40]))
else:
for j in range(280, -40, -40):
submatrices.append(((i, j), matrix[i:i+40, j:j+40]))

return submatrices


def get_delta(p1, p2):
return map(lambda x, y: x - y, p2, p1)


def goto_next(p1, p2, plot=True):
commands = []
delta_x, delta_y = get_delta(p1, p2)

commands += ["u", "d"][int(delta_x > 0)] * abs(delta_x)
commands += ["l", "r"][int(delta_y > 0)] * abs(delta_y)

return commands + ["a"] if plot else commands


def label_routes(matrix, offset):
matrix, count = label(matrix, return_num=True)

routes = []
Expand All @@ -18,13 +45,13 @@ def label_routes(matrix):
route = []
for point in np.argwhere(condition):
if point[0] == curr_row:
points.append(point)
points.append(point + offset)
else:
route += points if is_right else points[::-1]

curr_row = point[0]
is_right = not is_right
points = [point]
points = [point + offset]

route += points if is_right else points[::-1]

Expand All @@ -33,15 +60,11 @@ def label_routes(matrix):
return routes


def get_delta(p1, p2):
return map(lambda x, y: x - y, p2, p1)


def manh(p1, p2):
return sum(map(abs, get_delta(p1, p2)))


def cal_distance_matrix(endpoints):
def calculate_distance_matrix(endpoints):
n = len(endpoints)
distance_matrix = np.zeros((n, n))

Expand All @@ -55,27 +78,24 @@ def cal_distance_matrix(endpoints):
return distance_matrix


def goto_next(p1, p2):
commands = []
delta_x, delta_y = get_delta(p1, p2)

commands += ["u", "d"][int(delta_x > 0)] * abs(delta_x)
commands += ["l", "r"][int(delta_y > 0)] * abs(delta_y)

return commands + ["a"]
def pathing(matrix):
commands, current = [], (0, 0)
for offset, submatrix in divide_image(matrix):
if np.all(submatrix == 0):
continue

commands += goto_next(current, offset, plot=False)
current = offset

def pathing(matrix):
routes = label_routes(matrix)
endpoints = [(route[0], route[-1]) for route in routes]
routes = label_routes(submatrix, offset)
endpoints = [(route[0], route[-1]) for route in routes]

distances = cal_distance_matrix(endpoints)
distances = calculate_distance_matrix(endpoints)

commands, current = [], (0, 0)
for i in solve_tsp(distances, optim_steps=16, endpoints=(0, None)):
for point in routes[i]:
commands += goto_next(current, point)
current = point
for i in solve_tsp(distances, optim_steps=16, endpoints=(0, None)): # do not specify exit
for point in routes[i]:
commands += goto_next(current, point)
current = point

return commands + ["b"] # save and quit

Expand Down
7 changes: 4 additions & 3 deletions tests/test_macro.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from src.splatbot.macro import pathing


def cal_result(curr, prev):
def calculate_result(curr, prev):
return f"{prev / curr:.2f}x faster" if curr < prev else f"{curr / prev:.2f}x slower"


Expand All @@ -26,14 +26,15 @@ def test_pathing():

curr = len(pathing(matrix))
prev = 120 * 320 + np.count_nonzero(matrix)
result = cal_result(curr, prev)
result = calculate_result(curr, prev)

data.append((image.name, curr, prev, result))

curr_sum += curr
prev_sum += prev

data.append(("Geometric Mean", "N/A", "N/A", cal_result(curr_sum, prev_sum)))
total = calculate_result(curr_sum, prev_sum)
data.append(("Geometric Mean", "N/A", "N/A", total))

headers = ["Benchmark", "Current", "Previous", "Result"]

Expand Down

0 comments on commit a9eae1b

Please sign in to comment.