CPU-side ROI Align matching torchvision.ops.roi_align behavior. Features in ggml [C, W, H] layout. Box in XYXY format scaled to feature grid coords. Uses sub-sampling matching torchvision's sampling_ratio=0 (auto). Output: [C * roi_size * roi_size] in ggml layout.
| 7605 | // Uses sub-sampling matching torchvision's sampling_ratio=0 (auto). |
| 7606 | // Output: [C * roi_size * roi_size] in ggml layout. |
| 7607 | static void sam3_roi_align_single( |
| 7608 | const float* feats, // [C, W, H] ggml layout (C innermost, then W, then H) |
| 7609 | int C, int W_feat, int H_feat, |
| 7610 | float x0, float y0, float x1, float y1, |
| 7611 | int roi_size, |
| 7612 | float* out) // [C, roi_size, roi_size] |
| 7613 | { |
| 7614 | float roi_w = std::max(x1 - x0, 1e-6f); |
| 7615 | float roi_h = std::max(y1 - y0, 1e-6f); |
| 7616 | float bin_w = roi_w / (float)roi_size; |
| 7617 | float bin_h = roi_h / (float)roi_size; |
| 7618 | |
| 7619 | // Match torchvision sampling_ratio=0: use ceil(bin_size) sub-samples |
| 7620 | int sample_y_count = std::max(1, (int)ceilf(bin_h)); |
| 7621 | int sample_x_count = std::max(1, (int)ceilf(bin_w)); |
| 7622 | float inv_count = 1.0f / (float)(sample_y_count * sample_x_count); |
| 7623 | |
| 7624 | auto bilinear_sample = [&](float sx, float sy, int c) -> float { |
| 7625 | // Clamp to [-0.5, W-0.5] then adjust — matching torchvision |
| 7626 | if (sx < -1.0f || sx > (float)W_feat || sy < -1.0f || sy > (float)H_feat) |
| 7627 | return 0.0f; |
| 7628 | |
| 7629 | sy = std::max(0.0f, sy); |
| 7630 | sx = std::max(0.0f, sx); |
| 7631 | |
| 7632 | int y_lo = (int)sy; |
| 7633 | int x_lo = (int)sx; |
| 7634 | int y_hi = std::min(y_lo + 1, H_feat - 1); |
| 7635 | int x_hi = std::min(x_lo + 1, W_feat - 1); |
| 7636 | y_lo = std::min(y_lo, H_feat - 1); |
| 7637 | x_lo = std::min(x_lo, W_feat - 1); |
| 7638 | |
| 7639 | float ly = sy - (float)y_lo; |
| 7640 | float lx = sx - (float)x_lo; |
| 7641 | |
| 7642 | // ggml layout: feats[c + x * C + y * C * W_feat] |
| 7643 | float v00 = feats[c + x_lo * C + y_lo * C * W_feat]; |
| 7644 | float v10 = feats[c + x_hi * C + y_lo * C * W_feat]; |
| 7645 | float v01 = feats[c + x_lo * C + y_hi * C * W_feat]; |
| 7646 | float v11 = feats[c + x_hi * C + y_hi * C * W_feat]; |
| 7647 | |
| 7648 | return v00 * (1 - ly) * (1 - lx) + v10 * (1 - ly) * lx + v01 * ly * (1 - lx) + v11 * ly * lx; |
| 7649 | }; |
| 7650 | |
| 7651 | for (int ph = 0; ph < roi_size; ++ph) { |
| 7652 | for (int pw = 0; pw < roi_size; ++pw) { |
| 7653 | for (int c = 0; c < C; ++c) { |
| 7654 | float sum = 0.0f; |
| 7655 | for (int iy = 0; iy < sample_y_count; ++iy) { |
| 7656 | float sy = y0 + bin_h * ((float)ph + ((float)iy + 0.5f) / (float)sample_y_count); |
| 7657 | for (int ix = 0; ix < sample_x_count; ++ix) { |
| 7658 | float sx = x0 + bin_w * ((float)pw + ((float)ix + 0.5f) / (float)sample_x_count); |
| 7659 | sum += bilinear_sample(sx, sy, c); |
| 7660 | } |
| 7661 | } |
| 7662 | out[c + pw * C + ph * C * roi_size] = sum * inv_count; |
| 7663 | } |
| 7664 | } |
no outgoing calls
no test coverage detected