| 246 | // ═══════════════════════════════════════════════════════════════════════════════ |
| 247 | |
| 248 | static void test_encode_image(const std::string & model_path, |
| 249 | const std::string & image_path, |
| 250 | const std::string & ref_dir, |
| 251 | int & n_pass, int & n_fail) { |
| 252 | fprintf(stderr, "\n╔══════════════════════════════════════════╗\n"); |
| 253 | fprintf(stderr, "║ Test: Full Image Encoding ║\n"); |
| 254 | fprintf(stderr, "╚══════════════════════════════════════════╝\n"); |
| 255 | |
| 256 | sam3_params params; |
| 257 | params.model_path = model_path; |
| 258 | params.use_gpu = false; |
| 259 | params.n_threads = 4; |
| 260 | |
| 261 | auto model = sam3_load_model(params); |
| 262 | if (!model) { |
| 263 | fprintf(stderr, " FATAL: Failed to load model\n"); |
| 264 | n_fail++; |
| 265 | return; |
| 266 | } |
| 267 | |
| 268 | auto state = sam3_create_state(*model, params); |
| 269 | if (!state) { |
| 270 | fprintf(stderr, " FATAL: Failed to create state\n"); |
| 271 | n_fail++; |
| 272 | return; |
| 273 | } |
| 274 | |
| 275 | auto img = sam3_load_image(image_path); |
| 276 | if (img.data.empty()) { |
| 277 | fprintf(stderr, " FATAL: Failed to load image\n"); |
| 278 | n_fail++; |
| 279 | return; |
| 280 | } |
| 281 | |
| 282 | bool ok = sam3_encode_image(*state, *model, img); |
| 283 | if (!ok) { |
| 284 | fprintf(stderr, " FATAL: sam3_encode_image failed\n"); |
| 285 | n_fail++; |
| 286 | return; |
| 287 | } |
| 288 | |
| 289 | fprintf(stderr, " Image encoding completed successfully\n"); |
| 290 | |
| 291 | std::string dump_dir = ref_dir + "/cpp_out_phase3"; |
| 292 | { |
| 293 | std::string cmd = "mkdir -p " + dump_dir; |
| 294 | (void)system(cmd.c_str()); |
| 295 | } |
| 296 | |
| 297 | // ── Helper: dump and compare a state tensor ────────────────────────── |
| 298 | // C++ tensors are in ggml layout. We need to transpose to PyTorch layout. |
| 299 | |
| 300 | // For NHWC tensors (ViT intermediates): ggml [E, W, H, B] → PyTorch [B, H, W, E] |
| 301 | auto compare_nhwc = [&](const std::string & cpp_name, const std::string & ref_name, |
| 302 | float atol) { |
| 303 | bool dumped = sam3_dump_state_tensor(*state, cpp_name, dump_dir + "/" + cpp_name); |
| 304 | if (!dumped) { |
| 305 | fprintf(stderr, " [SKIP] %s — tensor '%s' not available in state\n", |
no test coverage detected