Re: Image Transition Effect (Using Gradient/Alpha Mask) Image Change One Image To Ano
------------------------------------------------------------------------------------
1. i like to update the image specific number of times but not loos any pixel (0-255)/256
------------------------------------------------------------------------------------
I assume you are trying to speed things up by skipping how often you refresh the picture, but as boops boops mentioned in post #14, the looping through all the pixels and comparing is the bottle neck in the code (takes longer to do) than the refreshing of the image.
So, what boops boops did was reduce the number of times you looped through all the pixels by skipping grayscale values.
A key part of making that work, is not to compare to see if the value was equal to the mask (quickBmpMask.Pixels(p) = c), but to see if it was less than or equal (quickBmpMask.Pixels(p) <= c).
That way as you loop through, you copy all the pixels equal to c, and all the pixels less than c, which will include the ones skipped.
So, you only need one loop when you skip four, not four loops like you're showing.
Since you skip four values, but then loop through the four values in four loops to catch the skipped ones, you are not really saving much time.
So, the first way to really save time is to change "= c" to "<= c" and only do one loop for each level.
------------------------------------------------------------------------------------
2. so, my question is how can i divide each number of pixels into fixed number of image/frames?
------------------------------------------------------------------------------------
This was covered in post #15.
Once you use "<= c", so you only have one loop per step, how do you end up at 255 at the last loop so all pixels are copied.
The answer is to change where your loop starts. Don't start at 0 unless the step size will divide evenly into 255. Start at a number greater than 0 that will allow the step size to accumulate and hit 255 exactly.
Your examples:
256/25 = 10.24 'You should actually be dividing into 255, so 255/25= 10.2 or 10 with 5 leftover.
255 Mod 25 = 5 'Mod gives you the remainder (number leftover) after the divide.
So, you start your loop at 5, and stepping by 25 will make your last loop be at 255.
Your second example is not that great, very coarse, but still would be.
255 Mod 100 = 55, so start your loop at 55, second time through it will be 155, and last time 255.
But, if you're willing, I will show you a different way to speed up the process by greatly eliminating the multiple passes through the pixels of the images and doing many,many compares, so you may choose not to use skipping to speed things up.
But, if you still want to skip, you can still do that even after speeding up the pixel compare process.
I didn't show you this before, because you might find it complicated, so didn't post it until seeing how things progressed.
I guess, now is the time to try.
Your current process involves comparing every pixels of the mask against every level of grayscale value you want to step through.
That gives you a nested loop, so multiply the number of pixels in the image times the number of levels of grayscale you want to step to get the total number of pixel comparisons you are doing.
In your original example, you had pictureboxes 352 width, 288 height, which is 101376 pixels. You loop though the picture 256 times to do the transition, so the total number of comparisons in your loops is 256 * 101376 = 25952256 (almost 26 million comparisons!).
Since you are using grayscale, there are only 256 different colors in your mask bitmap, so it would make a lot of sense if you could collect all the level 0 pixels in a list, and then all the level 1 pixels in another list, and the level 2 pixels in another list, and so on.
Now, instead of searching the whole image to find all the level 0 pixels and copy them, you only process the pixels in the 0 list. Then for level 1, only process the pixels in the level 1 list.
The end result, you make one pass through the mask bitmap, sorting the pixels into one of the 256 lists when you load the mask bitmap, then you never have to do that again until you load a different mask bitmap.
Then when you do the image transition, you don't have to do any comparisons at all, you just process the pixels in each list. So instead of 26 million comparisons and 101376 copies, you only do the 101376 copies. In theory that is around 256 times less work, and in practice ends up making the pixel processing about 200 times faster, so the time to refresh the screen does become the slower part of the process.
But, overall, the speed may now be too fast, even when you refresh for all 256 levels, so you will need to add a delay to the process, and then you may need to do some skipping to balance out the delays, if the delays can not be finely tuned.
OK, while the sorting of the pixels into lists and processing them may sound complicated, it isn't really, and the resulting code is not long at all.
The most compact and quick to process list is simply to use the pixel offsets within the pixel array as links in a linked list.
So, you need another array of integers, the same size as the number of pixels in the image. This will hold the offset to a given pixel and double as the link in the linked list at the same time. You need another array 0 to 255 to hold the offset to the first pixel offset for each level of grayscale. You preset this array with the value -1, indicating the end of the list. Since we haven't process the bitmap yet, there are no pixel offsets in any of the lists, so the -1 in the 0-255 array indicates that level has no pixels at this point.
For a given level, e.g. level 0, we don't care what order the level 0 pixels are added to the list, we just want to know what all the level 0 pixels are, so this makes populating the list easy. You just loop through the pixels by offset (from 0 to 101376) and whatever level the pixel is (0 to 255), you write the offset currently stored at that level in the level array (0 to 255) at this offset (0 to 101376), and write this offset (0 to 101376) to the level array.
When you're done, the level array (0 to 255) will hold the offset (aka index) of the last pixel found with that level of grayscale.
So, for example, to copy all the pixels with a grayscale level of 0, you read the value from index 0 of the (0 to 255) array. If the value is not -1, then you copy the pixel indicated by the value from bitmap to bitmap. You then use that value to get index into the (0 to 101376) and if it is not -1, you do the same thing, copy the pixel from bitmap to bitmap, and use the value to index to the next value. When you find an index with -1 in it, you are done and have transferred all the pixels that were level 0 in the mask, between the two bitmaps.
So, you only look at and copy the pixels for the grayscale level you need to copy, and don't waste any time looking at pixels that are not at that grayscale level.
So, the relevant code looks like this:
First the code that creates the two array we need for the linked lists, and the code that fills those arrays with the link/pixel offset values.
Code:
Dim maskBase(255) As Integer 'the base entry into the linked lists for each grayscale level
Dim maskLens() As Integer 'convert MaskPixels to Linked Lists so we can make one pass through the mask to create the lists,
'In the form load
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
'....Existing code to Init and set up PicB and BmpMask
'
'Create the linked lists from the Mask picture
ReDim maskLens(quickBmpMask.Pixels.Count - 1)
Dim c As Integer
For i As Integer = 0 To 255 : maskBase(i) = -1 : Next 'Set all the base pointers to -1 (end of list)
For p As Integer = 0 To quickBmpMask.Pixels.Count - 1 'Loop through all the pixels in the mask and link them into their respective lists
c = quickBmpMask.Pixels(p) And &HFF 'Isolate the Grayscale value (using the Red Color level)
maskLens(p) = maskBase(c) 'link this pixel to the last pixel found for this label
maskBase(c) = p 'Put this pixel as the new base (first pixel of the link list)
Next
End Sub
If we didn't want to skip values, but just do the full 256 grayscale transitions, refreshing the image each time, the process would be just this:
Code:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Init() 'Restore PicA to the original image
Dim p As Integer
For i = 0 To 255 'For each grayscale level
p = maskBase(i) ' Get the first pixel offset at this level
Do While p <> -1 ' While we haven't reached the end of the list
quickBmpPicA.Pixels(p) = quickBmpPicB.Pixels(p) ' Copy the corresponding pixel from picB to picA
p = maskLens(p) ' Get the offset of the next pixel
Loop ' Loop
picA.Refresh() ' Refresh picA so we can see the result of each of the 256 level transitions
Next
End Sub
The outer loop plus the inner loops will only loop once per pixels, plus 256 for detecting the end of list (-1) value, so very efficient compared to the original.
But, since it is so efficient, as already mentioned, we will want to add a delay to slow it down for visual effect, and then may want to skip some levels to balance it out. But since each grayscale level is now its own list, we can't catch skipped levels by doing a "<= c", because we aren't doing any comparisons, they don't apply (unless you preapplied the skip value and combined the pixels into fewer, but longer, lists when processing the mask bitmap at the start). So, you need to do all 256 outer loops, and process each list, and just refresh the screen at the skip levels, so you don't skip levels of list processing, just skip levels of refreshing the screen.
The code with skip processing built into it looks like:
Code:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Init() 'Restore PicA to the original image
sw.Reset() 'Reset our stopwatch
sw.Start() 'Start timing
Dim stepsize As Integer = 4 'A step size for example. This could be control by the program to tailor the speed for the machine
Dim stepStart As Integer = 255 Mod stepsize 'Adjust the starting loop value so that we always end our loop at iteration 255
Dim p As Integer
For i = 0 To 255 'For each grayscale level
p = maskBase(i) ' Get the first pixel offset at this level
Do While p <> -1 ' While we haven't reached the end of the list
quickBmpPicA.Pixels(p) = quickBmpPicB.Pixels(p) ' Copy the corresponding pixel from picB to picA
p = maskLens(p) ' Get the offset of the next pixel
Loop ' Loop
If i = stepStart Then
picA.Refresh() ' Refresh picA so we can see the result of each of the 256 level transitions
stepStart += stepsize ' Adjust stepStart by stepsize so we trigger at the next desired point
Threading.Thread.Sleep(1) ' Short delay so we don't transition too fast
End If
Next
End Sub