Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Problem loading a picture from an array 2

Status
Not open for further replies.

tedsmith

Programmer
Nov 23, 2000
1,762
AU
I have a webcam bmp that I can convert nicely to a jpg OK within my app using scraps of software I gleaned from another site.

The jpg picture at this point ends up as a simple byte array that has one element for each byte of the picture. It reduces the original file size by 20 and still gives a very good picture.
Eg if the picture size if 50,000 bytes. ? Ubound(MyArray) = 49999

When saved to a file (using Put) then read back using Loadpicture(Filename) it will show perfectly on an image box.

My problem is how to load the picture array directly into a picturebox without having to save and read it to temporary file as done in one example I found on another site.

Eg. MyPicture.Picture=MyArray obviously does not work

It would appear that Loadpicture is doing something more than just loading the file data into the picture property

How do I convert the array to a picture so I can load it to the imagebox?

I seem to remember an old post that had a suggestion something like this but I cant find it.
 
Unfortunately, we have two different (unconnected) networks here at work. On one of them I can reach the TT forum. On the other I have VB installed and all my VB code is there, and we have quite restricted capacity to move data between the two. So forgive me if I can't give you some worked examples.

However, I have a related app which can read raw data, manipulate it in various ways, and then display the output array as an image. It sounds like the last step is what you are after.

What I do is:

create an array where the bytes represent the r,g,b values of the picture
assign the pixel data to the picturebox image using the API sub SetBitMapBits.

strongm has also posted a lot of stuff using GDI+ which you might find interesting.

I hope that helps.

Tony
 
I've just re-read your post. Ignore what I said above.

What you are really asking is how can you take an array which represents all the bytes of a jpg image and convert it in memory (without going via a .bmp file or similar) to a bitmap in memory, which can then be assigned to a picturebox.

Hmm, sorry, that one's currently beyond me. I think I'll just slink qietly into a corner and lurk until strongm or somebody similar takes pity and provides the answer.

Then I think I'll have another thread to go into my archive ;-)

Tony
 
I have a response - just not time to post currently.

Worth pointing out that we did cover most of this for Ted about 5 years ago.
 
See PictureFromRawBits function in thread222-1541026.

You would use it like this.
[tt]MyPicture.Picture = PictureFromRawBits(MyByteArray)[/tt]
 
WIA 2.0 makes easy work of many small image processing tasks. Here is an example that does this for you, and also retrieves the dimensions and uses them:
Code:
Option Explicit

Private Sub Form_Load()
    'Get a JPEG file format image as a Byte array.
    Dim Bytes() As Byte
    Dim F As Integer
    
    F = FreeFile(0)
    Open "some.jpg" For Binary Access Read As #F
    ReDim Bytes(LOF(F) - 1)
    Get #F, , Bytes
    Close #F
    
    'Convert to a StdPicture object and use it, making use
    'of the image's dimensions as we go too.  Here we assume
    'ScaleMode = vbTwips.
    With New WIA.Vector
        .BinaryData = Bytes
        With .ImageFile
            Width = (Width - ScaleWidth) + ScaleX(.Width, vbPixels, vbTwips)
            Height = (Height - ScaleHeight) + ScaleY(.Height, vbPixels, vbTwips)
        End With
        Set Picture = .Picture
    End With
End Sub
 
Will WIA work in Windows XP or even 98? ALl the docs seem to refer to Vista and beyond. Some of my workstations are still Windows 98.

Yes while years ago I posted a problem of how to convert a BMP to JPG and send it on a LAN to an imagebox in another computer.
This had a similar question as part of it as well.

The discussion got completely bogged down with speed of LAN vs speed of BMP to JPG conversion.
I could easily send the BMP as a propertybag, accumulate it in a stream and feed the pb.readproperty to the receiving imagebox

I gave up on trying to send the JPG.
I never did get a proper solution that worked without crashing the computer so I put up with transmitting raw BMPs using propertybags and streams.

