Displaying .jpg on form from memory rather than loading from disk

techsupportpete
02-08-2005, 05:27 PM
Hi
I currently have a system whereby my application receives jpeg data from an ethernet connection. I want to be able to display the jpeg on a form (in pictue box, or frame or anythin!) without first having to save the file to disk and subsequently reload into a picture box or similar.

Essentially this is a ethernet-webcam type application and i want to be able to display 25frames per second at VGA 640x480 quality.

Any suggestions how best to do this? My data is formatted in a jpeg standard file structure and should i save it to disk first, i have no problems then loading it into a picturebox, however i want to save the time and display it immediately from memory.

Any suggestions and code tips would be reall great. Im not sure if i need to just send the 'array' to the picture box in some way, or do i need to use the bitblt methods (which i dont understand yet).... Can I simply load the picturebox with a chunk of memory rather than contents of a file? (seems logical to me,...but how to do it!!!???)

cheers for any help,
p
:confused:

OnErr0r
02-08-2005, 05:30 PM
If the header for the JPG is intact, you can load it from memory (Byte array). If you can write it to disk and load with LoadPicture, the header is there.

Search the forum for PictureFromRes by me.

techsupportpete
02-08-2005, 05:39 PM
thanks for such a fast response. I have found the posts you refer to and I will give it a go.

thanks so much, - i knew there must be a way!!!
rgds,pete

techsupportpete
02-09-2005, 04:42 AM
If the header for the JPG is intact, you can load it from memory (Byte array). If you can write it to disk and load with LoadPicture, the header is there.

Search the forum for PictureFromRes by me.

Hi,
I have tried the method you suggest, and have been able to display the images. However in my test program (which loads 25 jpegs from disk into byte arrays) I try and display them at sufficient interval to get 25frames per second, (using an accurate timer, rather than the vb timer).

Anyway, I can only get approximately 12 frames per second as it takes 2seconds to display all the images.

Previously I have tried loading the images into an IMage List and using the same timer code displaying these. I am actually able to display around 30 frames per second using this method.

Am I doing something wrong in the way Im using your suggested function? or is there any way to speed the process up?


Im initialising the array using (at form load):
<CODE>
Private Function InitialiseJPEGArray(RequiredArray() As Byte, FileNum)

Dim FF As Integer

FF = FreeFile()
Open (ImageRoot & FileNum & ".jpg") For Binary As FF

ReDim RequiredArray(LOF(FF))
Get FF, , RequiredArray
Close FF[/INDENT]
End Function
</CODE>

then im calling at the regular timer intervals using:
<CODE>
Set picImage.Picture = PictureFromRes(MyArray1())
</CODE>

and then the function picturefromres is as per the code posted by you.

Any suggestion / help?

Kind Regards


pete

OnErr0r
02-09-2005, 10:02 AM
My comment about saving to disk and loading with LoadPicture was to verify that the data you are receiving is actually a complete JPG. Once that test was completed, and worked properly, you would switch to passing the data purely from memory. So writing to disk would no longer be necessary.

When you say your application, "receives jpeg data from an ethernet connection". Does that mean to a file or stream or what?

I had the feeling from your first post that you had an array you could just pass directly to my function and bypass writing to disk completely.

techsupportpete
02-09-2005, 10:30 AM
hi,
sorry for the confusion.

My FINAL application will NOT need to load the jpegs from disk. You are correct. And yes I therefore plan to pass your function an array directly without using the disk.

At the minute, however, I am still coding the 'embedded' side of my device and it isnt talking to my PC yet so I cant pass the jpeg file across. However the PC application is pretty much ready and I just want to do a sanity check if you like to ensure that I can display the pictures fast enough.

So today I wrote a simple test program which has 3 different options - to test 3 different methods of displaying the pictures on the form so that I can compare the timedifferences between them.

Option 1 does a simple picImage.Picture = LoadPicture(FileName) with a new FileName everytime the timer expires to display a new image.

Option 2 loads the 25 different images into an ImageList control at the time the form loads and then selects the next picture in my picture array to display the image using:
picImage.Picture = imlImages.ListImages(next_frame).Picture

Option 3 - which is your method (and the one that I eventually want to use) loads the 25 jpegs into 25 different Byte Arrays when the form loads, and then calls:

Set picImage.Picture = PictureFromRes(MyArray1())

everytime I wish to display a new image (except the next time it uses MyArray2() and so on....


My results are as above, and the displaying of the images using the PictureFromRes seems to be about half as fast as displaying from the ImageList. Essentially since its displaying the pictures from memory I would expect them to work at about the same speed.

I can put the data I receive from my remote device into any format but I am anticipating putting it into a ByteArray to pass directly to your function.
The final version will not buffer images and will just display the next image as soon as it has been received (and checksums validated etc...)


Hope this makes things a little clearer.

Kind Regards,
Pete

OnErr0r
02-09-2005, 10:39 AM
Ok, that makes more sense now, thanks. :)

