C# 获取图像区域(如地图上的国家)的二维坐标轮廓

C# 获取图像区域(如地图上的国家)的二维坐标轮廓,c#,asp.net,.net,image-processing,coordinates,C#,Asp.net,.net,Image Processing,Coordinates,我将如何为一幅图像的某个区域生成二维坐标,例如,如果这张地图上的一个国家被挑出来,并且是唯一可见的国家:但是在同样大小的画布上,我将如何获取它的二维坐标 由于我想使用c#基于这些坐标创建悬停/单击区域,因此我找不到一种工具可以检测空白画布中的形状,并吐出其轮廓坐标 我主要认为这是我的一个措辞/术语问题,因为我觉得整个过程已经是一件“事情”,并且有很好的记录 您可以使用opencv的findcontour()函数。请参阅此处的文档:。实现任务的方法有很多,这里有几个: 看看哪一个几乎和你的一样,但

我将如何为一幅图像的某个区域生成二维坐标,例如,如果这张地图上的一个国家被挑出来,并且是唯一可见的国家:但是在同样大小的画布上,我将如何获取它的二维坐标

由于我想使用c#基于这些坐标创建悬停/单击区域,因此我找不到一种工具可以检测空白画布中的形状,并吐出其轮廓坐标


我主要认为这是我的一个措辞/术语问题,因为我觉得整个过程已经是一件“事情”,并且有很好的记录

您可以使用opencv的findcontour()函数。请参阅此处的文档:。

实现任务的方法有很多,这里有几个:

看看哪一个几乎和你的一样,但起点有点不同