So I am looking at improving my original app that is currently working without a hitch at about 40 remote sites and growing.
The BMPs are about 1meg while the JPGS are around 40k.

With the march of time, I now however now have a fast BMP to JPG conversion that doesn't crash. This accumulates the jpg into an array.

Now I can get the jpg as an array and send it as an array but how do I convert the array back to a picture once I receive it?
The array is identical to the contents of a jpg.file (the asc value of each byte is the same)

One unsophisticated way would be to use a Ramdisk and loadpicture but I was hoping a more elegant solution could be found.

Any help directly related to this subject would be appreciated.

Like most things, it is probably quite simple once you know how!

(By the way I am 76 this week)
 
Will WIA work in Windows XP or even 98? ALl the docs seem to refer to Vista and beyond. Some of my workstations are still Windows 98.
The docs mention Vista and newer because everything prior is basically considered unsupported anymore.

WIA 2.0 has always been available for redistribution to XP SP1 or later, and comes preinstalled starting With Vista.


Here's an attempt at a WIA-less demo. Perhaps this will do what you need:
Code:
Option Explicit

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
    ByRef Destination As Any, _
    ByRef Source As Any, _
    ByVal Length As Long)
Private Declare Function CreateStreamOnHGlobal Lib "ole32" ( _
    ByVal hGlobal As Long, _
    ByVal fDeleteOnRelease As Long, _
    ByRef ppstm As Any) As Long
Private Declare Function GlobalAlloc Lib "kernel32" ( _
    ByVal uFlags As Long, _
    ByVal dwBytes As Long) As Long
Private Declare Function GlobalLock Lib "kernel32" ( _
    ByVal hMem As Long) As Long
Private Declare Function GlobalUnlock Lib "kernel32" ( _
    ByVal hMem As Long) As Long
Private Declare Function OleLoadPicture Lib "olepro32" ( _
    ByRef pStream As Any, _
    ByVal lSize As Long, _
    ByVal fRunmode As Long, _
    ByRef riid As Any, _
    ByRef ppvObj As Any) As Long

Private Function Bytes2StdPicture(ByRef Bytes() As Byte) As StdPicture
    Const GMEM_MOVEABLE = &H2&
    Const WIN32_TRUE = 1
    Const WIN32_FALSE = 0
    Const WIN32_NULL = 0
    Const S_OK = 0
    Dim GUID(0 To 3) As Long
    Dim Size As Long
    Dim hMem  As Long
    Dim lpMem  As Long
    Dim IIStream As Object
    
    'GUID for StdPicture:
    GUID(0) = &H7BF80980
    GUID(1) = &H101ABF32
    GUID(2) = &HAA00BB8B
    GUID(3) = &HAB0C3000
    
    Size = UBound(Bytes) - LBound(Bytes) + 1
    hMem = GlobalAlloc(GMEM_MOVEABLE, Size)
    If hMem <> WIN32_NULL Then
        lpMem = GlobalLock(hMem)
        If lpMem <> WIN32_NULL Then
            CopyMemory ByVal lpMem, Bytes(LBound(Bytes)), Size
            GlobalUnlock hMem
            If CreateStreamOnHGlobal(hMem, WIN32_TRUE, IIStream) = S_OK Then
                  OleLoadPicture ByVal ObjPtr(IIStream), _
                                 0, _
                                 WIN32_FALSE, _
                                 GUID(0), _
                                 Bytes2StdPicture
            End If
        End If
    End If
End Function

Private Sub Form_Load()
    'Get a JPEG file format image as a Byte array.
    Dim Bytes() As Byte
    Dim F As Integer
    
    F = FreeFile(0)
    Open "some.jpg" For Binary Access Read As #F
    ReDim Bytes(LOF(F) - 1)
    Get #F, , Bytes
    Close #F
    
    'Convert to a StdPicture object and use it.
    Set Picture = Bytes2StdPicture(Bytes)
End Sub
 
dilettante, the PictureFromRawBits I pointed is based on the same code.

