Floating point pixel module

Share your advanced PureBasic knowledge/code with the community.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Floating point pixel module

Post by wilbert »

f4 is a cross platform module to convert (part of) an image to a floating point pixel array and back.
Each pixel consists of 4 floats (rgba) with values ranging [0, 1].
For large images, it is recommended to process an image as multiple smaller blocks because each pixel takes up 16 bytes of memory.

Code: Select all

; f4 module by Wilbert

; Supported platforms : All (Cross platform)

; CPU requirements    : SSE2 support

; Last change         : May 19, 2016


;- *** Module declaration ***

DeclareModule f4
  
  Structure rgba_f
    r.f
    g.f
    b.f
    a.f
  EndStructure
  
  Structure f4
    StructureUnion
      f.f[4]
      c.rgba_f
    EndStructureUnion
  EndStructure
  
  ; pxGet and pxSet
  ; should be called inside StartDrawing() / StopDrawing() block
  Declare pxGet(x, y, width, height, *px_f4)
  Declare pxSet(x, y, width, height, *px_f4)
  
  ; pxGetImage and pxSetImage
  ; should be called outside of StartDrawing() / StopDrawing() block
  Declare pxGetImage(Image, *px_f4)
  Declare pxSetImage(Image, *px_f4)
  
  ; low level conversion procedures
  Declare rgba2float(*px, *px_f4, num_px.l)
  Declare bgra2float(*px, *px_f4, num_px.l) 
  Declare float2rgba(*px_f4, *px, num_px.l)
  Declare float2bgra(*px_f4, *px, num_px.l)
  Declare rgb2float(*px, *px_f4, num_px.l)
  Declare bgr2float(*px, *px_f4, num_px.l) 
  Declare float2rgb(*px_f4, *px, num_px.l)
  Declare float2bgr(*px_f4, *px, num_px.l)
  
EndDeclareModule