First off, test the compiled EXE when doing speed tests. The IDE often gives odd results.

Secondly, since you will be calling the function repeatedly, you should consider caching the GUID outside of the function. This simply means calling CLSIDFromString once before PictureFromRes is called and giving the GUID module wide scope. A class would be handy instead of a BAS module, as you could use the Initialize event for the CLSIDFromString call.

I'm not sure how much difference that will make, as the overhead could be in CreateStreamOnHGlobal and/or OleLoadPicture.

Other alternatives would be GDI+, which isn't really known for being terrible fast, or the intel dll.

techsupportpete
02-09-2005, 11:01 AM
just tried what you suggested. On loading my form I have called :

CLSIDFromString StrPtr(SIPICTURE), tGuid
(making tGuid private within the module)

then removed this call from your function. Repeated the tests and they were slightly faster, but still taking around 2 seconds to display the 25 images (when I want it to be 1second or less)

not really sure what to do for the GDI or the intel options to try. Can you suggest a link or example program to explain further?

thanks so much for your help!

rgds
pete

OnErr0r
02-09-2005, 12:12 PM
Check vbaccelerator.com for the intel JPG example.

techsupportpete
02-09-2005, 05:06 PM
Check vbaccelerator.com for the intel JPG example.

Thanks so much for your help OnErrOr.
Have found the files your refer to and modified them up to see what sort of performance I can get. Looks like I should almost manage to display 25frames per second of JPEGs with a file size of around 50kB. :cool:

Using the intel tests and the same graphics files as tested in my previous posts I could actually achieve around 33frames per second (only 9kB files) but in reality my application is likely to need to handle files of upto 50kB so looks like the intel solution is the way forward here.

Thanks so much for your help :cool:

Kind Regards,

Pete
:D

techsupportpete
03-21-2005, 07:02 AM
Hi Guys,
So my system is now nearly fully working and is communicating with the Embedded video device. I can display the images on my PC using the intel graphics function as has been discussed above, however there remains one outstanding issue.

Presently the system captures the video in interlaced format so that for each jpeg I receive, its actually only half the number of lines required to make the full jpeg image heigh (640x480). ie only 240 lines are sent. Really to make the full image I should grab 2 frames and interlace each of the lines to create the full 480 height. However, inorder to get more frames per second, I am willing to dispense with the second frame and create the jpg from only the one frame.

Therefore using the intel method is there any way to stretch the image (by drawing each line twice) so that it fills the 640x480 window?

I attach a sample jpeg captured, and you can see that it is compressed by a half...

eg is there a redraw, or a stretch to picure box function? or do i need to edit the jpeg code so that it forces each line to be drawn twice? (any tips on where that is in the code!!?)

if you can help then that would be great,

thanks,

pete

OnErr0r
03-21-2005, 08:56 AM
IIRC, the vbaccelerator example uses a DIB along with the intel dll. If that is the case, you should be able to use StretchDIBits to draw the DIB stretched in one direction.

techsupportpete
03-21-2005, 09:01 AM
OnErrOr - thanks so much. - great help as usual!!!
Can you provide more info about StretchDIBits function and how I use it? or is there an example somewhere that you know?

thanks.
peter

OnErr0r
03-21-2005, 09:19 AM
Assuming you are using the cDibSection class, you would make another function like PaintPicture, call it StretchPicture if you like, and substitute StretchBlt for BitBlt. If that doesn't work, then you could try StretchDIBBits like so:

StretchDIBits lhDC, 0, 0, lWidth, lHeight * 2, 0, 0, lWidth, lHeight, ByVal m_lPtr, m_tBI, DIB_RGB_COLORS, vbSrcCopy

techsupportpete
03-21-2005, 03:05 PM
hi OnErrOr

Great. Thanks a million -- AGAIN! This worked perfectly.

For completeness and for other peoples use (if interested) here is the additional stuff I needed to do.

add a new function in the cDIBSection Class: (remember I wanted to scale the height by x 2)


Public Sub ResizePaintPicture( _
ByVal lhDC As Long, _
Optional ByVal lDestLeft As Long = 0, _
Optional ByVal lDestTop As Long = 0, _
Optional ByVal lDestWidth As Long = -1, _
Optional ByVal lDestHeight As Long = -1, _
Optional ByVal lSrcLeft As Long = 0, _
Optional ByVal lSrcTop As Long = 0, _
Optional ByVal eRop As RasterOpConstants = vbSrcCopy _
)
If (lDestWidth < 0) Then lDestWidth = m_tBI.bmiHeader.biWidth
If (lDestHeight < 0) Then lDestHeight = m_tBI.bmiHeader.biHeight
StretchBlt lhDC, lDestLeft, lDestTop, lDestWidth, lDestHeight * 2, m_hDC, lSrcLeft, lSrcTop, lDestWidth, lDestHeight, eRop

