|
-
Feb 23rd, 2017, 10:42 AM
#1
[RESOLVED] draw highlights over an image
Hey all,
I'm working on a pdf embellishment app and am having problems with drawing performance.
I convert pdf files to images and the image size is rather large (5000x3000). I could solve my problem by reducing the image size, but that is not an option (o how I wish it was) so I have to deal with big images.
The gist of the app is to draw the pdf image on a control, then draw an overlay image on the control after drawing the pdf image. The overlay image is the exact same size as the pdf image so I can handle panning and zooming fairly easily.
My issue is when I draw a highlight block (bright yellow over text to highlight... you know). The first obvious way to do this was to draw a simple rectangle using color.yellow with the alpha value set to make the block semi-transparent. This looks bad though. The yellow block looks faded and the text under it also looks faded.
What I have resorted to is copy the part of the pdf image that the block covers and change the white pixels to yellow. Since the pdf image is guaranteed to be black text on a white background, this works exceptionally well... the text remains black, and the highlight is nice and bright. Unfortunately it takes a while to make this white to yellow conversion and given the size of the images I'm dealing with, the problem gets exponentially worse as the highlight block size increases.
Here is my code for the conversion...
VB.net Code:
Private Sub drawHighlight(gr As Graphics, r As Rectangle)
'r is the highlight block rectangle
If r.Width > 0 AndAlso r.Height > 0 Then
'create a new image using the block size
Using img As New Bitmap(r.Width, r.Height)
RaiseEvent GetHighlightedImage(r, img) '<---this gets the underlying pdf image; img is passed ByRef
'iterate the image's pixels
For x As Integer = 0 To img.Width - 1
For y As Integer = 0 To img.Height - 1
'get the color of the pixel
Dim c As Color = img.GetPixel(x, y)
'if the R,G & B are larger than the threshold, then change the pixel to yellow
Const BW_THREASHOLD As Integer = 200
If c.R > BW_THREASHOLD AndAlso c.G > BW_THREASHOLD AndAlso c.B > BW_THREASHOLD Then
img.SetPixel(x, y, Me.BackColor)
End If
Next
Next
'finally draw the image on the overlay
gr.DrawImage(img, r.Location)
End Using
End If
End Sub
So is there a way that I can increase performance of this routine (significantly)?
Thanks for looking... Cheers!
kevin
Process control doesn't give you good quality, it gives you consistent quality.
Good quality comes from consistently doing the right things.
Vague general questions have vague general answers. A $100 donation is required for me to help you if you PM me asking for help. Instructions for donating to one of our local charities will be provided.
______________________________ Last edited by kebo : Now. Reason: superfluous typo's
-
Feb 23rd, 2017, 01:40 PM
#2
Re: draw highlights over an image
GetPixel/SetPixel is slow. you should rather use lockbits or New Bitmap with the overload where you can provide Scan0 as IntPtr. this gives you access to a bytearray where you can easily loop through and that is much faster. i see two options: one is to lockbits the entire big image and just work on the area you need to. this would also eliminate the drawimage back to the big image. or second option, you keep your drawHighlight function but use New Bitmap(...,Scan0) to create your img var. this would probably involve less code changes.
option a is better if you have many areas to color but i guess both should give you the desired speed boost.
here is an example snippet that counts the number of colors in an image:
Code:
Private Function CountColors(ByVal InBitmap As Bitmap) As UInt32
Dim dwPixels(InBitmap.Height * InBitmap.Width - 1) As UInt32
Dim hPixels As GCHandle = GCHandle.Alloc(dwPixels, GCHandleType.Pinned)
Dim bmp32Bpp As New Bitmap(InBitmap.Width, InBitmap.Height, InBitmap.Width * 4,
Imaging.PixelFormat.Format32bppRgb, hPixels.AddrOfPinnedObject)
Using gr As Graphics = Graphics.FromImage(bmp32Bpp)
gr.DrawImageUnscaledAndClipped(InBitmap, New Rectangle(0, 0, _
bmp32Bpp.Width, bmp32Bpp.Height))
End Using
Dim dwTable(CInt(2 ^ 19) - 1) As Int32
Dim index As Int32
Dim bit As Int32
Dim cDifferentColors As UInt32
For i = 0 To dwPixels.Length - 1
index = CInt(dwPixels(i) And &HFFFFFF)
bit = 1 << (index And &H1F)
index >>= 5
If (dwTable(index) And bit) = 0 Then
cDifferentColors += 1UI
dwTable(index) = dwTable(index) Or bit
End If
Next
hPixels.Free()
Return cDifferentColors
End Function
i'm sure you can find more examples and examples for lockbits on the internet.
-
Feb 23rd, 2017, 01:53 PM
#3
Re: draw highlights over an image
Check out fastpix by boops boops:
http://www.vbforums.com/showthread.p...mies-and-Dudes
Just add the module to your project and you hardly have to change your code.
-
Feb 24th, 2017, 06:46 AM
#4
Re: draw highlights over an image
For maximum performance, you could use the FastPix with an integer array for the pixels. But I suspect that a simple function like the following would be quick enough for your present purpose and it doesn't require LockBits or FastPix:
Code:
Private Function ReplaceColor(img As Image, oldColor As Color, newColor As Color) As Bitmap
Dim bmp1, bmp2 As New Bitmap(img)
bmp2.MakeTransparent(oldColor)
Using g As Graphics = Graphics.FromImage(bmp1)
g.Clear(newColor)
g.DrawImage(bmp2, Point.Empty)
End Using
Return bmp1
End Function
On quick testing. It seems to take only a couple of milliseconds regardless of image size. Of course, it only works if "oldColor" is the exact ARBG colour to be replaced so it can't preserve anti-aliasing of the text; the AA-pixels will still be blended with the original background colour.
BB
-
Feb 24th, 2017, 07:46 AM
#5
Re: draw highlights over an image
Thanks to all for your feedback...
@digitalShaman - I saw a bunch of Lockbits examples on the internets, but couldn't decipher exactly how any of them fit my situation. Coding outside of the managed space is something I haven't grasped. I am very comfortable in the nice tidy world of .net and I'm very comfortable manipulating registers and moving electrons around on small micro controllers, but I've never had a need for the "in between" so I've never learned it.
@paulg4ije - thanks for the link. It's was my ultimate solution. Dead easy drop in replacement for my existing code.
@BB - FastPix gave me a performance boost of around 5X which meets my criteria of "significant improvement". When I tried simple replacing specific colored pixels (i.e. Black), the text ended up looking stringy. Although I said the pdf files are black and white, nothing is ever black and white.. there is always some gray area. Because of that, I need to compare against a threshold value.
Thanks again to all of you
kevin
Process control doesn't give you good quality, it gives you consistent quality.
Good quality comes from consistently doing the right things.
Vague general questions have vague general answers. A $100 donation is required for me to help you if you PM me asking for help. Instructions for donating to one of our local charities will be provided.
______________________________ Last edited by kebo : Now. Reason: superfluous typo's
-
Feb 24th, 2017, 10:38 AM
#6
Re: [RESOLVED] draw highlights over an image
the is no "in between" and lockbits is part of the managed space. its just that a 24bpp bitmap is effectively an array of bytes, one byte for red, one for green and one for blue and that for each pixel. lockbits/scan0 allows you to directly access the RGB value of each pixel with the performance of a normal array read/write operation. i have not checked but i am sure that fastpix is actually using lockbits/scan0, but it encapsulates this away from you which is fair enough.
but now comes the clou. i realized that the correct way to do this is not accessing pixels but instead using a color transformation matrix when calling drawimage.
try this out:
Code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim img As New Bitmap("c:\kill\test.jpg")
Dim matrix As New Imaging.ColorMatrix(
New Single()() {New Single() {1, 0, 0, 0, 0},
New Single() {0, 1, 0, 0, 0},
New Single() {0, 0, 0, 0, 0},
New Single() {0, 0, 0, 1, 0},
New Single() {0, 0, 0, 0, 1}
})
Dim Attributes As New Imaging.ImageAttributes
Attributes.SetColorMatrix(matrix)
Using g As Graphics = PictureBox1.CreateGraphics
g.DrawImage(img, New Rectangle(0, 0, PictureBox1.Width, PictureBox1.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, Attributes)
End Using
End Sub
Last edited by digitalShaman; Feb 24th, 2017 at 11:06 AM.
-
Feb 24th, 2017, 12:00 PM
#7
Re: draw highlights over an image
 Originally Posted by kebo
When I tried simple replacing specific colored pixels (i.e. Black), the text ended up looking stringy. Although I said the pdf files are black and white, nothing is ever black and white.. there is always some gray area. Because of that, I need to compare against a threshold value.
kevin
To get goed results, you would have to convert the greyness of the pixels to transparency. Otherwise they will still be blended with the original white background and will show up as white against the new yellow background.
As an alternative, it might be worth trying with only the black pixels, and blur the result a little bit to give an impression of anti-aliasing. Here's a quick and dirty way of blurring which takes only a few lines of code. Draw the black pixels onto a yellow background, scaling the whole thing down a bit (e.g. 1/2 or 2/3 linear); then scale the result back up again. Use Graphics.DrawImage with InterpolationMode.HighQualityBilinear for scaling in both directions. Finally paint the black pixels again at the correct scale.
BB
Last edited by boops boops; Feb 24th, 2017 at 12:04 PM.
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
|