C# 获取图像区域(如地图上的国家)的二维坐标轮廓
我将如何为一幅图像的某个区域生成二维坐标,例如,如果这张地图上的一个国家被挑出来,并且是唯一可见的国家:但是在同样大小的画布上,我将如何获取它的二维坐标 由于我想使用c#基于这些坐标创建悬停/单击区域,因此我找不到一种工具可以检测空白画布中的形状,并吐出其轮廓坐标C# 获取图像区域(如地图上的国家)的二维坐标轮廓,c#,asp.net,.net,image-processing,coordinates,C#,Asp.net,.net,Image Processing,Coordinates,我将如何为一幅图像的某个区域生成二维坐标,例如,如果这张地图上的一个国家被挑出来,并且是唯一可见的国家:但是在同样大小的画布上,我将如何获取它的二维坐标 由于我想使用c#基于这些坐标创建悬停/单击区域,因此我找不到一种工具可以检测空白画布中的形状,并吐出其轮廓坐标 我主要认为这是我的一个措辞/术语问题,因为我觉得整个过程已经是一件“事情”,并且有很好的记录 您可以使用opencv的findcontour()函数。请参阅此处的文档:。实现任务的方法有很多,这里有几个: 看看哪一个几乎和你的一样,但
我主要认为这是我的一个措辞/术语问题,因为我觉得整个过程已经是一件“事情”,并且有很好的记录 您可以使用opencv的findcontour()函数。请参阅此处的文档:。实现任务的方法有很多,这里有几个: 看看哪一个几乎和你的一样,但起点有点不同 简而言之:
如果您的图像经过过滤(消除混叠、缩放等),则需要进行颜色比较,并留有一定的误差,甚至可能与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);
}