get_guiji.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. """手指滑动轨迹录制 → 生成轨迹图片 + JSON数据"""
  2. import sys, os, subprocess, re, time, json
  3. import cv2, numpy as np
  4. DEVICE = sys.argv[1] if len(sys.argv) > 1 else "JRLFJFSCGIGIS4LR"
  5. OUT_DIR = r"D:\pycharm\Project\project\tbsg\guiji_new"
  6. os.makedirs(OUT_DIR, exist_ok=True)
  7. # ── 找触摸设备 ──
  8. r = subprocess.run(["adb", "-s", DEVICE, "shell", "getevent", "-pl"], capture_output=True, text=True, timeout=10)
  9. touch_dev = None
  10. current = None
  11. axis_max_x = None
  12. axis_max_y = None
  13. for line in r.stdout.split('\n'):
  14. m = re.search(r'(/dev/input/event\d+)', line)
  15. if m:
  16. current = m.group(1)
  17. if current and 'ABS_MT_POSITION_X' in line and touch_dev is None:
  18. touch_dev = current
  19. am = re.search(r'max\s+(\d+)', line)
  20. if am:
  21. axis_max_x = int(am.group(1))
  22. elif current == touch_dev and 'ABS_MT_POSITION_Y' in line and axis_max_y is None:
  23. am = re.search(r'max\s+(\d+)', line)
  24. if am:
  25. axis_max_y = int(am.group(1))
  26. if touch_dev and axis_max_x and axis_max_y:
  27. break
  28. if not touch_dev:
  29. for d in ['/dev/input/event3', '/dev/input/event2', '/dev/input/event1']:
  30. r2 = subprocess.run(["adb", "-s", DEVICE, "shell", "getevent", "-pl", d], capture_output=True, text=True, timeout=5)
  31. if 'ABS_MT_POSITION' in r2.stdout:
  32. touch_dev = d
  33. mx = re.search(r'ABS_MT_POSITION_X.*?max\s+(\d+)', r2.stdout, re.S)
  34. my = re.search(r'ABS_MT_POSITION_Y.*?max\s+(\d+)', r2.stdout, re.S)
  35. axis_max_x = int(mx.group(1)) if mx else None
  36. axis_max_y = int(my.group(1)) if my else None
  37. break
  38. if not touch_dev:
  39. touch_dev = "/dev/input/event3"
  40. # ── 获取分辨率 ──
  41. size_r = subprocess.run(["adb", "-s", DEVICE, "shell", "wm", "size"], capture_output=True, text=True)
  42. m = re.search(r'(\d+)x(\d+)', size_r.stdout)
  43. W, H = (int(m.group(1)), int(m.group(2))) if m else (1220, 2712)
  44. print(f"设备: {DEVICE}")
  45. print(f"触摸设备: {touch_dev}")
  46. print(f"屏幕: {W}x{H} 原始触摸范围: {axis_max_x or '?'}x{axis_max_y or '?'}")
  47. print()
  48. print("在手机上滑动手指,手指抬起自动结束...")
  49. print()
  50. # ── 录制 ──
  51. proc = subprocess.Popen(
  52. ["adb", "-s", DEVICE, "shell", "getevent", "-lt", touch_dev],
  53. stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
  54. )
  55. points = []
  56. lx, ly, ts = None, None, None
  57. idle_count = 0
  58. start_ts = None
  59. for line in proc.stdout:
  60. # 解析时间戳 [ 12345.678901]
  61. tm = re.search(r'\[\s*([\d.]+)\]', line)
  62. if tm:
  63. ts = float(tm.group(1))
  64. if 'ABS_MT_POSITION_X' in line:
  65. mm = re.search(r'([0-9a-fA-F]{8})$', line.strip())
  66. if mm:
  67. lx = int(mm.group(1), 16)
  68. elif 'ABS_MT_POSITION_Y' in line:
  69. mm = re.search(r'([0-9a-fA-F]{8})$', line.strip())
  70. if mm:
  71. ly = int(mm.group(1), 16)
  72. elif 'SYN_REPORT' in line and lx is not None and ly is not None:
  73. if start_ts is None:
  74. start_ts = ts
  75. rel_ts = (ts - start_ts) * 1000 if ts and start_ts else 0 # 相对时间ms
  76. raw_x, raw_y = lx, ly
  77. x = int(round(raw_x * (W - 1) / axis_max_x)) if axis_max_x else raw_x
  78. y = int(round(raw_y * (H - 1) / axis_max_y)) if axis_max_y else raw_y
  79. points.append({"x": x, "y": y, "raw_x": raw_x, "raw_y": raw_y,
  80. "ts": ts, "rel_ms": round(rel_ts, 2)})
  81. idle_count = 0
  82. if len(points) % 15 == 0:
  83. print(f"\r 已录 {len(points)} 点 (x={x}, y={y})", end="", flush=True)
  84. elif 'BTN_TOUCH' in line and 'UP' in line:
  85. if len(points) > 3:
  86. print("\n 检测到手指抬起")
  87. break
  88. elif 'ABS_MT_TRACKING_ID' in line and 'ffffffff' in line:
  89. if len(points) > 3:
  90. print("\n 检测到手指抬起")
  91. break
  92. else:
  93. idle_count += 1
  94. if idle_count > 300 and len(points) > 5:
  95. print("\n 超时自动结束")
  96. break
  97. proc.kill()
  98. print(f"\n共录制 {len(points)} 点")
  99. if len(points) < 3:
  100. print("点数太少,请重试(确保触摸了屏幕)")
  101. sys.exit(1)
  102. xs = [p["x"] for p in points]
  103. ys = [p["y"] for p in points]
  104. # ── 保存JSON ──
  105. ts_str = time.strftime("%Y%m%d_%H%M%S") + f"_{time.time_ns() % 1000000:06d}"
  106. json_path = os.path.join(OUT_DIR, f"track_{ts_str}.json")
  107. data = {
  108. "device": DEVICE,
  109. "screen": {"w": W, "h": H},
  110. "touch_axis": {"max_x": axis_max_x, "max_y": axis_max_y},
  111. "start_ts": start_ts,
  112. "total_points": len(points),
  113. "start": {"x": xs[0], "y": ys[0]},
  114. "end": {"x": xs[-1], "y": ys[-1]},
  115. "dx": xs[-1] - xs[0],
  116. "dy": ys[-1] - ys[0],
  117. "duration_ms": round(points[-1]["rel_ms"], 2) if points else 0,
  118. "points": points
  119. }
  120. with open(json_path, 'w', encoding='utf-8') as f:
  121. json.dump(data, f, ensure_ascii=False)
  122. print(f"数据: {json_path}")
  123. # ── 画轨迹: 多种颜色 ──
  124. margin_x = 40
  125. margin_y = 300 # 上下大留白, 轨迹居中
  126. min_x, max_x = min(xs), max(xs)
  127. min_y, max_y = min(ys), max(ys)
  128. width = max_x - min_x + margin_x * 2
  129. height = max_y - min_y + margin_y * 2
  130. width, height = max(width, 200), max(height, 800)
  131. canvas = np.ones((height, width, 3), dtype=np.uint8) * 255
  132. total = len(points)
  133. # 找折回起点: X开始持续减小
  134. peak_i = 0
  135. for i in range(1, total):
  136. if xs[i] >= xs[peak_i]:
  137. peak_i = i
  138. else:
  139. break
  140. # 计算右段Y波动阈值
  141. right_ys = ys[:peak_i+1]
  142. y_diffs = [abs(right_ys[j] - right_ys[j-1]) for j in range(1, len(right_ys))]
  143. avg_y_diff = sum(y_diffs) / len(y_diffs) if y_diffs else 0
  144. for i in range(1, total):
  145. p1 = (xs[i-1] - min_x + margin_x, ys[i-1] - min_y + margin_y)
  146. p2 = (xs[i] - min_x + margin_x, ys[i] - min_y + margin_y)
  147. if i <= peak_i:
  148. color = (0, 200, 0) # 绿色=往右
  149. else:
  150. color = (0, 0, 200) # 红色=折回
  151. cv2.line(canvas, p1, p2, color, 30)
  152. # 起点(绿) 折回点(黄) 终点(红)
  153. p0 = (xs[0] - min_x + margin_x, ys[0] - min_y + margin_y)
  154. pp = (xs[peak_i] - min_x + margin_x, ys[peak_i] - min_y + margin_y)
  155. pe = (xs[-1] - min_x + margin_x, ys[-1] - min_y + margin_y)
  156. cv2.circle(canvas, p0, 16, (0, 200, 0), -1)
  157. cv2.circle(canvas, pp, 16, (0, 200, 200), -1)
  158. cv2.circle(canvas, pe, 16, (0, 0, 200), -1)
  159. cv2.putText(canvas, "START", (p0[0]+20, p0[1]+5), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,150,0), 3)
  160. cv2.putText(canvas, "TURN", (pp[0]+20, pp[1]+5), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200,150,0), 3)
  161. cv2.putText(canvas, "END", (pe[0]+20, pe[1]+5), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,150), 3)
  162. info = f"GREEN=right | RED=return | S({xs[0]},{ys[0]}) E({xs[-1]},{ys[-1]}) | {xs[-1]-xs[0]}px | {data['duration_ms']}ms"
  163. cv2.putText(canvas, info, (margin_x, height - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (120,120,120), 1)
  164. img_path = os.path.join(OUT_DIR, f"track_{ts_str}.png")
  165. cv2.imwrite(img_path, canvas)
  166. print(f"图片: {img_path}")
  167. print(f"起点({xs[0]},{ys[0]}) -> 终点({xs[-1]},{ys[-1]})")
  168. print(f"X: {xs[-1]-xs[0]}px Y: {ys[-1]-ys[0]}px 时长: {data['duration_ms']}ms")