并行化 GDI+图像调整大小.net
我尝试使用 .Net 并行调整 jpeg 的大小。我所有的尝试都失败了,因为 Graphics.DrawImage-func 似乎在活动时锁定。尝试以下截图:
Sub Main()
Dim files As String() = IO.Directory.GetFiles("D:\TEMP")
Dim imgs(25) As Image
For i As Integer = 0 To 25
imgs(i) = Image.FromFile(files(i))
Next
Console.WriteLine("Ready to proceed ")
Console.ReadLine()
pRuns = 1
For i As Integer = 0 To 25
Threading.Interlocked.Increment(pRuns)
Threading.ThreadPool.QueueUserWorkItem(New Threading.WaitCallback(AddressOf LongTerm), imgs(i))
Next
Threading.Interlocked.Decrement(pRuns)
pSema.WaitOne()
Console.WriteLine("Fin")
Console.ReadLine()
End Sub
Sub LongTerm(ByVal state As Object)
Dim newImageHeight As Integer
Dim oldImage As Image = CType(state, Image)
Dim newImage As Image
Dim graph As Graphics
Dim rect As Rectangle
Dim stream As New IO.MemoryStream
Try
newImageHeight = Convert.ToInt32(850 * oldImage.Height / oldImage.Width)
newImage = New Bitmap(850, newImageHeight, oldImage.PixelFormat)
graph = Graphics.FromImage(newImage)
rect = New Rectangle(0, 0, 850, newImageHeight)
With graph
.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
End With
'Save image to memory stream
graph.DrawImage(oldImage, rect)
newImage.Save(stream, Imaging.ImageFormat.Jpeg)
Catch ex As Exception
Finally
If graph IsNot Nothing Then
graph.Dispose()
End If
If newImage IsNot Nothing Then
newImage.Dispose()
End If
oldImage.Dispose()
stream.Dispose()
Console.WriteLine("JobDone {0} {1}", pRuns, Threading.Thread.CurrentThread.ManagedThreadId)
Threading.Interlocked.Decrement(pRuns)
If pRuns = 0 Then
pSema.Set()
End If
End Try
End Sub
所有线程都在 graph.DrawImage() 处等待。有没有办法使用其他函数来加快代码性能?是否无法在多线程中使用 Graphics.Draw ?在实际应用中,应该同时调整多个图像的大小(在四核电脑上),而不是始终相同。发布的代码仅用于测试目的...
提前致谢
编辑:根据评论更新代码
I've tried to parallelize the resizing of jpegs using .Net. All my tries failed, because the Graphics.DrawImage-func seems to lock while active. Try the following snipped:
Sub Main()
Dim files As String() = IO.Directory.GetFiles("D:\TEMP")
Dim imgs(25) As Image
For i As Integer = 0 To 25
imgs(i) = Image.FromFile(files(i))
Next
Console.WriteLine("Ready to proceed ")
Console.ReadLine()
pRuns = 1
For i As Integer = 0 To 25
Threading.Interlocked.Increment(pRuns)
Threading.ThreadPool.QueueUserWorkItem(New Threading.WaitCallback(AddressOf LongTerm), imgs(i))
Next
Threading.Interlocked.Decrement(pRuns)
pSema.WaitOne()
Console.WriteLine("Fin")
Console.ReadLine()
End Sub
Sub LongTerm(ByVal state As Object)
Dim newImageHeight As Integer
Dim oldImage As Image = CType(state, Image)
Dim newImage As Image
Dim graph As Graphics
Dim rect As Rectangle
Dim stream As New IO.MemoryStream
Try
newImageHeight = Convert.ToInt32(850 * oldImage.Height / oldImage.Width)
newImage = New Bitmap(850, newImageHeight, oldImage.PixelFormat)
graph = Graphics.FromImage(newImage)
rect = New Rectangle(0, 0, 850, newImageHeight)
With graph
.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
End With
'Save image to memory stream
graph.DrawImage(oldImage, rect)
newImage.Save(stream, Imaging.ImageFormat.Jpeg)
Catch ex As Exception
Finally
If graph IsNot Nothing Then
graph.Dispose()
End If
If newImage IsNot Nothing Then
newImage.Dispose()
End If
oldImage.Dispose()
stream.Dispose()
Console.WriteLine("JobDone {0} {1}", pRuns, Threading.Thread.CurrentThread.ManagedThreadId)
Threading.Interlocked.Decrement(pRuns)
If pRuns = 0 Then
pSema.Set()
End If
End Try
End Sub
All threads wait at graph.DrawImage(). Is there a way to speed up code performance using other functions? Is it impossible to use Graphics.Draw with multiple threads? In the real application multiple images should be resized at the same time (on a quad-core pc), not always the same. The posted code is only for testing purposes...
Thanks in advance
Edit: Updated the code according to comments
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
使用流程。
GDI+ 以多种方式阻止每个进程。是的,很痛苦,但没有办法解决。幸运的是,对于像这样的任务(以及处理文件系统上的文件的任何任务),将工作负载分配到多个进程之间太容易了。幸运的是,看起来 GDI+ 使用的是锁,而不是互斥锁,所以它是并发的!
我们有一些图形程序,我可以在其中进行图像处理。一名程序员在生产过程中一次启动 6-7 个转换程序副本。所以它并不混乱,相信我。黑客?你不会因为看起来漂亮而得到报酬。完成工作!
便宜的示例(注意这在 ide 中不起作用,构建它并运行 EXE):
Use Processes.
GDI+ blocks per process a lot of ways. Yep, a pain, but there's no way around it. Fortunately with tasks like this one (and any one that processes files on the filesystem), it's too easy to just split the workload up between multiple processes. Fortunately it looks like GDI+ is using locks, not mutex, so it is concurrent!
We have some graphics programs where I work to do image processing. One programmer starts 6-7 copies at once of a conversion program, in production. So it is not messy, trust me. Hack? You're not getting paid to look pretty. Get the job done!
Cheap Example (note this will NOT work in the ide, build it and run the EXE):
我不确定为什么 Graphics.DrawImage 的执行似乎会为您序列化,但我实际上注意到您对工作项进行排队的一般模式存在竞争条件。竞争发生在
WaitOne
和Set
之间。第一个工作项有可能在其他任何工作项尚未排队之前就已Set
。这将导致WaitOne
在所有工作项完成之前立即返回。解决方案是将主线程视为一个工作项。在排队开始之前递增
pRuns
一次,然后在排队完成后递减并向等待句柄发出信号,就像在正常工作项中一样。但是,更好的方法是使用CountdownEvent
类(如果您可以使用该类),因为它可以简化代码。幸运的是我最近在另一个问题中发布了该模式。I am not sure why execution of
Graphics.DrawImage
seems to serialize for you, but I actually noticed a race condition with your general pattern of queuing the work items. The race is between theWaitOne
and theSet
. It is possible for the first work item toSet
before any of the others have even been queued yet. That will causeWaitOne
to return immediately before all work items have completed.The solution is to treat the main thread as if it were a work item. Increment
pRuns
once before queueing begins and then decrement and signal the wait handle after queueing is complete just as you would in a normal work item. However, the better approach is to use theCountdownEvent
class if that is available to you as it simplifies the code. As luck would have it I just recently posted the pattern in another question.如果您不介意 WPF 方法,可以尝试以下方法。下面是一个简单的重新缩放方法,它接受图像流并生成包含生成的 JPEG 数据的 byte[]。由于您不想实际使用 GDI+ 绘制图像,因此我认为尽管它是基于 WPF 的,但它仍然适合您。 (唯一的要求是在项目中引用 WindowsBase 和PresentationCore。)
优点包括更快的编码(在我的计算机上提高了 200-300%)和更好的并行加速,尽管我还在 WPF 渲染路径中看到了一些不需要的序列化。让我知道这对您有何作用。我确信如果有必要它可以进一步优化。
代码:
If you don't mind a WPF approach, here is something to try. The following is a simple rescale method that accepts image streams and produces a byte[] containing the resulting JPEG data. Since you do not want to actually draw the images with GDI+, I thought this was suitable for you despite being WPF-based. (The only requirement is to reference WindowsBase and PresentationCore in your project.)
Advantages include faster encoding (by 200-300% on my machine) and better parallel speedup, although I also see some unwanted serialization in the WPF rendering path. Let me know how this works for you. I'm sure it could be optimized further if necessary.
The code:
使用 GDI+ 以外的图像处理库。
我们在一个容量相当大的网站上使用 ImageMagick,它会调整上传图像的大小(上传图像通常为 10-40 MPixels,但为了能够在网站中(在 Flash 模块中)使用它们,我们将它们调整为最小尺寸 1500像素)。处理速度非常快并且结果非常好。
我们当前使用命令行界面启动一个新的 ImageMagick 进程。这在启动新进程时会产生一些开销,但由于图像太大,因此通常只占整个调整大小过程的一小部分时间。也可以在进程内使用 ImageMagick,但尚未尝试过,因为它 1. 我们不需要它提供的额外性能,2. 在其他进程中运行第三方软件感觉很好。
使用 ImageMagick 时,您还可以获得许多其他可能性,例如更好的过滤和许多其他功能。
Use a image-processing library other than GDI+.
We use ImageMagick at a pretty high-volume web site it resize uploaded images (the uploaded images are usually 10-40 MPixels but to be able to work with them in the website (in Flash modules), we resize them to have smallest size 1500 pixels). Processing is pretty fast and gives excellent results.
We currently start a new ImageMagick process using the command-line interface. This gives some overhead when starting new processes, but since the images are so large it is usually a pretty small time slice of the total resizing process. It is also possible to use ImageMagick in-process but have haven't tried that yet since it 1. we do not need the extra performance it gives and 2. it feels good to run third-party software in other processes.
When using ImageMagick, you also get a number of other possibilities like better filtering and lots of other functions.