You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python转C#:heatmap_with_borders变量作用及数组语法疑问

问题解析与C#实现指南

Hey there! Let's break down your Python-to-C# conversion problem and explain the key parts step by step.


1. What does heatmap_with_borders do?

This variable is critical for safe and consistent peak detection in the heatmap:

  • First, np.pad(heatmap, [(2, 2), (2, 2)], mode='constant') adds a 2-pixel wide border of zeros around the original heatmap. If the original heatmap is H×W, the padded version becomes (H+4)×(W+4).
  • This padding solves two big problems:
    1. Avoids index out-of-bounds errors: When checking a pixel's neighbors (left/right/up/down), edge pixels in the original heatmap would have no valid neighbor (e.g., the top-left pixel has no upper or left neighbor). The zero border gives these edge pixels a "virtual neighbor" to compare against.
    2. Unifies peak detection logic: Instead of writing separate code for edge and inner pixels, we can treat every pixel the same way—compare it to its four adjacent neighbors (even if those neighbors are part of the zero border).

The subsequent slices (heatmap_center, heatmap_left, etc.) use this padded array to isolate the original heatmap and its shifted neighbor regions for peak comparison.


2. C# Implementation for Array Slicing & Padding

Since C# doesn't have numpy's built-in slicing/padding, we'll replicate this logic manually. Below is the complete, converted code with explanations:

Full C# Code (Using NumSharp NDArray; adjust for ML.NET Tensor if needed)

using System;
using System.Collections.Generic;
using System.Linq;
using NumSharp;
using static System.Math;

private static int ExtractKeypoints(NDArray heatmap, List<(float X, float Y, float Score, int Id)> allKeypoints, int totalKeypointsNum)
{
    // Step 1: Zero out all heatmap values below 0.1
    int originalRows = heatmap.Shape[0];
    int originalCols = heatmap.Shape[1];
    for (int i = 0; i < originalRows; i++)
    {
        for (int j = 0; j < originalCols; j++)
        {
            if (heatmap[i, j] < 0.1f)
            {
                heatmap[i, j] = 0f;
            }
        }
    }

    // Step 2: Replicate np.pad - add 2-pixel zero border around the heatmap
    var heatmapWithBorders = NDArray.Zeros<float>(originalRows + 4, originalCols + 4);
    for (int i = 0; i < originalRows; i++)
    {
        for (int j = 0; j < originalCols; j++)
        {
            // Copy original heatmap to the center of the padded array
            heatmapWithBorders[i + 2, j + 2] = heatmap[i, j];
        }
    }

    // Step 3: Detect peak points (center > all four neighbors)
    var peaks = new List<(int X, int Y)>();
    for (int i = 0; i < originalRows; i++)
    {
        for (int j = 0; j < originalCols; j++)
        {
            float centerValue = heatmap[i, j];
            if (centerValue == 0) continue; // Skip zero-valued points

            // Get neighbor values from the padded array (no index errors!)
            float left = heatmapWithBorders[i + 2, j + 3];
            float right = heatmapWithBorders[i + 2, j + 1];
            float up = heatmapWithBorders[i + 3, j + 2];
            float down = heatmapWithBorders[i + 1, j + 2];

            // Check if current pixel is a peak
            if (centerValue > left && centerValue > right && centerValue > up && centerValue > down)
            {
                peaks.Add((j, i)); // Match Python's (width, height) coordinate order
            }
        }
    }

    // Step 4: Sort peaks by X coordinate (matches Python's sorted with itemgetter(0))
    peaks = peaks.OrderBy(p => p.X).ToList();

    // Step 5: Non-Maximum Suppression (NMS) - suppress peaks within 6 pixels
    bool[] suppressed = new bool[peaks.Count];
    List<(float X, float Y, float Score, int Id)> keypointsWithMetadata = new List<(float, float, float, int)>();
    int keypointCount = 0;

    for (int i = 0; i < peaks.Count; i++)
    {
        if (suppressed[i]) continue;

        var currentPeak = peaks[i];
        // Mark overlapping peaks as suppressed
        for (int j = i + 1; j < peaks.Count; j++)
        {
            var nextPeak = peaks[j];
            float distance = Sqrt(Pow(currentPeak.X - nextPeak.X, 2) + Pow(currentPeak.Y - nextPeak.Y, 2));
            if (distance < 6)
            {
                suppressed[j] = true;
            }
        }

        // Add valid peak with score and ID to results
        float peakScore = heatmap[currentPeak.Y, currentPeak.X];
        keypointsWithMetadata.Add((currentPeak.X, currentPeak.Y, peakScore, totalKeypointsNum + keypointCount));
        keypointCount++;
    }

    // Append results to global list
    allKeypoints.AddRange(keypointsWithMetadata);
    return keypointCount;
}

Key Notes:

  • Padding Logic: We create a new array with extra space and copy the original heatmap to its center, mimicking np.pad.
  • Neighbor Access: Instead of slicing into separate arrays (like Python), we directly access the padded array's shifted indices to get neighbor values—this is more efficient in C#.
  • Type Safety: We use a value tuple (float X, float Y, float Score, int Id) instead of Python's nested lists for clearer type handling.

内容的提问来源于stack exchange,提问作者Irwin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 09:12:38