;- *** Module implementation ***
Module f4
  
  DisableDebugger
  EnableExplicit
  EnableASM
  
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    Macro rax:eax:EndMacro
    Macro rdx:edx:EndMacro
  CompilerEndIf
  
  Macro M_mem2xmm(arg1, arg2)
    !movups arg1, [arg2]
  EndMacro
  
  Macro M_xmm2mem(arg1, arg2)
    !movups [arg1], arg2
  EndMacro
  
  ;- ** rgb(a) to f4 conversion **
  
  Macro M_rgb2float(bgr = 0, st = 3)
    ; init xmm2, xmm3 and xmm4
    !mov eax, 0x4b004b00
    !mov ecx, 0x37800080
    !pxor xmm1, xmm1
    !movd xmm2, eax
    !movd xmm4, ecx
    !pshufd xmm2, xmm2, 0
    !pshufd xmm3, xmm2, 0
    !pshufd xmm4, xmm4, 0
    !pslld xmm3, 16
    ; load procedure arguments
    !mov ecx, [p.v_num_px]
    mov rdx, *px_f4
    mov rax, *px
    CompilerIf st = 3
      !pcmpeqd xmm5, xmm5
      !pslld xmm5, 24
    CompilerEndIf
    ; check num_px
    !sub ecx, 1
    !jz f4.rgb2float#bgr#st#_l1
    !jl f4.rgb2float#bgr#st#_l2
    
    ; handle two pixels
    !f4.rgb2float#bgr#st#_l0:
    CompilerIf st = 3
      movd xmm0, [rax]
      movd xmm1, [rax + 2]
      !psllq xmm1, 24
      !por xmm0, xmm1
      !por xmm0, xmm5
    CompilerElse
      movq xmm0, [rax]  
    CompilerEndIf
    !punpcklbw xmm0, xmm0
    CompilerIf bgr
      !pshufhw xmm1, xmm0, 11000110b
      !pshuflw xmm0, xmm0, 11000110b
    CompilerElse  
      !pshufhw xmm1, xmm0, 11100100b
    CompilerEndIf
    !punpcklwd xmm0, xmm2
    !punpckhwd xmm1, xmm2
    !subps xmm0, xmm3
    !subps xmm1, xmm3
    !mulps xmm0, xmm4
    !mulps xmm1, xmm4
    M_xmm2mem(rdx, xmm0)
    M_xmm2mem(rdx + 16, xmm1)
    add rax, st * 2
    add rdx, 32
    !sub ecx, 2
    !ja f4.rgb2float#bgr#st#_l0
    !jc f4.rgb2float#bgr#st#_l2
    
    ; handle single pixel
    !f4.rgb2float#bgr#st#_l1:    
    CompilerIf st = 3
      movzx ecx, word [rax + 1]
      !shl ecx, 8
      mov cl, [rax]
      !movd xmm0, ecx
      !por xmm0, xmm5
    CompilerElse
      movd xmm0, [rax]
    CompilerEndIf
    !punpcklbw xmm0, xmm0
    CompilerIf bgr
      !pshuflw xmm0, xmm0, 11000110b
    CompilerEndIf
    !punpcklwd xmm0, xmm2
    !subps xmm0, xmm3
    !mulps xmm0, xmm4
    M_xmm2mem(rdx, xmm0)
    
    !f4.rgb2float#bgr#st#_l2:
  EndMacro
  
  Procedure rgb2float(*px, *px_f4, num_px.l)
    M_rgb2float(0, 3) 
  EndProcedure
  
  Procedure bgr2float(*px, *px_f4, num_px.l)
    M_rgb2float(1, 3) 
  EndProcedure
  
  Procedure rgba2float(*px, *px_f4, num_px.l)
    M_rgb2float(0, 4) 
  EndProcedure
  
  Procedure bgra2float(*px, *px_f4, num_px.l)
    M_rgb2float(1, 4) 
  EndProcedure
  
  ;- ** f4 to rgb(a) conversion **
  
  Macro M_float2rgb(bgr = 0, st = 3)
    ; init xmm2
    !mov eax, 0x437f0000
    !movd xmm2, eax
    !pshufd xmm2, xmm2, 0
    ; load procedure arguments
    !mov ecx, [p.v_num_px]
    mov rax, *px_f4
    mov rdx, *px
    ; check num_px
    !sub ecx, 1
    !jz f4.float2rgb#bgr#st#_l1
    !jl f4.float2rgb#bgr#st#_l2
    
    ; handle two pixels
    !f4.float2rgb#bgr#st#_l0:
    M_mem2xmm(xmm0, rax)
    M_mem2xmm(xmm1, rax + 16)
    !mulps xmm0, xmm2
    !mulps xmm1, xmm2
    !cvtps2dq xmm0, xmm0
    !cvtps2dq xmm1, xmm1
    CompilerIf bgr
      CompilerIf st = 3
        !pshufd xmm0, xmm0, 00000110b
      CompilerElse
        !pshufd xmm0, xmm0, 11000110b
      CompilerEndIf
      !pshufd xmm1, xmm1, 11000110b
    CompilerElseIf st = 3
      !pshufd xmm0, xmm0, 10100100b
    CompilerEndIf
    !packssdw xmm0, xmm1
    !packuswb xmm0, xmm0
    CompilerIf st = 3
      movd [rdx], xmm0
      !psrlq xmm0, 24
      movd [rdx + 2], xmm0
    CompilerElse
      movq [rdx], xmm0
    CompilerEndIf
    add rax, 32
    add rdx, st * 2
    !sub ecx, 2
    !ja f4.float2rgb#bgr#st#_l0
    !jc f4.float2rgb#bgr#st#_l2
    
    ; handle single pixel
    !f4.float2rgb#bgr#st#_l1:
    M_mem2xmm(xmm0, rax)
    !mulps xmm0, xmm2
    !cvtps2dq xmm0, xmm0
    CompilerIf bgr
      !pshufd xmm0, xmm0, 11000110b
    CompilerEndIf
    !packssdw xmm0, xmm0
    !packuswb xmm0, xmm0
    CompilerIf st = 3
      !movd ecx, xmm0
      mov [rdx], cx
      !shr ecx, 16
      mov [rdx + 2], cl
    CompilerElse
      movd [rdx], xmm0
    CompilerEndIf
    
    !f4.float2rgb#bgr#st#_l2:
  EndMacro
  
  Procedure float2rgb(*px_f4, *px, num_px.l)
    M_float2rgb(0, 3) 
  EndProcedure
  
  Procedure float2bgr(*px_f4, *px, num_px.l)
    M_float2rgb(1, 3) 
  EndProcedure
  
  Procedure float2rgba(*px_f4, *px, num_px.l)
    M_float2rgb(0, 4) 
  EndProcedure
  
  Procedure float2bgra(*px_f4, *px, num_px.l)
    M_float2rgb(1, 4) 
  EndProcedure
  
  DisableASM
  Prototype _conv_proc(*src, *dst, num_px.l)
  
  ;- ** pxGet & pxSet **
  
  Procedure pxGet(x, y, width, height, *px_f4)
    
    Protected conv_proc._conv_proc
    Protected.i db, dp, pb, pf, w, h, px_f4_step
    
    w = OutputWidth() : h = OutputHeight()
    db = DrawingBuffer() : dp = DrawingBufferPitch()
    pb = OutputDepth() >> 3 : pf = DrawingBufferPixelFormat()
    Select pf & ~#PB_PixelFormat_ReversedY
      Case #PB_PixelFormat_24Bits_BGR
        conv_proc = @bgr2float()
      Case #PB_PixelFormat_32Bits_BGR
        conv_proc = @bgra2float()
      Case #PB_PixelFormat_24Bits_RGB
        conv_proc = @rgb2float()
      Default
        conv_proc = @rgba2float()
    EndSelect
    If pf & #PB_PixelFormat_ReversedY
      db + dp * h - dp
      dp = -dp
    EndIf
    
    px_f4_step = width << 4
    If x < 0 : *px_f4 - x << 4 : width + x : EndIf
    If y < 0 : *px_f4 - y * px_f4_step : height + y : EndIf
    If x > 0 : db + x * pb : w - x : EndIf
    If y > 0 : db + y * dp : h - y : EndIf
    If width > w : width = w : EndIf
    If height > h : height = h : EndIf
    
    If width > 0
      While height > 0
        conv_proc(db, *px_f4, width)
        db + dp
        *px_f4 + px_f4_step
        height - 1
      Wend
    EndIf
    
  EndProcedure
  
  Procedure pxSet(x, y, width, height, *px_f4)
    
    Protected conv_proc._conv_proc
    Protected.i db, dp, pb, pf, w, h, px_f4_step
    
    w = OutputWidth() : h = OutputHeight()
    db = DrawingBuffer() : dp = DrawingBufferPitch()
    pb = OutputDepth() >> 3 : pf = DrawingBufferPixelFormat()
    Select pf & ~#PB_PixelFormat_ReversedY
      Case #PB_PixelFormat_24Bits_BGR
        conv_proc = @float2bgr()
      Case #PB_PixelFormat_32Bits_BGR
        conv_proc = @float2bgra()
      Case #PB_PixelFormat_24Bits_RGB
        conv_proc = @float2rgb()
      Default
        conv_proc = @float2rgba()
    EndSelect
    If pf & #PB_PixelFormat_ReversedY
      db + dp * h - dp
      dp = -dp
    EndIf
    
    px_f4_step = width << 4
    If x < 0 : *px_f4 - x << 4 : width + x : EndIf
    If y < 0 : *px_f4 - y * px_f4_step : height + y : EndIf
    If x > 0 : db + x * pb : w - x : EndIf
    If y > 0 : db + y * dp : h - y : EndIf
    If width > w : width = w : EndIf
    If height > h : height = h : EndIf
    
    If width > 0
      While height > 0
        conv_proc(*px_f4, db, width)
        db + dp
        *px_f4 + px_f4_step
        height - 1
      Wend
    EndIf
    
  EndProcedure
  
  ;- ** pxGetImage & pxSetImage **
  
  Procedure pxGetImage(Image, *px_f4)
    If IsImage(Image) And StartDrawing(ImageOutput(Image))
      pxGet(0, 0, OutputWidth(), OutputHeight(), *px_f4)
      StopDrawing()
    EndIf 
  EndProcedure
  
  Procedure pxSetImage(Image, *px_f4)
    If IsImage(Image) And StartDrawing(ImageOutput(Image))
      pxSet(0, 0, OutputWidth(), OutputHeight(), *px_f4)
      StopDrawing()
    EndIf 
  EndProcedure
  