End Sub


Then added the new function in my application: (where picImage was the name of a picture box control on my form)


Private Function cmdResizePaint()
m_cDib.ResizePaintPicture picImage.hdc
End Function


and then simply called this as before with the pointer, and bytearray:


.
.

lSize = Len(rxVideo.Data) 'get required array size from received data
ReDim Newimage(0 To lSize - 1) As Byte 'resize array
'load the array from the receive data string from ethernet port
For x = 0 To UBound(Newimage())
Newimage(x) = Asc(Mid(rxVideo.Data, x + 1, 1))
Next x

lPtr = VarPtr(Newimage(0)) 'get pointer to image
If LoadJPGFromPtr(m_cDib, lPtr, lSize) Then
cmdResizePaint
end if
.....


job done!

thanks so much for your help!

rgds
pete
:D

OnErr0r
03-21-2005, 03:29 PM
No problem. I should have mentioned, for better quality, use SetStretchBltMode with HALFTONE before you call StretchBlt.

techsupportpete
03-21-2005, 05:19 PM
No problem. I should have mentioned, for better quality, use SetStretchBltMode with HALFTONE before you call StretchBlt.

great, thanks.
I have implemented this, but didnt actually see that much difference.
Is this something that you have to call for EVERY image you do a stretchBltBit to? or is it called for the .hDC once only? I want to try and keep execution speed as fast as possible so that my frame rate stays high, so if the calls only needed once I will do that at initialisation....

thanks so much for all your help,

kind regards
pete

OnErr0r
03-21-2005, 05:21 PM
Once per the hDC should be sufficient. The more percentage change (more noticable going smaller than larger) the more difference you will see.

techsupportpete
03-30-2005, 07:03 AM
Its me again!

The embdedded camera hardware produces interlaced images. ie for every picture it generates 2 jpeg images.

The current system that I have implemented grabs only one of the frames and therefore displays half the image. (hence the requirement to use the StretchBlt above).

If I gather both fields of information into 2 seperate byte arrays, is it possible to display them both ie recreating the interlaced image?

This would mean I get all the information and would not need to stretch the image. I understand doing this may take longer, but for testing the embedded hardware is working correctly it will be useful to display the full image. normal operating mode I will just continue displaying half the image and stretching it...

Any clue if its possible?

thanks again guys,
pete
:D

OnErr0r
03-30-2005, 08:38 AM
If I understand correctly, you'll need to get both JPGs into DIBs. From there create a third DIB of the full size and alternately write a scan line from each (Or write every scan line from one to every other in the target, then repeat with the second DIB and fill in the blanks), at that point you will have the entire image. Sounds like a slower procedure any way you slice it.

techsupportpete
03-30-2005, 08:43 AM
yes, you have hit the nail on the head. This is exactly what I need to do.

im thinking that there is no pre'written function to handle this alternate line scanning method... as its perhaps a non standard requirement...

if you know of one or any suggestion on what to modify in the jpeg code im using then please let me know!

cheers
pete

OnErr0r
03-30-2005, 08:57 AM
I don't know of one, no.

As far as modification of the code, it's pretty much as I described. You've got the class there to create DIBs, so that part is trivial. The only part you'll need to create is to loop through and copy scan lines to the target. I think SetDIBits will be handy for that purpose.

techsupportpete
08-08-2006, 01:25 AM
Hi OnErr0r

Ive been using this code for a whilst now and all is working well. However recently I have seen that if I leave it running for 20mins or more then suddenly the JPEG instead of being displayed in the Picture box, suddenly jumps and is displayed in the top left of the screen.

I believe from another post I made that this is due to me running out of hDC's. However, I dont specifically ask for a new DC each time i display a jpeg (or at least i dont think i do). So, i was wondering if i needed to release a DC and get a new one for each jpeg? Is there any way to see how many DC's i have available so that I can proove this theory?

Attached is the PaintPicture class. I then invoke it by calling:

Public Function cmdPaint()

Dim hdc As Long
Dim hdcComp As Long

hdc = GetDC(picImage.hwnd)

m_cDib.PaintPicture hdc
If frmMain.chkGridlines.value = vbChecked Then ShowVideoGridLines

Call ReleaseDC(picImage.hwnd, hdc)

End Function


Previously I wasnt doing GetDC and ReleaseDC but doing things both ways didnt resolve the issue.

Any ideas anyone?

Cheers
pete
:D