I checked the documentation, MSDN states that CreateStreamOnHGlobal and OleLoadPicture are supported on Windows 2000 and later.

Ted, did you try the PictureFromRawBits function? I don't have any older OS to test if it works or not.
 
WIA 1.0 was introduced in Windows Me and Windows XP and supports scanners, digital cameras and digital video equipment. WIA 2.0 was released with Windows Vista. WIA 2.0 is targeted towards scanners but continues to offer support for legacy WIA 1.0 applications and devices through a WIA 1.0 to WIA 2.0 compatibility layer provided by the WIA service. However, video content support was removed from WIA for Windows Vista. We recommend Windows Portable Devices (WPD) API for digital cameras and digital video equipment in the future. WIA 1.0 as well as STI TWAIN drivers are still supported directly on Windows Vista and Windows 7 alongside native WIA 2.0 device drivers and imaging applications.
And WIA 2.0 can be back-installed into XP SP1 or later to get the newer image processing features you'd want in this case.

I keep a VM around for Win9x testing: Win95 OSR2 with the Desktop Update. My Bytes2StdPicture() example runs fine there so Win98 should be no problem.
 
Thanks Hypetia
PictureFromRawBits works perfectly

I also got GetBitmaBits and SetBitmapBits to work but only to a picturebox

What exactly is the difference between .Image and .Picture properties in a picturebox?
 
For those interested the original BMP to JPG conversion was written in 2003 by a John Korejwa and was available at planetsourcecode.com

This was quite a bit more featured than I needed so I stripped out only the essential code to convert my bmp

When compiled it takes less than 1/10 second to reduce a 640x480 1 meg BMP to a 20k JPG with only a barely perceptible loss in quality. (3.4g Dual core)

With a 50k JPG it looks the same as the original.

You can experiment with different compressions.

Thank you all for your help.
 
.Picture is the read/write picture property which holds a static picture object. It might be loaded at design time or runtime.

.Image is a read-only property available at runtime only and returns overall output rendered on a picture box. This output covers all graphics related output drawn on the picture box. This includes circles, lines, rectangles, printed text or any output rendered on the picture box using API functions (like DrawIcon). You must use persistent drawing mode (AutoRedraw = True) to use the Image property.

If you load a picture in a picture box and draw something on the picture using drawing methods (Circle, Line, PSet etc.) or API calls, the Image property will return a picture object which contains the drawing output superimposed on the original picture in the picture property, just as you see the picture box. Picture property will continue to return the original picture without any changes.

See Image and AutoRedraw properties for further details.
 
> the original BMP to JPG

Still a shame that you can't use WIA 2.0, as all of this is really easy to do there. The code below demonstrates directly grabbing images from a video camera as either bitmap or a jpeg array, and then displaying the jpeg array in a picturebox. I only provide the code as an example of how trivial this all is with WIA
Code:
[blue]Option Explicit

[green]' Requires that you add the
' Microsoft Windows Image Acquisition Library v2.0
' as a control allowing you to place a VideoPreview on the form
' and automatically adds trhe necessary reference as well[/green]

Private Sub Command1_Click()
    [green]' Grab a bitmap array representation of a single frame captured from the video camera
    ' convert it to a JPG array, and display it[/green]
    DisplayArray BMPtoJPGArray(GrabArray(wiaFormatBMP))
End Sub

Private Sub Command2_Click()
    [green]' No need to bother with a bitmap though ...
    ' Grab a jpeg array representation of a single frame captured from the video camera
    ' and display it[/green]
    DisplayArray GrabArray(wiaFormatJPEG) 'spoon
End Sub

Private Sub DisplayArray(GraphicsArray() As Byte)
    With New wia.Vector
        .BinaryData = GraphicsArray
        Set Picture1.Picture = .ImageFile.FileData.Picture
    End With
End Sub

