balthou commited on
Commit
3adf118
·
1 Parent(s): 0639f56

add success ribons + difficulty (patch size)

Browse files
Files changed (1) hide show
  1. app.py +54 -14
app.py CHANGED
@@ -1,4 +1,5 @@
1
  from interactive_pipe import interactive_pipeline, interactive, Image
 
2
  import numpy as np
3
 
4
  # Helper functions
@@ -21,19 +22,50 @@ def get_crop(img, pos_x, pos_y, crop_size=0.1):
21
  # -----------------
22
 
23
 
24
- @interactive(seed=(45, [0, 100], "Puzzle seed"))
25
- def generate_random_puzzle(seed: int = 45, context: dict = {}):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  np.random.seed(seed)
27
  pos_x, pos_y = np.random.uniform(0.2, 0.8, 2)
28
  context["puzzle_pos"] = (pos_x, pos_y)
29
  context["puzzle_flip_mirror"] = np.random.choice([True, False], 2)
 
30
 
31
 
32
- def create_puzzle(img, intensity: float = 0.4, context: dict = {}):
 
 
 
 
 
33
  out = img.copy()
34
  x_gt, y_gt = context["puzzle_pos"]
35
  flip_gt, mirror_gt = context["puzzle_flip_mirror"]
36
- cs_x, cs_y = get_crop(img, x_gt, y_gt)
 
37
  crop = img[cs_y[0]:cs_y[1], cs_x[0]:cs_x[1], ...]
38
  out[cs_y[0]:cs_y[1], cs_x[0]:cs_x[1]] = intensity*crop
39
  crop = flip_image(crop, flip=flip_gt, mirror=mirror_gt)
@@ -50,6 +82,7 @@ def flip_mirror_piece(
50
  mirror: bool = False,
51
  context: dict = {}
52
  ) -> np.ndarray:
 
53
  context["user_flip_mirror"] = (flip, mirror)
54
  return flip_image(piece.copy(), flip=flip, mirror=mirror)
55
 
@@ -65,9 +98,11 @@ def place_puzzle(
65
  pos_y: float = 0.5,
66
  context: dict = {}
67
  ) -> np.ndarray:
 
68
  out = puzzle.copy()
69
  context["user_pos"] = (pos_x, pos_y)
70
- cp_x, cp_y = get_crop(img, pos_x, pos_y)
 
71
  out[cp_y[0]:cp_y[1], cp_x[0]:cp_x[1]] = piece
72
  return out
73
 
@@ -79,7 +114,8 @@ TOLERANCE_LEVELS = list(TOLERANCES.keys())
79
  @interactive(
80
  tolerance=(TOLERANCE_LEVELS[0], TOLERANCE_LEVELS, "Tolerance")
81
  )
82
- def check_puzzle(tolerance: str = "low", context: dict = {}):
 
83
  x_gt, y_gt = context["puzzle_pos"]
84
  flip_gt, mirror_gt = context["puzzle_flip_mirror"]
85
  x, y = context["user_pos"]
@@ -91,26 +127,30 @@ def check_puzzle(tolerance: str = "low", context: dict = {}):
91
  context["success"] = success
92
 
93
 
94
- def show_feedback(context: dict = {}):
 
 
 
 
 
95
  success = context["success"]
96
- flat_array = np.ones((256, 256, 3))
97
- if success:
98
- return flat_array*np.array([0., 1., 0.])[None, None, :]
99
- else:
100
- return flat_array*np.array([1., 0., 0.])[None, None, :]
101
 
102
  # pipeline definition
103
  # -------------------
104
 
105
 
106
  def captcha_pipe(inp):
 
107
  generate_random_puzzle()
108
  puzzle, puzzle_piece = create_puzzle(inp)
109
  puzzle_piece = flip_mirror_piece(puzzle_piece)
110
  puzzle = place_puzzle(puzzle, puzzle_piece)
111
  check_puzzle()
112
- validity = show_feedback()
113
- return [puzzle_piece, puzzle, validity]
114
 
115
 
116
  if __name__ == "__main__":
 
1
  from interactive_pipe import interactive_pipeline, interactive, Image
2
+ from typing import Tuple
3
  import numpy as np
4
 
5
  # Helper functions
 
22
  # -----------------
23
 
24
 