OnErr0r
08-08-2006, 07:04 AM
The attached code does have some problems. I see GetDC followed by DeleteDC, which really should be ReleaseDC. And I also see a ReleaseDC before SelectObject could be called (see CreateFromPicture).

Btw, if you don't return a value, use a Sub instead of a Function.

techsupportpete
08-08-2006, 10:07 AM
I am essentially only calling PaintPicture to display my jpeg in a picture box. So that uses the code above with the code form the .cls:


Public Sub PaintPicture( _
ByVal lhDC As Long, _
Optional ByVal lDestLeft As Long = 0, _
Optional ByVal lDestTop As Long = 0, _
Optional ByVal lDestWidth As Long = -1, _
Optional ByVal lDestHeight As Long = -1, _
Optional ByVal lSrcLeft As Long = 0, _
Optional ByVal lSrcTop As Long = 0, _
Optional ByVal eRop As RasterOpConstants = vbSrcCopy _
)
If (lDestWidth < 0) Then lDestWidth = m_tBI.bmiHeader.biWidth
If (lDestHeight < 0) Then lDestHeight = m_tBI.bmiHeader.biHeight
BitBlt lhDC, lDestLeft, lDestTop, lDestWidth, lDestHeight, m_hDC, lSrcLeft, lSrcTop, eRop

End Sub


and the resizepaint

Public Sub ResizePaintPicture( _
ByVal lhDC As Long, SF_X As Long, SF_Y As Long, _
Optional ByVal lDestLeft As Long = 0, _
Optional ByVal lDestTop As Long = 0, _
Optional ByVal lDestWidth As Long = -1, _
Optional ByVal lDestHeight As Long = -1, _
Optional ByVal lSrcLeft As Long = 0, _
Optional ByVal lSrcTop As Long = 0, _
Optional ByVal eRop As RasterOpConstants = vbSrcCopy _
)
If (lDestWidth < 0) Then lDestWidth = m_tBI.bmiHeader.biWidth
If (lDestHeight < 0) Then lDestHeight = m_tBI.bmiHeader.biHeight
StretchBlt lhDC, lDestLeft, lDestTop, lDestWidth * SF_X, lDestHeight * SF_Y, m_hDC, lSrcLeft, lSrcTop, lDestWidth, lDestHeight, eRop

End Sub


so i dont think there is any getting,deleting or releasing DCs here. Other code for copying to the clipboard may be wrong, but i dont actually use that in my application.


After every JPEG is received, I call the function Paint (as originally posted above), or ResizePaint which is looks like:



Public Sub cmdResizePaint()
Dim hdc As Long
Dim hdcComp As Long

hdc = GetDC(picImage.hwnd)

m_cDib.ResizePaintPicture hdc, SFactor_X, SFactor_Y
If frmMain.chkGridlines.value = vbChecked Then ShowVideoGridLines

Call ReleaseDC(picImage.hwnd, hdc)

End Sub


So im not sure where my dc's could be going!

any thoughts?
cheers
pete

OnErr0r
08-08-2006, 08:15 PM
Where do you instantiate the m_cDib instance? Where is m_hdc set?

techsupportpete
08-09-2006, 02:29 AM
Where do you instantiate the m_cDib instance? Where is m_hdc set?

Ahhhahhh!
maybe this is it. Im not sure Im clearing it up when I should.....

in the top of my module I reference m_cDib as:


Public m_cDib As New cDIBSection


I do perform a call to:

Call m_cDib.ConfigStretchBltMode(frmMain.picImage.hdc, HALFTONE) 'set resize mode for better quality


after reception of the jpeg via ethernet, I call LoadJPEGFromPtr to create the Dib. This is pretty much like the Intel DLL except it handles interlaced images too and can interlace the odd and even frame.


if LoadJPGFromPtr(m_cDib, lPtrEven, lSizeEven, lPtrOdd, lSizeOdd) then
'valid jpeg image, display it using the paint method
end if


then, inside of the function LoadJPGFromPtr it calls the following initialisation function for the dib:

' Create a buffer of sufficient size to hold the image:
If cDib.Create(IFrameWidth, IFrameHeight) Then



QUESTION: Inside the CreateDIB Function there is the following:

If (m_hDC <> 0) Then
....
Else
DeleteDC m_hDC
m_hDC = 0
End If


Could this if test fail and return the m_hDC of zero? Would this be something to do with the problem? I will run a test now and see what happens....

Why does the function createCompatibleDC(0), does this not get a DC from the desktop? Should I replace 0 with the picturebox.hdc on my form ?:
m_hDC = CreateCompatibleDC(0)

Any other ideas what I need to do during the setup or tear down of the dib?


Am I missing something obvious? Sorry if this seems stooopid!

cheers
pete
:confused:

EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum