- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
在我的代码中,我使用指向非托管对象的指针从相机中检索帧,对其进行一些计算,然后在图片框控件上将其可视化。
在我进一步了解这个应用程序的所有细节之前,我想确保这个过程的基本代码是好的。我特别想:
- 保持执行时间最短并避免不必要的操作,例如 复制不必要的图像。我只想保留必要的 操作
- 了解每帧计算过程中的延迟是否会对图像的显示方式产生不利影响(即如果它没有按照我的预期打印)或跳过某些图像
- 防止更严重的错误,例如由于内存或线程管理或图像显示引起的错误。
为此,我设置了几行实验代码(如下),但我无法解释我发现的结果。如果你有 OpenCv 的可执行文件,你可以自己尝试一下。
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
public partial class FormX : Form
{
private delegate void setImageCallback();
Bitmap _bmp;
Bitmap _bmp_draw;
bool _exit;
double _x;
IntPtr _ImgBuffer;
bool buffercopy;
bool copyBitmap;
bool refresh;
public FormX()
{
InitializeComponent();
_x = 10.1;
// set experimemental parameters
buffercopy = false;
copyBitmap = false;
refresh = true;
}
private void buttonStart_Click(object sender, EventArgs e)
{
Thread camThread = new Thread(new ThreadStart(Cycle));
camThread.Start();
}
private void buttonStop_Click(object sender, EventArgs e)
{
_exit = true;
}
private void Cycle()
{
_ImgBuffer = IntPtr.Zero;
_exit = false;
IntPtr vcap = cvCreateCameraCapture(0);
while (!_exit)
{
IntPtr frame = cvQueryFrame(vcap);
if (buffercopy)
{
UnmanageCopy(frame);
_bmp = SharedBitmap(_ImgBuffer);
}
else
{ _bmp = SharedBitmap(frame); }
// make calculations
int N = 1000000; /*1000000*/
for (int i = 0; i < N; i++)
_x = Math.Sin(0.999999 * _x);
ShowFrame();
}
cvReleaseImage(ref _ImgBuffer);
cvReleaseCapture(ref vcap);
}
private void ShowFrame()
{
if (pbCam.InvokeRequired)
{
this.Invoke(new setImageCallback(ShowFrame));
}
else
{
Pen RectangleDtPen = new Pen(Color.Azure, 3);
if (copyBitmap)
{
if (_bmp_draw != null) _bmp_draw.Dispose();
//_bmp_draw = new Bitmap(_bmp); // deep copy
_bmp_draw = _bmp.Clone(new Rectangle(0, 0, _bmp.Width, _bmp.Height), _bmp.PixelFormat);
}
else
{
_bmp_draw = _bmp; // add reference to the same object
}
Graphics g = Graphics.FromImage(_bmp_draw);
String drawString = _x.ToString();
Font drawFont = new Font("Arial", 56);
SolidBrush drawBrush = new SolidBrush(Color.Red);
PointF drawPoint = new PointF(10.0F, 10.0F);
g.DrawString(drawString, drawFont, drawBrush, drawPoint);
drawPoint = new PointF(10.0F, 300.0F);
g.DrawString(drawString, drawFont, drawBrush, drawPoint);
g.DrawRectangle(RectangleDtPen, 12, 12, 200, 400);
g.Dispose();
pbCam.Image = _bmp_draw;
if (refresh) pbCam.Refresh();
}
}
public void UnmanageCopy(IntPtr f)
{
if (_ImgBuffer == IntPtr.Zero)
_ImgBuffer = cvCloneImage(f);
else
cvCopy(f, _ImgBuffer, IntPtr.Zero);
}
// only works with 3 channel images from camera! (to keep code minimal)
public Bitmap SharedBitmap(IntPtr ipl)
{
// gets unmanaged data from pointer to IplImage:
IntPtr scan0;
int step;
Size size;
OpenCvCall.cvGetRawData(ipl, out scan0, out step, out size);
return new Bitmap(size.Width, size.Height, step, PixelFormat.Format24bppRgb, scan0);
}
// based on older version of OpenCv. Change dll name if different
[DllImport( "opencv_highgui246", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr cvCreateCameraCapture(int index);
[DllImport("opencv_highgui246", CallingConvention = CallingConvention.Cdecl)]
public static extern void cvReleaseCapture(ref IntPtr capture);
[DllImport("opencv_highgui246", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr cvQueryFrame(IntPtr capture);
[DllImport("opencv_core246", CallingConvention = CallingConvention.Cdecl)]
public static extern void cvGetRawData(IntPtr arr, out IntPtr data, out int step, out Size roiSize);
[DllImport("opencv_core246", CallingConvention = CallingConvention.Cdecl)]
public static extern void cvCopy(IntPtr src, IntPtr dst, IntPtr mask);
[DllImport("opencv_core246", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr cvCloneImage(IntPtr src);
[DllImport("opencv_core246", CallingConvention = CallingConvention.Cdecl)]
public static extern void cvReleaseImage(ref IntPtr image);
}
结果 [双核 2 Duo T6600 2.2 GHz]:
一个。缓冲区复制=假;复制位图=假;刷新=假;
这是更简单的配置。依次检索每一帧,进行运算(实际是基于同一帧,这里只是计算),然后将计算结果打印在图像上,最后显示在图片框上。< br/>OpenCv 文档说:
OpenCV 1.x functions cvRetrieveFrame and cv.RetrieveFrame return image stored inside the video capturing structure. It is not allowed to modify or release the image! You can copy the frame using cvCloneImage() and then do whatever you want with the copy.
但这并不妨碍我们做实验。
如果计算强度不高(迭代次数少,N),一切都很好,我们操纵非托管帧检索器拥有的图像缓冲区这一事实在这里不会造成问题。
原因可能是他们建议不要触及缓冲区,以防人们修改它的结构(而不是它的值)或在没有意识到的情况下异步执行操作。现在我们依次检索帧并修改它们的内容。
如果增加 N(N=1000000 或更多),当每秒帧数不高时,例如在人造光和低曝光下,一切似乎都正常,但过一会儿视频就会滞后并且图形会在上面留下深刻印象正在闪烁。对于更高的帧速率,即使视频仍然流畅,闪烁也会从一开始就出现。
这是因为在控件上显示图像(或刷新或其他)的机制在某种程度上是异步的,当图片框正在获取其数据缓冲区时,它同时被相机修改,删除图形?
还是有其他原因?
为什么图像会以这种方式滞后,即我预计由于计算而导致的延迟只会在计算尚未完成时跳过相机接收到的帧,并且实际上只会降低帧速率;或者,所有帧都已接收,并且由于计算导致的延迟使系统处理几分钟前获得的图像,因为要处理的图像队列会随着时间的推移而增加。
相反,观察到的行为似乎是两者的混合体:有几秒钟的延迟,但随着捕获过程的进行,延迟似乎并没有增加多少。
B。缓冲区复制=真;复制位图=假;刷新=假;
在这里,我按照 OpenCv 文档的建议将缓冲区的深度复制到第二个缓冲区中。
没有什么改变。第二个缓冲区在运行期间不会更改其在内存中的地址。
C。缓冲区复制=假;复制位图=真;刷新=假;
现在,每次在内存中分配一个新空间时,位图的(深)副本就会被分配。
闪烁效果消失了,但在一定时间后仍会出现滞后现象。
D。缓冲区复制=假;复制位图=假;刷新=真;
像以前一样。
请帮我解释一下这些结果!
最佳答案
坦率地说,了解您问题的所有细节有点乏味,但让我提出几点来帮助您分析结果。
在情况 A 中,您说您直接在缓冲区上执行计算。文档说你不应该这样做,所以如果你这样做,你可能会得到未定义的结果。 OpenCV 假设你不会碰它,所以它可能会做一些事情,比如突然删除那部分内存,让其他应用程序处理它,等等。它可能看起来有效,但你永远无法确定,所以不要这样做 *拍打你的手腕* 特别是,如果你的处理需要很长时间,相机可能会在你处理过程中覆盖缓冲区。
你应该做的是在做任何事情之前复制缓冲区。这会给你一段内存,你可以随心所欲地做任何事。您可以创建一个引用此内存的位图,并在不再需要时手动释放内存。
如果您的处理速率(每秒处理的帧数)小于相机每秒捕获的帧数,则您必须预料到会丢失一些帧。如果您想显示处理过的图像的实时 View ,它会滞后并且没有简单的解决方法。如果您的应用程序处理流畅的视频至关重要(例如,如果您正在跟踪一个对象,这可能是必要的),那么请考虑将视频存储到磁盘,这样您就不必实时处理。您也可以考虑使用多线程同时处理多个帧,但实时取景会有延迟。
顺便问一下,您没有使用 EmguCV 有什么特别的原因吗?它具有相机抽象和一个系统,该系统在相机捕捉到新帧时引发事件。这样,您就不需要在后台线程上连续调用 cvQueryFrame
。
关于c# - 在 Picturebox 上显示从相机检索到的位图的实验,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29881151/
我正在测试 SQL,但我陷入了一个查询。这是一个无用的查询,但我想理解它。 select count(*), floor(rand()*2) as x from table_name group by
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
在我编写代码时,我经常喜欢查看代码库中特定区域的工作原理或某些表达式产生的结果。 大多数其他语言都提供了在运行时评估新的自定义表达式的工具。 Golang 似乎还没有提供这个功能,或者至少没有我使用过
1、kvm 简介 kernel-based virtual machine的简称,是一个开源的系统虚拟化模块,自linux 2.6.20之后集成在linux的各个主要发行版本中。它使用linux
我是中继新手,我正在尝试在并发模式下使用中继现代实验。我已经能够使用 Suspense 和 ErrorBoundary 很好地加载节点、边等。我现在正在处理用于创建和更新对象的表单。 我无法弄清楚如何
我正在参加在线软件安全类(class)。我正在尝试使用 shellcode 进行实验。我编写了一个易受攻击的服务器、一个注入(inject)程序、一个(可能已损坏的)shellcode,我将其转换为程
我最糟糕的噩梦是日期对象,所以我创建了一个 fiddle 来查看它是如何工作的,并尝试为以下格式的 date strings 找到解决方案 2015-10- 05T11:49:13.587Z 但要注意
我一直在研究 Accessibility Object Model API,我认为尝试在 Puppeteer 测试中使用它会很酷。 getComputedAccessibleNode 返回一个 pro
我目前正在概述 C++11 的新特性,由于目前不明原因,其中一些特性无法编译。我使用 gcc version 4.6.0 20100703 (experimental) (GCC) 所以根据 GNU
1.动态年龄判定规则 对象进入老年代的4个常见的时机: 1、 躲过15次gc,达到15岁高龄之后进入老年代; 2、 动态年龄判定规则,如果Survivor区域内年龄1+年龄2+年龄3+年龄n的对象
有没有办法停止ray.tune实验(例如使用 PBT)当明显过度拟合或一个指标长时间没有改善时? 最佳答案 现在,这在 Tune 中得到了很好的支持,https://github.com/ray-pr
我尝试在gcc 6.0的开发中实际使用新的c++ 1z功能。 如果我尝试这个小例子: #include #include namespace fs = std::experimental::fil
我想知道为什么我在服务器端运行实验时必须包含 JavaScript cxApi。此外,我可以通过 PHP 发送选定的实验和变体吗?或者可能通过在没有外部资源(如 cxApi)的情况下注入(inject
我正在开发一个使用 Firebase iOS SDK 的 iOS 应用,我正在尝试弄清楚如何访问用户已看到的所有 Firebase AB 实验以及用户参与的实验变体。 我查看了文档以及 Firebas
我用 python 2.7.7 运行了以下命令: import gc import memory_profiler print memory_profiler.memory_usage()[0] x
我在浏览器控制台中做了一个 JavaScript 实验 - 首先我创建了一个新对象 foo 如下 var foo = { bar: function() { return this.baz
据说“4916 个正面训练示例经过手工挑选对齐、归一化并缩放到 24x24 的基本分辨率。通过从 9500 张不包含人脸的图像中随机挑选子窗口来选择 10,000 个负面示例。”在论文“Paul Vi
这个问题在这里已经有了答案: 关闭 12 年前。 Possible Duplicate: Can you write object oriented code in C? 嗨! 只是为了好玩,这两天
1. 程序的JVM参数示范 已知,平时系统运行创建的对象,除非是那种大对象,否则通常来说都是优先分配在新生代中的Eden区域的。 而且新生代还有另外两块Survivor区域,默认Eden区域占据新
在 上创建新实验DAGsHub 使用Git,".._metrics.csv"的格式应该是什么和 ".._params.yml"文件? 不幸的是,我在任何地方都找不到引用。 最佳答案 特尔;博士: 对于
我是一名优秀的程序员,十分优秀!