PTa*_*PTa 15 .net multithreading jpeg gdi+ resize
我试图使用.Net并行调整jpegs的大小.我的所有尝试都失败了,因为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
Run Code Online (Sandbox Code Playgroud)
所有线程都在graph.DrawImage()处等待.有没有办法加快使用其他功能的代码性能?是不是可以使用多线程的Graphics.Draw?在实际应用中,应该同时调整多个图像的大小(在四核PC上),并不总是相同.发布的代码仅用于测试目的......
提前致谢
编辑:根据评论更新代码
Fas*_*tAl 17
GDI +阻止每个进程的很多方法.是的,痛苦,但没有办法绕过它.幸运的是,像这样的任务(以及处理文件系统上的文件的任何任务),在多个进程之间分配工作负载太容易了.幸运的是,看起来GDI +使用的是锁,而不是互斥锁,所以它是并发的!
我们有一些图形程序,我在那里做图像处理.一位程序员在转换程序中同时启动6-7份副本.所以它并不凌乱,相信我.哈克?你看起来很漂亮没有得到报酬.把工作做完!
Imports System.Drawing
Module Module1
Dim CPUs As Integer = Environment.ProcessorCount
Dim pRuns As New System.Collections.Generic.List(Of Process)
Sub Main()
Dim ts As Date = Now
Try
If Environment.GetCommandLineArgs.Length > 1 Then
LongTerm(Environment.GetCommandLineArgs(1))
Exit Sub
End If
Dim i As Integer = 0
Dim files As String() = IO.Directory.GetFiles("D:\TEMP", "*.jpg")
Dim MAX As Integer = Math.Min(26, files.Count)
While pRuns.Count > 0 Or i < MAX
System.Threading.Thread.Sleep(100)
If pRuns.Count < CInt(CPUs * 1.5) And i < MAX Then ''// x2 = assume I/O has low CPU load
Console.WriteLine("Starting process pRuns.count = " & pRuns.Count & " for " & files(i) & " path " & _
Environment.GetCommandLineArgs(0))
Dim p As Process = Process.Start(Environment.GetCommandLineArgs(0), """" & files(i) & """")
pRuns.Add(p)
i += 1
End If
Dim i2 As Integer
i2 = 0
While i2 < pRuns.Count
If pRuns(i2).HasExited Then
pRuns.RemoveAt(i2)
End If
i2 += 1
End While
End While
Catch ex As Exception
Console.WriteLine("Blew up." & ex.ToString)
End Try
Console.WriteLine("Done, press enter. " & Now.Subtract(ts).TotalMilliseconds)
Console.ReadLine()
End Sub
Sub LongTerm(ByVal file As String)
Try
Dim newImageHeight As Integer
Dim oldImage As Image
Console.WriteLine("Reading " & CStr(file))
oldImage = Image.FromFile(CStr(file))
Dim rect As Rectangle
newImageHeight = Convert.ToInt32(850 * oldImage.Height / oldImage.Width)
Using newImage As New Bitmap(850, newImageHeight, oldImage.PixelFormat)
Using graph As Graphics = 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
Console.WriteLine("Converting " & CStr(file))
graph.DrawImage(oldImage, rect)
Console.WriteLine("Saving " & CStr(file))
newImage.Save("d:\temp\Resized\" & _
IO.Path.GetFileNameWithoutExtension(CStr(file)) & ".JPG", _
System.Drawing.Imaging.ImageFormat.Jpeg)
End Using
End Using
Catch ex As Exception
Console.WriteLine("Blew up on " & CStr(file) & vbCrLf & ex.ToString)
Console.WriteLine("Press enter")
Console.ReadLine()
End Try
End Sub
End Module
Run Code Online (Sandbox Code Playgroud)
我不确定为什么执行Graphics.DrawImage似乎对您进行了序列化,但我实际上注意到了您将工作项排队的一般模式的竞争条件。比赛是在WaitOne和之间Set。第一个工作项可能Set在任何其他工作项还没有排队之前就完成了。这将导致WaitOne在所有工作项目完成之前立即返回。
解决方案是将主线程视为工作项。pRuns在排队开始前递增一次,然后在排队完成后递减并用信号通知等待句柄,就像在正常工作项中一样。但是,更好的方法是使用CountdownEvent该类(如果您可以使用该类),因为它可以简化代码。幸运的是,我最近刚刚在另一个问题中发布了该模式。
如果您不介意 WPF 方法,可以尝试以下方法。下面是一个简单的重新缩放方法,它接受图像流并生成包含生成的 JPEG 数据的 byte[]。由于您不想实际使用 GDI+ 绘制图像,因此我认为尽管它是基于 WPF 的,但它仍然适合您。(唯一的要求是在您的项目中引用WindowsBase 和PresentationCore。)
优点包括更快的编码(在我的机器上提高了 200-300%)和更好的并行加速,尽管我也在 WPF 渲染路径中看到了一些不需要的序列化。让我知道这对您有何作用。我确信如果有必要它可以进一步优化。
代码:
byte[] ResizeImage(Stream source)
{
BitmapFrame frame = BitmapFrame.Create(source, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.None);
var newWidth = frame.PixelWidth >> 1;
var newHeight = frame.PixelHeight >> 1;
var rect = new Rect(new System.Windows.Size(newWidth, newHeight));
var drawingVisual = new DrawingVisual();
using (var drawingContext = drawingVisual.RenderOpen())
drawingContext.DrawImage(frame, rect);
var resizedImage = new RenderTargetBitmap(newWidth, newHeight, 96.0, 96.0, PixelFormats.Default);
resizedImage.Render(drawingVisual);
frame = BitmapFrame.Create(resizedImage);
using (var ms = new MemoryStream())
{
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(frame);
encoder.Save(ms);
return ms.ToArray();
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6767 次 |
| 最近记录: |