25
+ def generate_feedback_ribbon() -> Tuple[np.ndarray, np.ndarray]:
26
+ """Generate green and red ribbons for feedback"""
27
+ flat_array = np.ones((800, 12, 3))
28
+ colors = [[0., 1., 0.], [1., 0., 0.]]
29
+ ribbons = [flat_array*np.array(col)[None, None, :] for col in colors]
30
+ return ribbons[0], ribbons[1]
31
+
32
+
33
+ DIFFICULY = {"easy": 0.18, "medium": 0.1, "hard": 0.05}
34
+ DIFFICULY_LEVELS = list(DIFFICULY.keys())
35
+
36
+
37
+ @interactive(
38
+ seed=(45, [0, 100], "Puzzle seed"),
39
+ difficulty=(DIFFICULY_LEVELS[0], DIFFICULY_LEVELS, "Difficulty")
40
+ )
41
+ def generate_random_puzzle(
42
+ seed: int = 45,
43
+ difficulty: str = DIFFICULY_LEVELS[0],
44
+ context: dict = {}
45
+ ):
46
+ """Generate random puzzle configuration and store in context.
47
+
48
+ Configuration = 2D position and flip/mirror.
49
+ Freeze seed for reproducibility.
50
+ """
51
  np.random.seed(seed)
52
  pos_x, pos_y = np.random.uniform(0.2, 0.8, 2)
53
  context["puzzle_pos"] = (pos_x, pos_y)
54
  context["puzzle_flip_mirror"] = np.random.choice([True, False], 2)
55
+ context["puzzle_piece_size"] = DIFFICULY.get(difficulty, 0.18)
56
 
57
 
58
+ def create_puzzle(
59
+ img: np.ndarray,
60
+ intensity: float = 0.4,
61
+ context: dict = {}
62
+ ) -> Tuple[np.ndarray, np.ndarray]:
63
+ """Extract puzzle piece from image. Make a dark hole where the """
64
  out = img.copy()
65
  x_gt, y_gt = context["puzzle_pos"]
66
  flip_gt, mirror_gt = context["puzzle_flip_mirror"]
67
+ cs_x, cs_y = get_crop(
68
+ img, x_gt, y_gt, crop_size=context["puzzle_piece_size"])
69
  crop = img[cs_y[0]:cs_y[1], cs_x[0]:cs_x[1], ...]
70
  out[cs_y[0]:cs_y[1], cs_x[0]:cs_x[1]] = intensity*crop
71
  crop = flip_image(crop, flip=flip_gt, mirror=mirror_gt)
 
82
  mirror: bool = False,
83
  context: dict = {}
84
  ) -> np.ndarray:
85
+ """Flip and/or mirror the puzzle piece."""
86
  context["user_flip_mirror"] = (flip, mirror)
87
  return flip_image(piece.copy(), flip=flip, mirror=mirror)
88
 
 
98
  pos_y: float = 0.5,
99
  context: dict = {}
100
  ) -> np.ndarray:
101
+ """Place the puzzle piece at the user-defined position."""
102
  out = puzzle.copy()
103
  context["user_pos"] = (pos_x, pos_y)
104
+ cp_x, cp_y = get_crop(
105
+ img, pos_x, pos_y, crop_size=context["puzzle_piece_size"])
106
  out[cp_y[0]:cp_y[1], cp_x[0]:cp_x[1]] = piece
107
  return out
108
 
 
114
  @interactive(
115
  tolerance=(TOLERANCE_LEVELS[0], TOLERANCE_LEVELS, "Tolerance")
116
  )
117
+ def check_puzzle(tolerance: str = "low", context: dict = {}) -> None:
118
+ """Check if the user placed the puzzle piece correctly."""
119
  x_gt, y_gt = context["puzzle_pos"]
120
  flip_gt, mirror_gt = context["puzzle_flip_mirror"]
121
  x, y = context["user_pos"]
 
127
  context["success"] = success
128
 
129
 
130
+ def display_feedback(
131
+ puzzle: np.ndarray,
132
+ ok_ribbon: np.ndarray,
133
+ nok_ribbon: np.ndarray,
134
+ context: dict = {}
135
+ ) -> np.ndarray:
136
  success = context["success"]
137
+ ribbon = ok_ribbon if success else nok_ribbon
138
+ out = np.hstack([puzzle, ribbon[:puzzle.shape[0], ...]])
139
+ return out
 
 
140
 
141
  # pipeline definition
142
  # -------------------
143
 
144
 
145
  def captcha_pipe(inp):
146
+ ok_ribbon, nok_ribbon = generate_feedback_ribbon()
147
  generate_random_puzzle()
148
  puzzle, puzzle_piece = create_puzzle(inp)
149
  puzzle_piece = flip_mirror_piece(puzzle_piece)
150
  puzzle = place_puzzle(puzzle, puzzle_piece)
151
  check_puzzle()
152
+ puzzle = display_feedback(puzzle, ok_ribbon, nok_ribbon)
153
+ return puzzle
154
 
155
 
156
  if __name__ == "__main__":