在 C# 中转换图像
Converting Image in c#
编辑:已解决! 请在下面查看我的回答以了解详细信息。
我找不到原始问题的答案,但我找到了替代解决方案
这个问题可能会在其他地方问到,但我已经搜索了好几天了,找不到任何有用的东西。
问题:我需要一次性把"Stream"转成"image(bgr, byte)",有没有way/command 直接从 System.Drawing.Image.FromStream 转换为 Emgu.CV.Image(Bgr, Byte) 而无需从 stream 转换为 image到 bitmap 到 image(bgr, byte)?
信息: 作为我论文项目的一部分,我在 Visual Studio 2010 年使用 c# 进行编码。
我正在从网络上的 IP 摄像机获取图像流,并应用许多算法来检测 faces/extract 面部特征并识别个人面部。在我的笔记本电脑本地摄像头上,我可以达到大约 25~(给定或接受)的 FPS,包括算法,因为我不必转换图像。对于 IP 摄像机流,我需要多次转换以达到所需的格式,结果大约为 5-8fps。
(我知道我目前的方法效率极低,这就是我来这里的原因,我实际上将图像总共转换了 5 次(甚至灰度缩放),实际上只使用了我处理器内存的一半(i7, 8GB 内存))。它必须是 image(bgr, byte),因为这是算法可以使用的唯一格式。
我用来获取图像的代码:
//headers
using System.IO
using System.Threading;
using System.Net;
//request a connection
req = (HttpWebRequest)HttpWebRequest.Create(cameraUrl);
//gives chance for timeout for errors to occur or loss of connection
req.AllowWriteStreamBuffering = true;
req.Timeout = 20000;
//retrieve response (if successfull)
res = req.GetResponse();
//image returned
stream = res.GetResponseStream();
我在后台管理连接、数据、安全等方面有很多东西,我已将其简化为上述代码。
我当前将图像转换为所需输出的代码:
//Convert stream to image then to bitmap
Bitmap bmpImage = new Bitmap(System.Drawing.Image.FromStream(stream));
//Convert to emgu image (desired goal)
currentFrame = new Emgu.CV.Image<Bgr, Byte>(bmpImage);
//gray scale for other uses
gray = currentFrame.Convert<Gray, Byte>();
我知道有一种方法可以在本地临时保存图像,但出于安全考虑,我需要避免这种情况。我正在寻找更多的直接转换来帮助节省处理能力。
我忽略了什么吗?感谢所有帮助。
感谢阅读。 (如果有人需要更多细节,我会更新这个)
-戴夫
您可以在内存(缓冲区)中保存多个图像,然后从缓冲区开始处理。
像这样:
//Convert stream to image then to bitmap
Bitmap bmpImage = new Bitmap(System.Drawing.Image.FromStream(stream));
//Convert to emgu image (desired goal)
currentFrame = new Emgu.CV.Image<Bgr, Byte>(bmpImage);
//gray scale for later use
gray = currentFrame.Convert<Gray, Byte>();
SaveToBuffer(gray);
Queue<Emgu.CV.Image<Gray, Byte>> buffer = new Queue<Emgu.CV.Image<Gray, Byte>>();
bool canProcess = false;
// ...
private void SaveToBuffer(Emgu.CV.Image<Gray, Byte> img)
{
buffer.Enqueue(img);
canProcess = buffer.Count > 100;
}
private void Process()
{
if(canProcess)
{
buffer.Dequeue();
// Processing logic goes here...
}
else
{
// Buffer is still loading...
}
}
但请注意,您将需要足够的 RAM 来将图像存储在内存中,并且您还应该调整缓冲区大小以满足您的要求。
您有几个潜在的瓶颈,其中最重要的是您可能将流 jpeg 解码为图像,然后将其转换为位图,然后转换为 openCV 图像。
解决此问题的一种方法是完全绕过 .NET 映像。这将涉及尝试直接使用 libjpeg。在 C# 中有一个 free port of it here,您可以挂钩 IIRC 以在每个扫描线的基础上调用它来填充缓冲区。
缺点是您在托管代码中解码 JPEG 数据,这将 运行 至少比等效的 C 慢 1.5 倍,但坦率地说,我预计网络速度会大大降低这一速度。
OpenCV 应该能够直接读取 jpeg 图像(想猜猜它们在幕后使用什么?调查显示:libjpeg),这意味着您可以缓冲整个流并将其交给 OpenCV 并绕过 .NET完全分层。
我相信我找到了问题的答案。我已经尝试使用 Vano Maisuradze 在内存中处理的想法,它提高了 fps 的微小幅度(未经测试不会立即注意到)。还要感谢 Plinths 的回答,我了解了多线程,我可以随着我的进步对其进行优化,因为我可以拆分算法以并行工作。
我认为是我的原因是网络速度!不是实际的算法延迟。正如 Vano 用秒表指出的那样,算法实际上并没有消耗那么多速度。因此,无论是否使用算法,如果我使用线程进行优化,速度大致相同,因此在前一帧完成处理时收集下一帧。
我在一些物理 Cisco 路由器上做了一些测试,得到了相同的结果,如果时钟速度和带宽较慢,这很明显。所以我需要找到一种更快地通过网络检索帧的方法,非常感谢所有回答的人帮助我更好地理解!
结论:
- 多线程优化
- 在内存中处理而不是不断地转换
- 更好的网络解决方案(更高的带宽和速度)
编辑:用于检索图像并在内存中处理的代码,供任何发现此问题并寻求帮助的人使用
public void getFrames(object sender, EventArgs e)
{//Gets a frame from the IP cam
//Replace "IPADDRESS", "USERNAME", "PASSWORD"
//with respective data for your camera
string sourceURL = "http://IPADDRESS/snapshot.cgi?user=USERNAME&pwd=PASSWORD";
//used to store the image retrieved in memory
byte[] buffer = new byte[640 * 480];
int read, total = 0;
//Send a request to the peripheral via HTTP
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(sourceURL);
WebResponse resp = req.GetResponse();
//Get the image capture after recieving a request
//Note: just a screenshot not a steady stream
Stream stream = resp.GetResponseStream();
while ((read = stream.Read(buffer, total, 1000)) != 0)
{
total += read;
}//While End
//Convert memory (byte) to bitmap and store in a picturebox
pictureBox1.Image = (Bitmap)Bitmap.FromStream(new MemoryStream(buffer, 0, total));
}//getFrames End
private void button1_Click(object sender, EventArgs e)
{//Trigger an event to start running the function when possible
Application.Idle += new EventHandler(getFrames);
}//Button1_Click End
编辑:已解决! 请在下面查看我的回答以了解详细信息。 我找不到原始问题的答案,但我找到了替代解决方案
这个问题可能会在其他地方问到,但我已经搜索了好几天了,找不到任何有用的东西。
问题:我需要一次性把"Stream"转成"image(bgr, byte)",有没有way/command 直接从 System.Drawing.Image.FromStream 转换为 Emgu.CV.Image(Bgr, Byte) 而无需从 stream 转换为 image到 bitmap 到 image(bgr, byte)?
信息: 作为我论文项目的一部分,我在 Visual Studio 2010 年使用 c# 进行编码。 我正在从网络上的 IP 摄像机获取图像流,并应用许多算法来检测 faces/extract 面部特征并识别个人面部。在我的笔记本电脑本地摄像头上,我可以达到大约 25~(给定或接受)的 FPS,包括算法,因为我不必转换图像。对于 IP 摄像机流,我需要多次转换以达到所需的格式,结果大约为 5-8fps。
(我知道我目前的方法效率极低,这就是我来这里的原因,我实际上将图像总共转换了 5 次(甚至灰度缩放),实际上只使用了我处理器内存的一半(i7, 8GB 内存))。它必须是 image(bgr, byte),因为这是算法可以使用的唯一格式。
我用来获取图像的代码:
//headers
using System.IO
using System.Threading;
using System.Net;
//request a connection
req = (HttpWebRequest)HttpWebRequest.Create(cameraUrl);
//gives chance for timeout for errors to occur or loss of connection
req.AllowWriteStreamBuffering = true;
req.Timeout = 20000;
//retrieve response (if successfull)
res = req.GetResponse();
//image returned
stream = res.GetResponseStream();
我在后台管理连接、数据、安全等方面有很多东西,我已将其简化为上述代码。 我当前将图像转换为所需输出的代码:
//Convert stream to image then to bitmap
Bitmap bmpImage = new Bitmap(System.Drawing.Image.FromStream(stream));
//Convert to emgu image (desired goal)
currentFrame = new Emgu.CV.Image<Bgr, Byte>(bmpImage);
//gray scale for other uses
gray = currentFrame.Convert<Gray, Byte>();
我知道有一种方法可以在本地临时保存图像,但出于安全考虑,我需要避免这种情况。我正在寻找更多的直接转换来帮助节省处理能力。 我忽略了什么吗?感谢所有帮助。
感谢阅读。 (如果有人需要更多细节,我会更新这个) -戴夫
您可以在内存(缓冲区)中保存多个图像,然后从缓冲区开始处理。
像这样:
//Convert stream to image then to bitmap
Bitmap bmpImage = new Bitmap(System.Drawing.Image.FromStream(stream));
//Convert to emgu image (desired goal)
currentFrame = new Emgu.CV.Image<Bgr, Byte>(bmpImage);
//gray scale for later use
gray = currentFrame.Convert<Gray, Byte>();
SaveToBuffer(gray);
Queue<Emgu.CV.Image<Gray, Byte>> buffer = new Queue<Emgu.CV.Image<Gray, Byte>>();
bool canProcess = false;
// ...
private void SaveToBuffer(Emgu.CV.Image<Gray, Byte> img)
{
buffer.Enqueue(img);
canProcess = buffer.Count > 100;
}
private void Process()
{
if(canProcess)
{
buffer.Dequeue();
// Processing logic goes here...
}
else
{
// Buffer is still loading...
}
}
但请注意,您将需要足够的 RAM 来将图像存储在内存中,并且您还应该调整缓冲区大小以满足您的要求。
您有几个潜在的瓶颈,其中最重要的是您可能将流 jpeg 解码为图像,然后将其转换为位图,然后转换为 openCV 图像。
解决此问题的一种方法是完全绕过 .NET 映像。这将涉及尝试直接使用 libjpeg。在 C# 中有一个 free port of it here,您可以挂钩 IIRC 以在每个扫描线的基础上调用它来填充缓冲区。
缺点是您在托管代码中解码 JPEG 数据,这将 运行 至少比等效的 C 慢 1.5 倍,但坦率地说,我预计网络速度会大大降低这一速度。
OpenCV 应该能够直接读取 jpeg 图像(想猜猜它们在幕后使用什么?调查显示:libjpeg),这意味着您可以缓冲整个流并将其交给 OpenCV 并绕过 .NET完全分层。
我相信我找到了问题的答案。我已经尝试使用 Vano Maisuradze 在内存中处理的想法,它提高了 fps 的微小幅度(未经测试不会立即注意到)。还要感谢 Plinths 的回答,我了解了多线程,我可以随着我的进步对其进行优化,因为我可以拆分算法以并行工作。
我认为是我的原因是网络速度!不是实际的算法延迟。正如 Vano 用秒表指出的那样,算法实际上并没有消耗那么多速度。因此,无论是否使用算法,如果我使用线程进行优化,速度大致相同,因此在前一帧完成处理时收集下一帧。
我在一些物理 Cisco 路由器上做了一些测试,得到了相同的结果,如果时钟速度和带宽较慢,这很明显。所以我需要找到一种更快地通过网络检索帧的方法,非常感谢所有回答的人帮助我更好地理解!
结论:
- 多线程优化
- 在内存中处理而不是不断地转换
- 更好的网络解决方案(更高的带宽和速度)
编辑:用于检索图像并在内存中处理的代码,供任何发现此问题并寻求帮助的人使用
public void getFrames(object sender, EventArgs e)
{//Gets a frame from the IP cam
//Replace "IPADDRESS", "USERNAME", "PASSWORD"
//with respective data for your camera
string sourceURL = "http://IPADDRESS/snapshot.cgi?user=USERNAME&pwd=PASSWORD";
//used to store the image retrieved in memory
byte[] buffer = new byte[640 * 480];
int read, total = 0;
//Send a request to the peripheral via HTTP
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(sourceURL);
WebResponse resp = req.GetResponse();
//Get the image capture after recieving a request
//Note: just a screenshot not a steady stream
Stream stream = resp.GetResponseStream();
while ((read = stream.Read(buffer, total, 1000)) != 0)
{
total += read;
}//While End
//Convert memory (byte) to bitmap and store in a picturebox
pictureBox1.Image = (Bitmap)Bitmap.FromStream(new MemoryStream(buffer, 0, total));
}//getFrames End
private void button1_Click(object sender, EventArgs e)
{//Trigger an event to start running the function when possible
Application.Idle += new EventHandler(getFrames);
}//Button1_Click End