简而言之:

  • 提取相邻白色像素的所有非白色像素

    只要在整个图像中循环(除了外边界像素),如果处理后的像素不是白色的,则查看处理后像素的4/8邻居。如果其中任何一个颜色不同,则将处理后的像素颜色和坐标添加到列表中

  • 按颜色对点列表排序

    这将导致国家分裂

  • 应用闭环/连通性分析

    这是矢量化/多边形化过程。只是从列表中加入尚未使用的相邻像素以形成线条

  • 还有一种*替代方案可能更容易实现:

  • 提取相邻白色像素的所有非白色像素

    只要在整个图像中循环(除了外边界像素),如果处理后的像素不是白色的,则查看处理后像素的4/8邻居。如果它们都不是不同的颜色,则使用一些未使用的颜色(黑色)清除当前像素

  • 将所有白色和透明颜色重新着色为单色(黑色)。

    从这一点上看,红色意味着墙壁

  • 应用路径查找

    找到第一个非墙壁像素,并应用类似*的生长填充。完成填充后,只需记住列表中点的顺序,即多边形。可选地将直线像素连接到单线

  • 另一个选项是调整此

    [notes]


    如果您的图像经过过滤(消除混叠、缩放等),则需要进行颜色比较,并留有一定的误差,甚至可能与HSV连接(取决于颜色失真的程度)。

    我认为您的做法是错误的。大陆的轮廓是疯狂的;它们通常由几个部分和许多小岛组成。而且,你不需要图像上大陆的坐标;如果您的当前坐标在列表中,则查找将花费太长的时间。相反,您应该做相反的事情:为整个图像制作一个索引表,在索引表上为它所属的每个像素指示

    这要容易得多

    由于您显然必须为每个大陆指定一种颜色来识别它们,因此您可以检查图像的所有像素,将每个像素的颜色与大陆颜色中最接近的匹配匹配,并用相应的找到的大陆索引填充数组中的每个字节。这样,您就得到了一个直接引用您的大陆数组的字节数组。实际上,这意味着您可以创建一个索引的8位图像,就像普通字节数组一样。(请注意,有一些方法可以将其与颜色阵列结合起来,得到一幅您可以使用的图像。这并不难。)

    对于实际的颜色匹配,最佳做法是在源图像上使用锁位来直接访问底层字节数组。在下面的代码中,调用
    GetImageData
    可以获取字节和数据步长。然后,您可以迭代每行的字节数,并从代表一个像素的每个数据块构建颜色。如果您不想为支持不同的像素大小(如24bpp)而操心太多,那么一个简单的技巧就是只在相同尺寸的新32bpp图像上绘制源图像(调用
    PaintOn32bpp
    ),这样您就可以始终简单地每四个字节迭代一次,并对ARGB按3,2,1,0的顺序获取字节值。我在这里忽略了透明度,因为它只是把什么是颜色和什么不是颜色的概念复杂化了

    private void InitContinents(Bitmap map, Int32 nearPixelLimit)
    {
        // Build hues map from colour palette. Since detection is done
        // by hue value, any grey or white values on the image will be ignored.
        // This does mean the process only works with actual colours.
        // In this function it is assumed that index 0 in the palette is the white background.
        Double[] hueMap = new Double[this.continentsPal.Length];
        for (Int32 i = 0; i < this.continentsPal.Length; i++)
        {
            Color col = this.continentsPal[i];
            if (col.GetSaturation() < .25)
                hueMap[i] = -2;
            else
                hueMap[i] = col.GetHue();
        }
        Int32 w = map.Width;
        Int32 h = map.Height;
        Bitmap newMap = ImageUtils.PaintOn32bpp(map, continentsPal[0]);
        // BUILD REDUCED COLOR MAP
        Byte[] guideMap = new Byte[w * h];
        Int32 stride;
        Byte[] imageData = ImageUtils.GetImageData(newMap, out stride);
        for (Int32 y = 0; y < h; y++)
        {
            Int32 sourceOffs = y * stride;
            Int32 targetOffs = y * w;
            for (Int32 x = 0; x < w; x++)
            {
                Color c = Color.FromArgb(255, imageData[sourceOffs + 2], imageData[sourceOffs + 1], imageData[sourceOffs + 0]);
                Double hue;
                // Detecting on hue. Values with < 25% saturation are ignored.
                if (c.GetSaturation() < .25)
                    hue = -2;
                else
                    hue = c.GetHue();
                // Get the closest match
                Double smallestHueDiff = Int32.MaxValue;
                Int32 smallestHueIndex = -1;
                for (Int32 i = 0; i < hueMap.Length; i++)
                {
                    Double hueDiff = Math.Abs(hueMap[i] - hue);
                    if (hueDiff < smallestHueDiff)
                    {
                        smallestHueDiff = hueDiff;
                        smallestHueIndex = i;
                    }
                }
                guideMap[targetOffs] = (Byte)(smallestHueIndex < 0 ? 0 : smallestHueIndex);
                // Increase read pointer with 4 bytes for next pixel
                sourceOffs += 4;
                // Increase write pointer with 1 byte for next index
                targetOffs++;
            }
        }
        // Remove random edge pixels, and save in global var.
        this.continentGuide = RefineMap(guideMap, w, h, nearPixelLimit);
        // Build image from the guide map.
        this.overlay = ImageUtils.BuildImage(this.continentGuide, w, h, w, PixelFormat.Format8bppIndexed, this.continentsPal, null);
    }
    
    private void inits(位图映射,Int32 nearPixelLimit)
    {
    //从调色板构建色调图。因为检测已经完成
    //根据色调值,图像上的任何灰色或白色值都将被忽略。
    //这意味着该过程仅适用于实际颜色。
    //在此函数中,假定调色板中的索引0为白色背景。
    Double[]hueMap=新的Double[this.contractionspal.Length];
    for(Int32 i=0;iprivate void picImage_MouseMove(object sender, MouseEventArgs e)
    {
        Int32 x = e.X - picImage.Padding.Top;
        Int32 y = e.Y - picImage.Padding.Left;
        Int32 coord = y * this.picWidth + x;
        if (x < 0 || x > this.picWidth || y < 0 || y > this.picHeight || coord > this.continentGuide.Length)
            return;
        Int32 continent = this.continentGuide[coord];
        if (continent == previousContinent)
            return;
        previousContinent = continent;
        if (continent >= this.continents.Length)
            return;
        this.lblContinent.Text = this.continents[continent];
        this.picImage.Image = GetHighlightPic(continent);
    }