Private Function BMPtoJPGArray(BMPArray() As Byte) As Byte()
    Dim ImgProc As wia.ImageProcess
    
    Set ImgProc = New wia.ImageProcess
    With ImgProc.Filters
        .Add ImgProc.FilterInfos("Convert").FilterID
        .Item(1).Properties("FormatID").Value = wiaFormatJPEG
        .Item(1).Properties("Quality").Value = 20 ' 1 to 100%
    End With
    
    With New wia.Vector
            .BinaryData = BMPArray
            BMPtoJPGArray = ImgProc.Apply(.ImageFile).FileData.BinaryData
    End With
End Function

Private Function GrabArray(GrabStyle As String) As Byte()
    [green]'Get captured image as a byte array[/green]
    GrabArray = VideoPreview1.Device.ExecuteCommand(wiaCommandTakePicture).Transfer(GrabStyle).FileData.BinaryData
End Function[/blue]
 
Hypetia: Have a star. Until now I thought I more or less understood the difference, or at least managed to muddle by, sometimes through trial and error. But that seems like a much clearer and more practical description than any I've seen from MS.

Have you ever considered working for the MS help dept? :)

BTW I added a note last night, saying more or less the above, but it does not seem to have appeared for some reason. Hmm. Odd.

Tony
 
I haven't got a 98 machine handy to try WIA but will when I get the chance to reinstall my old drive.

Interestingly I am using the old clipboard.getdata routine to retrieve the webcam picture.
This works fine in XP and WIN7 64 BIT except if I ever unplug the camera before stopping the app properly, only in WIN 7 it freezes the app in the computer and even though you can select the task bar and other programs, nothing will unload the app except removing the power to the computer! Rather odd?

I saw another alternative to the clipboard method somewhere but can't find it. Any suggestions (apart from WIA)?
 
>I saw another alternative to the clipboard method

I believe I may have mentioned capVideoStreamCallback as a possible alternative
 
Thanks for your continued interest.
I see you did mention that but I am afraid I have no idea how to implement it!
I only want to see the camera once a second.

Basically the picture part of my software current exists as you originally suggested -

Code:
'In Module
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Declare Function SendMessageAsLong Lib "user32" Alias "SendMessageA" _
                                            (ByVal hwnd As Long, _
                                            ByVal wMsg As Long, _
                                            ByVal wParam As Long, _
                                            ByVal lParam As Long) As Long
Public Const WM_USER As Long = &H400
Public Const WM_CAP_START As Long = WM_USER
Public Const WM_CAP_DLG_VIDEOSOURCE As Long = WM_CAP_START + 42


'In Form
Const ConnectCam As Long = 1034         'variables for API camera routine
Const DisconnectCam As Long = 1035
Const GET_FRAME As Long = 1084
Const COPY As Long = 1054

Sub StartCam()
'start USB camera being able to capture
SendMessage mCapHwnd, ConnectCam, 0, 0
End Sub

Sub StopCam()
'close USB Camera capture before ending otherwise application crashes on close.
SendMessage mCapHwnd, DisconnectCam, 0, 0
Clipboard.Clear
End Sub

Private Sub Timer1_Timer()
'every second
PluggedInFlag = SendMessage(mCapHwnd, GET_FRAME, 0, 0) 'get cam picture. (Win7 crashes here when camera unplugged)
If PluggedInFlag = 0 Then
   CameraStatusLabel.Caption = "NO Camera"
Else
   CameraStatusLabel.Caption = "Camera OK"
End If
SendMessage mCapHwnd, COPY, 0, 0 'put camera on clipoard
ImageCamera.Picture = Clipboard.GetData 'show in an image box
End Sub

I notice my PlugInFlag is initially zero until I plug in a camera but if I pull out the plug while it is showing, it doesn't change back to a zero. The picture in the clipboard just freezes and resumes when I plug it back in after stopping and starting again.
Is there another way of detecting if the camera has been unplugged? (this is aside from the issue of not using the clipboard which might be neater anyway)

It would be nice to have it detect a no camera so it could black out the screen and disconnect possibly avoiding the Win7 crash?

In Win7 only, it crashes at the point noted the next second it is read if the StopCam has not been activated.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top