|
-
Apr 29th, 2012, 01:13 PM
#1
Thread Starter
Addicted Member
Fastest way to generate thumbnails.
Hello,
I'm looking for suggestions on the fastest way to generate thumbnails using VB.net and multiple computers without bogging down the UI.
Right now I have 3 computers and I run an instance of my thumbnail generator code on each. One computer hosts the images. The other two computers access the folder of images via the network.
My code is written to compare the main folder and a thumbnail folder to see if new thumbnails need to be generated. If so, a thread is generated and the thumbnail generation code is ran on the thread. There are 5 threads created in all. So, theoretically, across 3 machines, I'm getting 5 independent threads and I'm thinking that Windows 7 automatically spreads the load across the local cores. In an effort to avoid duplicating work, the comparison is done before the generation of each thumbnail.
This method is faster than using only one computer but drags down the performance on all computers (THE UI also becomes unresponsive). I'm looking for a better way to 1.) Produce the thumbnails as fast as possible 2.) Keep all UI's responsive (boy possibly controlling the priority setting) and 3.) Done within a single instance of the program.
In the #3, I mentioned that I would like to use one instance. Is it possible for networking information be used by the hosting computer that will allow threads to run on the other 2 computers? This way, the 15 threads will be controlled by a single host computer.
Here is a snippet of my code:
Code:
Public Sub CreateThumbsFUNCTION(ByVal IMAGE As String, ByVal MAXDIMENSION As Integer)
'following code resizes picture to fit.
'IMAGE needs the full path
'If thumbnail already exists, it will skip
Dim newimage As System.Drawing.Image
Dim EW As New ExifWorks(IMAGE)
Dim OUTPUTPATH As String() = ExtractPath(IMAGE) ' Note, the '\' is returned
Dim NewOutputPath As String = OUTPUTPATH(0) & "thumbs_MIS\"
If File.Exists(NewOutputPath & OUTPUTPATH(1)) Then
' do nothing
Else
Dim CreateFlagg As Integer = 1
Dim Hout, Wout As Integer
newimage = Rotate_Image_From_EXIF(IMAGE)
Dim bm As New Bitmap(newimage)
If bm.Height < MAXDIMENSION And bm.Width < MAXDIMENSION Then
Hout = bm.Height
Wout = bm.Width
Else
If bm.Height > bm.Width Then
Hout = MAXDIMENSION
Wout = Hout * (bm.Width / bm.Height)
Else
Wout = MAXDIMENSION
Hout = Wout * (bm.Height / bm.Width)
End If
End If
Dim thumb As New Bitmap(Wout, Hout) ' New object for smaller image
Dim g As Graphics = Graphics.FromImage(thumb)
g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
g.DrawImage(bm, New Rectangle(0, 0, Wout, Hout), New Rectangle(0, 0, bm.Width, _
bm.Height), GraphicsUnit.Pixel)
g.Dispose()
'MsgBox(Directory.Exists(NewOutputPath))
If Directory.Exists(NewOutputPath) = False Then
Directory.CreateDirectory(NewOutputPath)
End If
'MsgBox(NewOutputPath & OUTPUTPATH(1))
Try
thumb.Save(NewOutputPath & OUTPUTPATH(1), System.Drawing.Imaging.ImageFormat.Jpeg) 'can use any image format
bm.Dispose()
thumb.Dispose()
newimage.Dispose()
EW.Dispose()
Catch ex As Exception
msgbox(ex.text)
End Try
End If
End Sub
Thanks,
Adrian
Last edited by adrian1906; Apr 29th, 2012 at 05:21 PM.
Reason: Typo.
-
Apr 29th, 2012, 07:29 PM
#2
Re: Fastest way to generate thumbnails.
Are you sure you're doing the threading right ? The UI shouldn't be unresponsive when operations are running on other threads. If you're certain your threading is right then you could try lowering the priority of the threads and see how that changes anything.
-
Apr 29th, 2012, 09:18 PM
#3
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
Thank you for your reply.
I have the same question as you. Why is my UI so unresponsive? When I look at my CPU usage on my dual core machine, I am close to 100% on both cores.
I set up 5 backgroundworker objects. When new images enter a watched folder (and subfolders), one of the 5 threads is queried to see if it is busy. If not, then the RunWorkerAsync method is called. Arguments are passed to my thumbnail creater through the DoWorkEventArgs variable).
I set the priority using the following code:
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.BelowNormal;
When I look at the resource meter, the I/O Priority says Normal and the Average CPU oscillates between 30 and 40 and my UI is still sluggish.
Adrian
-
Apr 29th, 2012, 10:02 PM
#4
Re: Fastest way to generate thumbnails.
Would you mind uploading the project so I can take a look at it ?
-
Apr 30th, 2012, 11:16 PM
#5
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
Here is the main code:
Code:
Public Function CreateMissingThumbs(ByVal IMAGEFOLDER As String) As String()
'The purpose of this subroutine is to create thumbnails for a given folder based on missing elements.
'The folder information and image array information is supplied as HoodEventsArgs to pass parameters to .DoWork
Dim TestHoodStructure As New HoodDirImageFileClass(MAINPATH & "\" & IMAGEFOLDER)
Dim Missing As String() = TestHoodStructure.FindMissingImages()
FilesToDelete(MAINPATH & "\" & IMAGEFOLDER & "\thumbs_MIS", MAINPATH & "\" & IMAGEFOLDER, "*.jpg")
If Missing(0) <> "" Then
Dim HEVENT As New HoodEventArgs(IMAGEFOLDER, MAINPATH, Missing) ' needed to pass parameters to thread
FindOpenThread(Nothing, HEVENT) ' finds an open thread and then creates thumbnails on that thread.
FilesToDelete(MAINPATH & "\" & IMAGEFOLDER & "\thumbs_MIS", MAINPATH & "\" & IMAGEFOLDER, "*.jpg")
End If
Return Missing
End Function
' ************ THREADING SUBROUTINES **************************
Public Sub FindOpenThread(ByVal sender As Object, ByVal e As EventArgs)
'(ByVal ImageFolder As String, ByVal Parser As String)
If BW1.IsBusy = False Then
'BWkeep = BW1
BW1.WorkerReportsProgress = True
BW1.WorkerSupportsCancellation = True
BW1.RunWorkerAsync(e)
ElseIf BW2.IsBusy = False Then
BW2.WorkerReportsProgress = True
BW2.WorkerSupportsCancellation = True
BW2.RunWorkerAsync(e)
ElseIf BW3.IsBusy = False Then
BW3.WorkerReportsProgress = True
BW3.WorkerSupportsCancellation = True
BW3.RunWorkerAsync(e)
ElseIf BW4.IsBusy = False Then
BW4.WorkerReportsProgress = True
BW4.WorkerSupportsCancellation = True
BW4.RunWorkerAsync(e)
ElseIf BW5.IsBusy = False Then
BW5.WorkerReportsProgress = True
BW5.WorkerSupportsCancellation = True
BW5.RunWorkerAsync(e)
Else
' just wait
End If
'BWkeep.RunWorkerAsync(e)
'BW1.RunWorkerAsync(e)
'BGWorkerThumbs.RunWorkerAsync(e)
End Sub
Public Sub BWkeep_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles BW1.DoWork, BW2.DoWork, BW3.DoWork, BW4.DoWork, BW5.DoWork
SetLowProcessPriorityLevel()
'Public Sub BWkeep_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BGWorkerThumbs.DoWork, BW1.DoWork
'MsgBox("This method works")
'MsgBox("Ok this box was entered")
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
Dim ARG = e.Argument
Dim IMAGEFOLDER = ARG.GetImageDirectory
Dim Missing = ARG.GetMissingImages
Dim TOTALIMAGES As Integer = ARG.GetImageCount
'********** RELEGATE TO A SUBROUTINE THAT CAN BE PUT ON A THREAD *****
For ImagCount = 0 To Missing.Length - 1
'WaitForAvailibility is needed because of the possibility of an image being seen but not quite ready. For
'example, it may still be in the process of being created. This will force the thumbnail creator to wait.
'Todo- This whole process should be put onto a thread.
WaitForFileAvailibility(MAINPATH & "\" & IMAGEFOLDER & "\" & Missing(ImagCount), 20) ' timout is in seconds
Try
CreateThumbsFUNCTION(MAINPATH & "\" & IMAGEFOLDER & "\" & Missing(ImagCount), 800)
' Determine number of thumbnails created.
Dim str As New IO.DirectoryInfo(MAINPATH & "\" & IMAGEFOLDER & "\thumbs_MIS")
Dim listing As IO.FileInfo() = str.GetFiles("*.jpg")
Dim TotalThumbs = listing.Length
If Missing.Length > 1 Then
worker.ReportProgress(100 * Int(TotalThumbs / TOTALIMAGES), IMAGEFOLDER)
Else
FileProgressValue = 100
worker.ReportProgress(100)
End If
Catch ex As Exception
' Just continue and hope problem fixes itself soon
End Try
Next
'********** RELEGATE TO A SUBROUTINE THAT CAN BE PUT ON A THREAD *****
If sender.Equals(BW1) Then
If BW1.IsBusy Then
BW1.CancelAsync()
End If
End If
If sender.Equals(BW2) Then
If BW2.IsBusy Then
BW2.CancelAsync()
End If
End If
If sender.Equals(BW3) Then
If BW3.IsBusy Then
BW3.CancelAsync()
End If
End If
If sender.Equals(BW4) Then
If BW4.IsBusy Then
BW4.CancelAsync()
End If
End If
If sender.Equals(BW5) Then
If BW5.IsBusy Then
BW5.CancelAsync()
End If
End If
End Sub
Public Class HoodDirImageFileClass
Public MissingJpegs(0) As String
Public CurrentJPGlist(0) As String
Public AnyJPEGimagesTF As Boolean
Public DirectoryName As String()
Public FULLPATH As String
Public TotalImageCount As Integer
Public Sub New(ByVal DirectoryFULLPATH)
'Get JPG Images located in DirectoryFullPath
FULLPATH = DirectoryFULLPATH
Dim str As New IO.DirectoryInfo(DirectoryFULLPATH)
Dim listing As IO.FileInfo() = str.GetFiles("*.jpg")
If listing.Length = 0 Then
AnyJPEGimagesTF = False
Else
AnyJPEGimagesTF = True
End If
TotalImageCount = listing.Length
End Sub
Public Function GetCurrentJPGlist() As String()
Dim str As New IO.DirectoryInfo(FULLPATH)
Dim listing As IO.FileInfo() = str.GetFiles("*.jpg")
Dim counter As Integer = 0
For Each cc In listing
ArrayAddItemHOOD(CurrentJPGlist, cc.Name)
Next
Return CurrentJPGlist
End Function
Public ReadOnly Property IsAnyJPEG As Boolean
Get
Return AnyJPEGimagesTF
End Get
End Property
Public ReadOnly Property GetJPG As Boolean
Get
Return AnyJPEGimagesTF
End Get
End Property
Public Property GetMissingImageArray As String()
Set(ByVal value As String())
MissingJpegs = value
End Set
Get
Return MissingJpegs
End Get
End Property
Public Function FindMissingImages() As String()
Dim Missing As String()
If Directory.Exists(FULLPATH & "\thumbs_MIS") = False Then
Directory.CreateDirectory(FULLPATH & "\thumbs_MIS")
End If
Dim MissingString As String = CompareFolders(FULLPATH, FULLPATH & "\thumbs_MIS", "*.jpg") ' CompareFolers is part of HoodMainModule.
Missing = Split(MissingString, ";")
Return Missing
End Function
End Class
Last edited by adrian1906; May 2nd, 2012 at 12:17 AM.
Reason: Update
-
May 2nd, 2012, 09:37 PM
#6
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
-
May 4th, 2012, 08:26 AM
#7
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
Is my request considered advanced? I ask because I'm having difficulty finding and answer when usually I do not.
-
May 4th, 2012, 03:45 PM
#8
Re: Fastest way to generate thumbnails.
I see you are using BackgroundWorkers and you are using a single event handler for all of them. I wouldn't expect that to be a problem but this is multi-threading so there may be an issue there. I do all my threading directly so I'm a little unfamiliar with the intricacies of the BackgroundWorker component. Jmc knows more about this component than me.
-
May 4th, 2012, 05:56 PM
#9
Junior Member
Re: Fastest way to generate thumbnails.
Why generate your own thumbnails when so many applications already do that. Windows 7 keep them in C:\Users\YOURNAME\AppData\Local\Microsoft\Windows\Explorer directory. Picassa also maintains a Thumb library. All we have to do is find out how to access them.
-
May 4th, 2012, 10:33 PM
#10
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
Niya,
I use the backgroundworker because I was able to figure out how to control objects that were not within the thread such as a progress bar. I think I have a good grasp as to how it works but I am not confident that I'm using any best practices.
-
May 4th, 2012, 10:40 PM
#11
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
Ray,
I believe I'm using the thumbnail name incorrectly. I need a lower resolution version of the original file...on the order of 800x600 pixels (sometimes a little bigger). The program is a slideshow and I need the images to load as fast as possible. My code is set up to create thumbnails as fast as possible and the images may come in batches of 100 to 150 files (4MB each). I'd like to be able to create all the thumbnails in less than 1 minute.
-
May 10th, 2012, 08:57 AM
#12
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
I downloaded this book on Parrallel Processing with .NET
http://www.microsoft.com/en-us/downl....aspx?id=19222
I'll report back when I'm finished reading it.
Adrian
-
Jun 12th, 2012, 11:45 PM
#13
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
I'm writing to give the latest.
I created a pseudo-cluster to process the thumbnails. I have 4 networked computers (three dual core machine and one single core). I run the thumbnail generator on all computers. Before creating a thumbnail, it checks to see if it is currently being created by another process, if so, it grabs the next available file in the common folder to create a thumbnail. On all 4 computers (7 cores), the CPU is near 100%. The UIs on all 4 computers are sluggish. It took about 30 minutes to process about 2700 images (~2.5MB each). I tried modifying the CPU priority in VB.net but I didn't see any change.
So, I figured out a faster way to create the thumbnails, now I just need to know how to do it without bringing my UIs to a halt for 1/2 hour.
Thanks.
-
Jun 13th, 2012, 01:54 AM
#14
Hyperactive Member
Re: Fastest way to generate thumbnails.
Thread Pool is made to manage multiple background threads
http://www.codeproject.com/Articles/...ackgroundWorke
-
Jun 13th, 2012, 06:29 AM
#15
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
Looks promising.
I can't wait to code it up and give it a shot. I'll report back with my findings.
Thank you.
-
Jun 13th, 2012, 08:36 AM
#16
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
Looking over the article in more detail, it appears that using BackgroundWorker will already take advantage or share the same advantages of a thread pool.
I believe the reason why my UI's are unresponsive is because my thumbnail creater is using the CPU to the max.
I would think that this is a common issue however I am having difficulty finding any best practices for dealing with computationally expensive operations and GUIs. For example, will the use of the wait command help? If so, how do I determine the duration and location in the code?
Thank you.
-
Jun 13th, 2012, 09:08 AM
#17
Hyperactive Member
Re: Fastest way to generate thumbnails.
I think that if you calculate ALL your expensive code in background thread UI can not be blocked.
For example if I have x2 proc then let say 1st core would be 100% and second 0% (I must stress that windows decides how much get to which core - i think it will be around 1c:80-100%; 2c:20%).
In background threads I have always followed jmcilhinney-s examples and I never have had a problem http://www.vbforums.com/showthread.php?t=471889
But I also think that 2 programs like that can and will block dual core x2 processor.
Thread pool have I never personally used. It is here to manage multiple background threads for us.
-
Jun 13th, 2012, 09:16 AM
#18
Re: Fastest way to generate thumbnails.
I normally do not do much with images but I did have the need to write an app a couple of years ago in VB.Net where the app would pull pictures from a camera resize them and create thumbnails then create a record in a database with the filenames and the item number for use on a web site. I can't remember now how I did it but seems like it was a good bit faster than what you are seeing and it was using only a single thread on an older dual core AMD box x2 4400+
I'll see if I can locate the code I used.
-
Jun 13th, 2012, 09:24 AM
#19
Re: Fastest way to generate thumbnails.
I think this is the code I used to handle the sizing though I am not totally sure as I have more than one variation and it has been over 2 years.
Code:
Public Function ResizeImage(ByVal SourceImage As Image, ByVal NewWidth As Integer, ByVal NewHeight As Integer) As Image
Dim bm As New Bitmap(SourceImage)
Dim OldWidth As Integer = bm.Width
Dim OldHeight As Integer = bm.Height
Dim TmpRatio As Double = 0
If OldHeight > OldWidth Then 'portrait mode
If OldHeight / 4 <> OldWidth / 3 Then 'not 4:3 portrait mode
If NewHeight > OldHeight Then
TmpRatio = NewHeight / OldHeight
NewWidth = NewWidth * TmpRatio
Else
TmpRatio = OldHeight / NewHeight
NewWidth = NewWidth / TmpRatio
End If
Else 'is 4 x 3 portrait need to reverse our vars for proper scale
'Check with custom if we want to use the full size for portrait or if we should scale down to the height specified.
OldHeight = NewWidth
OldWidth = NewHeight
NewHeight = OldHeight
NewWidth = OldWidth
End If
End If
Dim thumb As New Bitmap(NewWidth, NewHeight)
Dim g As Graphics = Graphics.FromImage(thumb)
g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
g.DrawImage(bm, 0, 0, NewWidth, NewHeight)
g.Dispose()
Return thumb
End Function
-
Jun 13th, 2012, 11:15 AM
#20
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
Datamiser,
My thumbnail creator slows down all the computers on the cluster. It is just noticable on the UI because that is the screen I am trying to interact with. The buttons and other controls are slow to react. I may be mischaracterizing my problem by calling the UI unresponsive.
Thanks for digging up your code. It does't look much different than the one I'm using. I have an additional step to rotate the image based on it's EXIF infromation. I can possibly postpone that operation to the thumbnail instead of the full image or benchmark with that feature disabled...(I'm going to try that.)
Also, I noticed that you bring in the System.Drawing.Image
object. I create the System.Drawing.Image
object with every call. I'm not sure if it makes a difference.
Last edited by adrian1906; Jun 13th, 2012 at 11:22 AM.
-
Jun 13th, 2012, 12:03 PM
#21
Re: Fastest way to generate thumbnails.
I don't think it would make much difference wether it was loaded in the function or passed to the function, may even be slightly faster if it were loaded in the function rather than loaded then passed. In my case the image is loaded for a different reason so I pass the image to the function to resize.
It is possible that my memory is faulty and it took longer than I thought to process the images but seems like the longest delay I saw when processing a folder was just a couple of minutes and most of that was in loading the images.
-
Jun 13th, 2012, 12:49 PM
#22
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
My current benchmark is about 1.5 seconds per image (2700 images in about 30 minutes). I'd like it to be about 1/2 that but I can live with it. I just need to figure out how to manage the processes so that my computer doesn't slow to a crawl.
So far I have two things to try out. 1.) Rotate the thumbnail instead of the original image 2.) throw in some sleep commands to tame the processes.
-
Jun 20th, 2012, 07:46 PM
#23
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.-Update (With Graphics)
UPDATE....
I added a
System.Threading.Thread.Sleep(2000)
into my thread after a thumbnail is made.
My GUI is a little more responsive but still a little choppy. Here is my Windows Performance Meter. The spikes indicate the sleep commands.
-
Jun 21st, 2012, 02:37 PM
#24
Re: Fastest way to generate thumbnails.
Just out of curiosity, are multiple instances of this program running on different computers targeting the same folder using a shared drive ?
-
Jun 21st, 2012, 02:57 PM
#25
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
Niya,
Yes, you are correct. Each of the 4 computers are running their own instance of the program. The executable was copied to each machine and then ran independently. At startup, I select the target directory. For 3 of the 4 computers, I selected the shared folder. For the computer that contains all the images, I selected the folder directly.
Adrian
-
Jun 21st, 2012, 03:04 PM
#26
Re: Fastest way to generate thumbnails.
The thing that bothers me is that Thread.Sleep should degrade the performance of any operation that uses it. Thread.Sleep pauses a thread completely. Nothing can happen on a paused thread. Would it be any trouble to upload the application source so I can look at how you do it ?
-
Jun 21st, 2012, 03:48 PM
#27
Re: Fastest way to generate thumbnails.
You have four threads hammering two cores.
Up the number of computers.
Reduce the number of threads.
Are you timing everything? Have you identified any bottlenecks?
-
Jun 22nd, 2012, 08:22 AM
#28
Thread Starter
Addicted Member
Re: Fastest way to generate thumbnails.
Niya..
I also do not like using thread.sleep as it defeats the original purpose to generate thumbnails as fast as possible. However, it looks like resources are being stripped from my GUI forcing it repond poorly.
As far as uploading the code, do I upload to this thread or in the CodeBank?
Milk ...
It was actually hammering away on 7 cores acros 4 computers.
I am not timing everything? Can you please explain what you mean. I am simply putting my thumbnail creation subroutine on a thread and having it work until completion...(recently I put in the thread.sleep() as an attempt to tame the process)
The purpose for the multiple threads is so that thumbnails can be generated in different folders simultaneously. I felt that 5 folders at one time would satisfy my needs.
-
Jun 22nd, 2012, 09:49 AM
#29
Re: Fastest way to generate thumbnails.
The code bank is for working code samples. Code related to questions should be posted in the thread where the question resides, just attach it to a post but do not include binaries, source only.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|