【发布时间】:2012-01-17 14:33:13
【问题描述】:
我正在开发一个名为虚拟衣橱的应用程序,对于该应用程序,我需要知道用户的大小。我的问题是如何从边缘检测到的图像中获取用户的大小。我正在使用 cvcanny 进行边缘检测。 我对emgu了解不多。 有什么建议 。 提前谢谢你
【问题讨论】:
标签: emgucv
我正在开发一个名为虚拟衣橱的应用程序,对于该应用程序,我需要知道用户的大小。我的问题是如何从边缘检测到的图像中获取用户的大小。我正在使用 cvcanny 进行边缘检测。 我对emgu了解不多。 有什么建议 。 提前谢谢你
【问题讨论】:
标签: emgucv
这里有两种思路,但首先有一些事情必须解决。除非您使用 3D 成像,否则您无法获得准确的尺寸,即使那样它也取决于分辨率,您可能希望查看使用 Xbox Kinect 获取 3D 数据。
如果您使用的是一台相机,则需要先了解规格和镜头类型,然后才能计算真实世界的尺寸。这样您就可以计算每毫米像素值。有关 Homography 和 FOV 计算的详细说明,请参阅我的回答 HERE。
如果您知道这一点,您可以画出身体的轮廓,并要求该人站立以适应它,然后再根据预期值计算尺寸。
这当然只适用于您想拍摄用户的图像并将其与真实世界的尺寸相匹配,因此您想告诉他们他们需要 10.12、14 SML XL 等尺寸...
但是把衣服画在身体上要容易得多。我参考了 EMGU Directory\EMGU.CV.Example\ 文件夹中的示例。
首先您需要检测身体我不建议为此使用边缘检测,虽然可以这样做,但您必须循环遍历每个轮廓并找到与身体匹配的轮廓,然后如果可以匹配,则绘制图像的服装项目。看一下“ShapeDetection”示例,因为它使用了正方形和三角形的计数器匹配。
更快速的方法是 Haar 分类器方法,虽然无法将其调整为可靠,但实现起来要简单得多,并且将您的范围视为可接受的解决方案。查看代码的“FaceDetection”示例。是的,这个只检测面部,但还有其他 haar 分类器可以检测身体,这些分类器位于 EMGU Directory\opencv\data\haarcascades 中。您正在寻找:
使用网络摄像头运行这些程序并对其进行测试以了解它们的工作方式。 google 上有很多示例,但我建议您自己将“CamerCapture”示例和“FaceDetection”示例拼凑在一起,以便更好地理解。如果您遇到困难,请在此处或 EMGU 论坛提问,我会为您提供帮助。
使用 Haar 分类器将为我们提供 ROI(感兴趣区域),我们知道该人的全身/顶部/底部正在使用此信息,我们可以缩小或放大服装项目的图像,并将其放置在 ROI 的相关位置。
至于叠加这两个图像有两个真正的选择。第一种是将图像显示在新的图片框中,图像的背景与图片框的背景一样透明。这只需要您调整控件的位置和内容,但可能会更复杂。它可以更好地执行,因为它不会花费很长时间来显示图像。特别是如果您使用高清图像。
第二个更简单的选项更简单,它假设衣服的背景是黑色或白色。循环遍历要放置衣服的 ROI 的每个像素,衣服的相关值不是黑色或白色,然后将捕获的图像中的像素替换为衣服项目的值。如果您想了解有关访问图像数据的更多信息,请参阅我的文章 HERE。
还有一个“PedestrianDetection”可用,它使用 Hog 描述符,但是这需要更多的工作,它可以设计为比 Haar 级联方法更准确地检测一个人,但它的执行时间通常更差。如果 Haar 不符合您的要求,它可能是一个替代方案,但我相信它会。
如果您需要更多信息或代码的 sn-ps,我希望这可以帮助您,让我知道,
干杯,
克里斯
这段代码写得很快,还没有经过全面测试,所以如果有奇怪的错误,请告诉我,我会修复它。
好的,所以我们将从相机捕获开始,这相当简单,我们根据我们希望使用的设备创建一个新的 Capture 变量。如果有多个设备,那么我们可以定义何时完成捕获设备的创建,
_capture = new Capture(0);将使用第一个设备
_capture = 新的捕获(1);将使用第二个设备
在我们的例子中,我们假设我们的相机是默认的或唯一的相机设备。
供参考的表格只是一个放置在漂亮而简单的表格上的图片框。
相机和它的捕获...
private Capture _capture;
public Form1()
{
InitializeComponent();
//We will start our capture here we can always do this from another method
_capture = new Capture();
Application.Idle += ProcessFrame;
}
Application.Idle += ProcessFrame;每当我们获取帧并且我们的程序没有做任何事情时添加对方法的调用,这是使用计时器的首选方法,因为它允许更高的帧速率。
所以到这里的处理代码,我们还全局声明了我们的 haarcascade,因为我们经常使用它,我们不想每帧都生成它。一个重要的注意事项是,我们制作我们获取的帧的副本,这不是必需的,但是如果垃圾收集器在我们执行大型处理操作时释放帧信息,请确保我们不会遇到任何问题。我们还为我们的框架分配内存作为全局变量,因为我们一直使用它,我们不想继续创建它。
我已经向您展示了对上身和下身的检测,以展示如何应用两个级联。 Haarcascades 也只适用于灰度图像,所以我们创建了一个来使用。
无论如何这将根据检测到的上身位置绘制图像。
//Add the cascade file to you project and set to copy always as it needs to
//be in your output directory
HaarCascade UpperBody = new HaarCascade("haarcascade_mcs_upperbody.xml");
HaarCascade LowerBody = new HaarCascade("haarcascade_lowerbody.xml");
Image<Bgr, Byte> frame = new Image<Bgr, Byte>();
Image<Gray, Byte> Gray_frame = new Image<Gray, Byte>();
private void ProcessFrame(object sender, EventArgs arg)
{
frame = _capture.QueryFrame().Copy();
Gray_frame = frame.Convert<Gray, Byte>();
//Upper Body
MCvAvgComp[][] UpperBodyDetected = Gray_frame.DetectHaarCascade(
UpperBody ,
1.1,
10,
Emgu.CV.CvEnum.HAAR_DETECTION_TYPE.DO_CANNY_PRUNING,
new Size(20, 20));
//Lower Body
MCvAvgComp[][] LowerBodyDetected = Gray_frame.DetectHaarCascade(
LowerBody ,
1.1,
10,
Emgu.CV.CvEnum.HAAR_DETECTION_TYPE.DO_CANNY_PRUNING,
new Size(20, 20));
//draw the results on the image just for effect for now
// but alternatively here we could display the clothing item according to the ROI
foreach (MCvAvgComp Upp_Body in UpperBodyDetected[0])
{
//draw the upper bodt detected in the with blue
frame.Draw(Upp_Body.rect, new Bgr(Color.Blue), 2);
}
foreach (MCvAvgComp Low_Body in LowerBodyDetected[0])
{
//draw the upper bodt detected in the with red
frame.Draw(Low_Body.rect, new Bgr(Color.Red), 2);
}
}
好的,这就是你找到身体所需的代码,现在我们必须在我们的图像上绘制对象,这将需要一个更具调查性的应用程序,但我将给你一个关于如何绘制白色背景图像的快速示例在获得的帧上。如果您希望使用新控件绘制图像,那么恐怕我没有时间完全测试所需的代码。如果您遇到困难,请询问,因为这里了解 c# 的人比图像分析多。
//our image to draw
Image<Bgr, Byte> Jumper_example = new Image<Bgr, Byte>("filename.jpg");
foreach (MCvAvgComp Upp_Body in UpperBodyDetected[0])
{
for(int i = 0; i < Jumper_example.Height; i++)
{
for(int j = 0; j < Jumper_example.Width; j++)
{
//This will only execute if the data isn't white
if(Jumper_example.Data[i,j,0] != 255 && Jumper_example.Data[i,j,1] != 255 && Jumper_example.Data[i,j,2] != 255)
{
//work out our co-ordinates to draw to
x = Upp_Body.rect.X + j;
y = Upp_Body.rect.Y + i;
//.Data is usually backwards if I remember but if it doesn't draw
//correctley swap x an y this is where I may have messed up if so let me
//copy data
frame.Data[y,x,0] = Jumper_example.Data[i,j,0];
frame.Data[y,x,1] = Jumper_example.Data[i,j,1];
frame.Data[y,x,2] = Jumper_example.Data[i,j,2];
}
}
}
}
好了,就这么多吧,希望对你有帮助
干杯,
克里斯
【讨论】: