Work on image in memory

Just starting out? Need help? Post your questions and find answers here.
abarkley
User
User
Posts: 10
Joined: Sun Jun 07, 2009 7:44 pm
Location: London, UK

Work on image in memory

Post by abarkley »

Still pretty new to PB.

I want to do some RGB or grayscale image Pixel data (image manipulation) work. I'd like these ops to work fast. This is probably best done by working directly on memory - and if necessary, using some simple assembly language routines (I did this for 8-bit MPU's many years ago, so I'd like to have another try). The alternative seems to be DRAWing on images using PLOT and POINT which I suspect will be too slow.

I see plenty of PB commands for allocating/manipulating memory addresses as well as CatchImage.

How do I load an image from disk into memory and then get it's memory start address?

Help appreciated.
User avatar
idle
Always Here
Always Here
Posts: 5890
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Post by idle »

Assuming your on windows.

If you're intending to manipulate the image and save it back to file then it's probably better to use GDI methods.

The internal PB 2D functions are pretty fast, however if speed is really a concern then you will get a bit more speed from using a DIB.

This is generally the method I use found on the forum

Code: Select all

 
Procedure CopyImageToMemory(ImageNumber, Memory)
 
  Protected TemporaryDC, TemporaryBitmap.BITMAP, TemporaryBitmapInfo.BITMAPINFO
 
  TemporaryDC = CreateDC_("DISPLAY", #Null, #Null, #Null)
 
  GetObject_(ImageID(ImageNumber), SizeOf(BITMAP), TemporaryBitmap.BITMAP)
  
   
  TemporaryBitmapInfo\bmiHeader\biSize        = SizeOf(BITMAPINFOHEADER)
  TemporaryBitmapInfo\bmiHeader\biWidth       = TemporaryBitmap\bmWidth
  TemporaryBitmapInfo\bmiHeader\biHeight      = -TemporaryBitmap\bmHeight
  TemporaryBitmapInfo\bmiHeader\biPlanes      = 1
  TemporaryBitmapInfo\bmiHeader\biBitCount    = 32
  TemporaryBitmapInfo\bmiHeader\biCompression = #BI_RGB
 
  GetDIBits_(TemporaryDC, ImageID(ImageNumber), 0, TemporaryBitmap\bmHeight, Memory, TemporaryBitmapInfo, #DIB_RGB_COLORS)
 
  DeleteDC_(TemporaryDC)
 
EndProcedure 

Procedure CopyMemoryToImage(Memory, ImageNumber)
 
  Protected TemporaryDC, TemporaryBitmap.BITMAP, TemporaryBitmapInfo.BITMAPINFO
 
  TemporaryDC = CreateDC_("DISPLAY", #Null, #Null, #Null)
 
  GetObject_(ImageID(ImageNumber), SizeOf(BITMAP), TemporaryBitmap.BITMAP)
 
  TemporaryBitmapInfo\bmiHeader\biSize        = SizeOf(BITMAPINFOHEADER)
  TemporaryBitmapInfo\bmiHeader\biWidth       = TemporaryBitmap\bmWidth
  TemporaryBitmapInfo\bmiHeader\biHeight      = -TemporaryBitmap\bmHeight
  TemporaryBitmapInfo\bmiHeader\biPlanes      = 1
  TemporaryBitmapInfo\bmiHeader\biBitCount    = 32
  TemporaryBitmapInfo\bmiHeader\biCompression = #BI_RGB
 
  SetDIBits_(TemporaryDC, ImageID(ImageNumber), 0, TemporaryBitmap\bmHeight, Memory, TemporaryBitmapInfo, #DIB_RGB_COLORS)
 
  DeleteDC_(TemporaryDC)
 
EndProcedure

UsePNGImageDecoder()
UseJPEGImageDecoder()

If OpenWindow(0,0,0,800,600,"test",#PB_Window_SizeGadget | #PB_Window_SystemMenu | #PB_Window_MinimizeGadget |#PB_Window_MaximizeGadget | #PB_Window_ScreenCentered)

  If FileSize("legion.jpg")= -1
   InitNetwork()
   ReceiveHTTPFile("http://www.idlearts.com/images/legion.jpg","legion.jpg")
  EndIf

  img1 = LoadImage(#PB_Any,"legion.jpg")

  If img1
  
    width = ImageWidth(img1)
    height = ImageHeight(img1)
    ResizeWindow(0,0,0,width,height)
    ImageGadget(0,0,0,width,height,ImageID(img1))
    
    *mem = AllocateMemory(width*height*4) ;32 bit image 
    CopyImageToMemory(img1,*mem) 
    
    Global *px.long, color.i 
    
    For x = 0 To width-1 
       For y = 0 To height-1 
           *px = *mem + ((x + (y * width)) << 2 )
             ;(BGR format)
             B= *px\l & FF
             G = *px\l >> 8 & $FF
             R = *px\l >> 16 & $FF
             
             *px\l = r << 16 ;set the pixel red     
    
        Next 
    Next 
    
    copymemorytoimage(*mem,img1) 
    SetGadgetState(0,ImageID(img1))
    
    If *mem 
      FreeMemory(*mem)
     *mem=0
    EndIf
  
  EndIf 
EndIf 

MessageRequester("DSA test","Image is now red")   
[/code]
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

Also assuming Windows: here's another sample to study and play with. Note the speed of using a structured pointer to manipulate the color bits vs. point/plot:

Code: Select all

Declare Swap24(*bmp.BITMAP) 
Declare Swap24_POINT_PLOT(img) 

; Load the image into memory 
UseJPEGImageDecoder() 
LoadImage(0, #PB_Compiler_Home+"examples\sources\data\r2skin.jpg") 

; Get information about the image 
GetObject_(ImageID(0), SizeOf(BITMAP), @bmp.BITMAP) 

With bmp 
  Debug \bmWidth 
  Debug \bmHeight 
  Debug \bmWidthBytes 
  Debug \bmBitsPixel 
  Debug \bmBits 
EndWith 

OpenWindow(0,0,0,bmp\bmWidth,bmp\bmHeight+70,"",#PB_Window_ScreenCentered|#PB_Window_SystemMenu) 
ImageGadget(0,0,0,0,0,ImageID(0)) 
ButtonGadget(1,250,522,200,20,"Swap via structured pointer") 
ButtonGadget(2,50,522,200,20,"Swap via point/plot") 
ProgressBarGadget(3, 0,552,512,20,0,ImageWidth(0)*ImageHeight(0))

disabledebugger

Repeat 
  ev=WaitWindowEvent() 
  Select ev 
    Case #PB_Event_Gadget 
      Select EventGadget() 
        Case 1 
          Swap24(@bmp) 
          SetGadgetState(0,ImageID(0)) 
        Case 2
          Swap24_POINT_PLOT(0) 
          SetGadgetState(0,ImageID(0))
      EndSelect 
  EndSelect 
Until ev=#PB_Event_CloseWindow 

Procedure Swap24(*bmp.BITMAP) 
   SetGadgetState(3,0) : cc=0
   *buffer.RGBTRIPLE = *bmp\bmBits 
   While *buffer <= *bmp\bmBits+*bmp\bmWidthBytes**bmp\bmHeight-SizeOf(RGBTRIPLE) 
     Swap *buffer\rgbtBlue, *buffer\rgbtRed 
     *buffer+SizeOf(RGBTRIPLE) : cc+1
     If cc%1000=0:SetGadgetState(3, cc):EndIf
   Wend 
EndProcedure 

Procedure Swap24_POINT_PLOT(img) 
  SetGadgetState(3,0) : cc=0
  StartDrawing(ImageOutput(img))
    For j=0 To ImageHeight(img)-1
      For i=0 To ImageWidth(img)-1
        color = Point(i,j)
        Plot(i,j, RGB(Blue(color),Green(color),Red(color)))
        cc+1
        If cc%1000=0:SetGadgetState(3, cc):EndIf
      Next
    Next
  StopDrawing()
EndProcedure 
BERESHEIT
abarkley
User
User
Posts: 10
Joined: Sun Jun 07, 2009 7:44 pm
Location: London, UK

Post by abarkley »

Thanks for that. A bit complex for this noob - useful for me to try to work through it though.

Can I rephrase the question?

Is it realistic for me to work on a bitmap in memory without making any operating system calls? You say that the native PB commands are pretty quick.

The app is fairly simple - sort of very limited Photoshop - and I'm thinking about it running on a basic netbook (altho, I know zip about Linux at the moment).
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

My example employs only one API call, GetObject_(), used to get the address of the color bits. Surely you can live with that?
BERESHEIT
coder14
Enthusiast
Enthusiast
Posts: 327
Joined: Tue Jun 21, 2011 10:39 am

Re:

Post by coder14 »

idle wrote:

Code: Select all

 Procedure CopyImageToMemory(ImageNumber, Memory)
 
  Protected TemporaryDC, TemporaryBitmap.BITMAP, TemporaryBitmapInfo.BITMAPINFO
 
  TemporaryDC = CreateDC_("DISPLAY", #Null, #Null, #Null)
 
  GetObject_(ImageID(ImageNumber), SizeOf(BITMAP), TemporaryBitmap.BITMAP)
  
   
  TemporaryBitmapInfo\bmiHeader\biSize        = SizeOf(BITMAPINFOHEADER)
  TemporaryBitmapInfo\bmiHeader\biWidth       = TemporaryBitmap\bmWidth
  TemporaryBitmapInfo\bmiHeader\biHeight      = -TemporaryBitmap\bmHeight
  TemporaryBitmapInfo\bmiHeader\biPlanes      = 1
  TemporaryBitmapInfo\bmiHeader\biBitCount    = 32
  TemporaryBitmapInfo\bmiHeader\biCompression = #BI_RGB
 
  GetDIBits_(TemporaryDC, ImageID(ImageNumber), 0, TemporaryBitmap\bmHeight, Memory, TemporaryBitmapInfo, #DIB_RGB_COLORS)
 
  DeleteDC_(TemporaryDC)
 
EndProcedure 

Procedure CopyMemoryToImage(Memory, ImageNumber)
 
  Protected TemporaryDC, TemporaryBitmap.BITMAP, TemporaryBitmapInfo.BITMAPINFO
 
  TemporaryDC = CreateDC_("DISPLAY", #Null, #Null, #Null)
 
  GetObject_(ImageID(ImageNumber), SizeOf(BITMAP), TemporaryBitmap.BITMAP)
 
  TemporaryBitmapInfo\bmiHeader\biSize        = SizeOf(BITMAPINFOHEADER)
  TemporaryBitmapInfo\bmiHeader\biWidth       = TemporaryBitmap\bmWidth
  TemporaryBitmapInfo\bmiHeader\biHeight      = -TemporaryBitmap\bmHeight
  TemporaryBitmapInfo\bmiHeader\biPlanes      = 1
  TemporaryBitmapInfo\bmiHeader\biBitCount    = 32
  TemporaryBitmapInfo\bmiHeader\biCompression = #BI_RGB
 
  SetDIBits_(TemporaryDC, ImageID(ImageNumber), 0, TemporaryBitmap\bmHeight, Memory, TemporaryBitmapInfo, #DIB_RGB_COLORS)
 
  DeleteDC_(TemporaryDC)
 
EndProcedure
Sorry to bump an old topic but is there any way to convert these two functions for Mac OSX? :?:
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Work on image in memory

Post by Keya »

here is an example of reading every pixel in an image, in memory, no Point()/Plot(), all OS:

Code: Select all

Structure RGB
  r.a
  g.a
  b.a
EndStructure

Procedure ReadImage(hImg)
  Protected *imgbuf, *pixel.RGB, Pixbytes, Rowbytes, x,y, width, height
  If StartDrawing(ImageOutput(hImg))
    width = ImageWidth(hImg)
    height = ImageHeight(hImg)
    Pixbytes = ImageDepth(hImg)/8
    Rowbytes = DrawingBufferPitch()
    *imgbuf = DrawingBuffer()
    For y = 0 To height-1
      *pixel = *imgbuf + (y * Rowbytes)
      For x = 0 To width-1      
        Debug "RGB = " + Hex(*pixel\r)+" "+Hex(*pixel\g)+" "+Hex(*pixel\b)  ;RGB on Linux/Mac, BGR on Windows      
        *pixel + Pixbytes
      Next x
    Next y    
    StopDrawing()  
  EndIf
EndProcedure

UsePNGImageDecoder(): UseJPEGImageDecoder()
Define hImg = LoadImage(#PB_Any,"c:\test.jpg")
If hImg = 0: MessageRequester("Error","Couldnt load image"): End: EndIf
ReadImage(hImg)
coder14
Enthusiast
Enthusiast
Posts: 327
Joined: Tue Jun 21, 2011 10:39 am

Re: Work on image in memory

Post by coder14 »

Keya wrote:here is an example of reading every pixel in an image, in memory, no Point()/Plot(), all OS:

Code: Select all

Structure RGB
  r.a
  g.a
  b.a
EndStructure

Procedure ReadImage(hImg)
  Protected *imgbuf, *pixel.RGB, Pixbytes, Rowbytes, x,y, width, height
  If StartDrawing(ImageOutput(hImg))
    width = ImageWidth(hImg)
    height = ImageHeight(hImg)
    Pixbytes = ImageDepth(hImg)/8
    Rowbytes = DrawingBufferPitch()
    *imgbuf = DrawingBuffer()
    For y = 0 To height-1
      *pixel = *imgbuf + (y * Rowbytes)
      For x = 0 To width-1      
        Debug "RGB = " + Hex(*pixel\r)+" "+Hex(*pixel\g)+" "+Hex(*pixel\b)  ;RGB on Linux/Mac, BGR on Windows      
        *pixel + Pixbytes
      Next x
    Next y    
    StopDrawing()  
  EndIf
EndProcedure

UsePNGImageDecoder(): UseJPEGImageDecoder()
Define hImg = LoadImage(#PB_Any,"c:\test.jpg")
If hImg = 0: MessageRequester("Error","Couldnt load image"): End: EndIf
ReadImage(hImg)
Which function is that? :?
Post Reply