EndModule
Last edited by wilbert on Thu May 19, 2016 10:07 am, edited 1 time in total.
Windows (x64)
Raspberry Pi OS (Arm64)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Floating point pixel module

Post by wilbert »

A few examples

Code: Select all

XIncludeFile "f4module.pbi"

; dim first rows, then columns
Dim img.f4::f4(199, 299)

For y = 0 To 199
  For x = 0 To 299
    
    img(y, x)\c\r = Sin(#PI * x / 300)
    img(y, x)\c\g = Cos(2 * #PI * y / 200)
    img(y, x)\c\b = Cos(2 * #PI * x / 300)
    img(y, x)\c\a = 1
    
  Next
Next

OpenWindow(0, 0, 0, 320, 220, "f4 module test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CanvasGadget(0, 10, 10, 300, 200)

StartDrawing(CanvasOutput(0))
f4::pxSet(0, 0, 300, 200, @img())
StopDrawing()

Repeat
  Event = WaitWindowEvent() 
Until Event = #PB_Event_CloseWindow

Code: Select all

XIncludeFile "f4module.pbi"

UseJPEGImageDecoder()

LoadImage(0, "myImage.jpg")

; dim first rows, then columns
h = ImageHeight(0)
w = ImageWidth(0)
max_y = h - 1
max_x = w - 1
Dim img.f4::f4(max_y, max_x)

; get pixels
f4::pxGetImage(0, @img())

; increase contrast
For y = 0 To max_y
  For x = 0 To max_x
    
    img(y, x)\c\r * 2.0 - 0.5
    img(y, x)\c\g * 2.0 - 0.5
    img(y, x)\c\b * 2.0 - 0.5
    
  Next
Next

; set pixels
f4::pxSetImage(0, @img())

; show result
OpenWindow(0, 0, 0, w, h, "f4 module test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ImageGadget(0, 0, 0, w, h, ImageID(0))

Repeat
  Event = WaitWindowEvent() 
Until Event = #PB_Event_CloseWindow

Code: Select all

XIncludeFile "f4module.pbi"

UseJPEGImageDecoder()

LoadImage(0, "myImage.jpg")

; dim first rows, then columns
h = ImageHeight(0)
w = ImageWidth(0)
max_y = h - 1
max_x = w - 1
Dim img.f4::f4(max_y, max_x)

; get pixels
f4::pxGetImage(0, @img())

; convert to sepia
For y = 0 To max_y
  For x = 0 To max_x
    
    r.f = img(y, x)\c\r
    g.f = img(y, x)\c\g
    b.f = img(y, x)\c\b
    img(y, x)\c\r = r * 0.393 + g * 0.769 + b * 0.189
    img(y, x)\c\g = r * 0.349 + g * 0.686 + b * 0.168
    img(y, x)\c\b = r * 0.272 + g * 0.534 + b * 0.131
    
  Next
Next

; set pixels
f4::pxSetImage(0, @img())

; show result
OpenWindow(0, 0, 0, w, h, "f4 module test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ImageGadget(0, 0, 0, w, h, ImageID(0))

Repeat
  Event = WaitWindowEvent() 
Until Event = #PB_Event_CloseWindow

Code: Select all

XIncludeFile "f4module.pbi"

UseJPEGImageDecoder()

; dim first rows, then columns
Dim img.f4::f4(199, 299)

LoadImage(0, "myImage.jpg")

StartDrawing(ImageOutput(0))
f4::pxGet(0, 0, 300, 200, @img())
For y = 0 To 199
  For x = 0 To 299
    
    r.f = img(y, x)\c\r
    g.f = img(y, x)\c\g
    b.f = img(y, x)\c\b
    img(y, x)\c\r = r * 0.393 + g * 0.769 + b * 0.189
    img(y, x)\c\g = r * 0.349 + g * 0.686 + b * 0.168
    img(y, x)\c\b = r * 0.272 + g * 0.534 + b * 0.131
    
  Next
Next
f4::pxSet(0, 0, 300, 200, @img())
StopDrawing()

OpenWindow(0, 0, 0, 300, 200, "f4 module test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ImageGadget(0, 0, 0, 300, 200, ImageID(0))

Repeat
  Event = WaitWindowEvent() 
Until Event = #PB_Event_CloseWindow
Sepia conversion functions are based on a code example from JHPJHP.
Windows (x64)
Raspberry Pi OS (Arm64)
Mesa
Enthusiast
Enthusiast
Posts: 345
Joined: Fri Feb 24, 2012 10:19 am

Re: Floating point pixel module

Post by Mesa »

Hi, I have not found on internet an algorithm to recover the original colors of old photos when all the colors have turned into red.
Image

The following code improves well reddened pictures but do you have a better algorithm to get a better result?

Code: Select all

XIncludeFile "f4module.pbi" 

UseJPEGImageDecoder()

LoadImage(0, "mypicture.jpg") 

; dim first rows, then columns 
Dim img.f4::f4(199, 299) 

h = ImageHeight(0)
w = ImageWidth(0)
max_y = h - 1
max_x = w - 1




StartDrawing(ImageOutput(0)) 
f4::pxGet(0, 0, 300, 200, @img()) 
For y = 0 To 199 
  For x = 0 To 299 
    
    r.f = img(y, x)\c\r 
    g.f = img(y, x)\c\g 
    b.f = img(y, x)\c\b 
    
    ;Which algorythm to remove red color on an old photo ?
    img(y, x)\c\r = r * 0.393 + g * 0.769 + b * 0.189 
    img(y, x)\c\g = g 
    img(y, x)\c\b = b 
    
  Next 
Next 
f4::pxSet(0, 0, 300, 200, @img()) 
StopDrawing() 

; show result
OpenWindow(0, 0, 0, w, h, "f4 module test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ScrollAreaGadget(1, 0, 0, w,h, w+1000, h+1000, 30)

ImageGadget(0, 0, 0, w, h, ImageID(0))
CloseGadgetList() 

Repeat
  Event = WaitWindowEvent() 
Until Event = #PB_Event_CloseWindow

Thanx.

M.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Floating point pixel module

Post by wilbert »

Mesa wrote:Hi, I have not found on internet an algorithm to recover the original colors of old photos when all the colors have turned into red.
The following code improves well reddened pictures but do you have a better algorithm to get a better result?
That looks nice Mesa.
I wish I had a good answer but unfortunately I don't.
You probably need user interaction (click on a part that needs to be gray so you can detect the amount of red shift) or analyze the image data first before doing a correction.
Did the place where you found the current algorithm provide any more information ?
Windows (x64)
Raspberry Pi OS (Arm64)
JHPJHP
Addict
Addict
Posts: 2129
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: Floating point pixel module

Post by JHPJHP »

Hi Mesa,

Have you tried PureBasic Interface to OpenCV?
- building on the results returned by wilbert's algorithm

You can write your own filter, or try one of the existing examples.
- an example that comes to mind: cv_add_subtract.pb

Visually is shows the words Add / Subtract, but with the saved image the captions are excluded.
- press the Spacebar to toggle between Add / Subtract
- save the image using the context menu
User avatar
VB6_to_PBx
Enthusiast
Enthusiast
Posts: 617
Joined: Mon May 09, 2011 9:36 am

Re: Floating point pixel module

Post by VB6_to_PBx »

Image

the old Picture enhancement software = Microsoft Digital Image Pro 10
does a pretty good job with just 1-click enhancement

i wish i knew how they did it ?

Image
 
PureBasic .... making tiny electrons do what you want !

"With every mistake we must surely be learning" - George Harrison
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Floating point pixel module

Post by wilbert »

Maybe some sort of Auto White Balance algorithm.
Windows (x64)
Raspberry Pi OS (Arm64)
Post Reply