Page 1 of 1

Floyd–Steinberg Not Working Quite Right....

Posted: Wed Jul 15, 2020 12:43 pm
by em_uk
Hello guys and gals.

I've converted this processing example to PB, but for the life of me cant figure out why it wont work as expected.

Code: Select all


UseJPEGImageDecoder()


ExamineDesktops():InitSprite():InitKeyboard()
OpenWindow(0,DesktopWidth(0)/2-400,DesktopHeight(0)/2-300,800,600,"Demo")
OpenWindowedScreen(WindowID(0),0,0,800,600)
Global image = LoadImage(#PB_Any,"kitten.jpg")
Global copimg = CopyImage(image,#PB_Any)
Global Dim image.i(ImageWidth(image),ImageHeight(image))


Procedure ImageToArray()
  fact.i = 1
  ; fill array
  
  StartDrawing(ImageOutput(image))
  For y = 0 To ImageHeight(image)-2
    For x = 1 To ImageWidth(image)-2
      image(x,y) = Point(x,y)
    Next 
  Next 
  StopDrawing()
  
  StartDrawing(ImageOutput(copimg))
  For y = 0 To ImageHeight(copimg)-2
    For x = 1 To ImageWidth(copimg)-2
      
      col.i = image(x,y)
      
      oldr.f = Red (col) 
      oldg.f = Green (col) 
      oldb.f = Blue (col) 
      
      newr.i = Round(fact * oldr / 255,#PB_Round_Nearest   )*(255/fact)
      newg.i = Round(fact * oldg / 255,#PB_Round_Nearest   )*(255/fact)
      newb.i = Round(fact * oldb / 255,#PB_Round_Nearest   )*(255/fact)

      image(x,y)=RGB(newr,newg,newb)
      
      errr.f = oldr - newr
      errg.f = oldg - newg
      errb.f = oldb - newb
      
      c.i = image(x+1, y )
      r.f = Red(c)
      g.f = Green(c)
      b.f = Blue(c)
      r = r + errr * 7/ 16.0
      g = g + errg * 7/ 16.0
      b = b + errb * 7/ 16.0
      image(x+1,y) = RGB(r,g,b)

      c = image(x-1,y+1)
      r = Red(c)
      g = Green(c)
      b = Blue(c)
      r = r + errr * 3/ 16.0
      g = g + errg * 3/ 16.0
      b = b + errb * 3/ 16.0
      image(x-1,y+1) = RGB(r,g,b)
      
      c = image(x,y+1)
      r = Red(c)
      g = Green(c)
      b = Blue(c)
      r = r + errr * 5/ 16.0
      g = g + errg * 5/ 16.0
      b = b + errb * 5/ 16.0
      image(x,y+1) = RGB(r,g,b)
      
      c = image(x+1, y+1 )
      r = Red(c)
      g = Green(c)
      b = Blue(c)
      r = r + errr * 1/ 16.0
      g = g + errg * 1/ 16.0
      b = b + errb * 1/ 16.0
      image(x+1,y+1) = RGB(r,g,b)
      
    Next 
  Next 

  StopDrawing()
  
EndProcedure

Procedure UpdateImage()
  
  StartDrawing(ImageOutput(copimg))
  
  For y = 0 To ImageHeight(copimg)-1
    For x=0 To ImageWidth(copimg)-1
      Plot(x,y,image(x,y))
    Next
  Next 
  StopDrawing()
  
EndProcedure


Procedure UpdateScreen()
  
  ClearScreen(0)
  ; draws the copy image 
  StartDrawing(ScreenOutput())
  DrawImage(ImageID(copimg),0,0)
  StopDrawing()
  FlipBuffers()
  Delay(1)
  
  
EndProcedure


ImageToArray()
UpdateImage()

Repeat 
  
  event=WaitWindowEvent()
  
  ExamineKeyboard()
  
  UpdateScreen()
    
  
Until KeyboardPushed(#PB_Key_Escape)



Here is the original : https://github.com/CodingTrain/website/ ... hering.pde

The output should be dithered like so (ignore the greyscale) :

Image

Here is what I get

Image

Link for the kitten image https://github.com/CodingTrain/website/ ... kitten.jpg

Cheers!

Re: Floyd–Steinberg Not Working Quite Right....

Posted: Wed Jul 15, 2020 12:53 pm
by Derren
Did you try greyscale? The bright pink and bright cyan spots might come from an overflow or underrun that is not apparent in greyscale (and assuming the original algorithm is flawless and no checks to prevent this are in place, should and could not possibly be an issue in greyscale)
Did you check that the algorithm can be used for 24bit images without alterations?

Re: Floyd–Steinberg Not Working Quite Right....

Posted: Wed Jul 15, 2020 1:15 pm
by em_uk
That is a good point, however you can ignore the grey scale option and it should turn out like this :

Image

Re: Floyd–Steinberg Not Working Quite Right....

Posted: Wed Jul 15, 2020 1:37 pm
by wilbert
When you set a color r, g and b should be in range [0, 255].
In your code they can be both < 0 and > 255.
You have to limit them to make it function properly.

By the way, here's another dither example but it creates a B/W image instead of color
viewtopic.php?p=413137#p413137

Re: Floyd–Steinberg Not Working Quite Right....

Posted: Wed Jul 15, 2020 2:58 pm
by infratec

Code: Select all

EnableExplicit


Procedure.i DitherImage(Image.i, Factor.i=1)
  
  Protected.l pix, c
  Protected.i NewImg, x, y, ImgHeight, ImgWidth
  Protected.f oldR, oldG, oldB, errR, errG, errB, newR, newG, newB, r, g, b
  
  If IsImage(Image)
    
    NewImg = CopyImage(image, #PB_Any)
    If NewImg
      
      ; fill array
      If StartDrawing(ImageOutput(NewImg))
        
        ImgHeight = ImageHeight(NewImg) - 2
        ImgWidth = ImageWidth(NewImg) - 2
        
        For y = 0 To ImgHeight
          For x = 1 To ImgWidth
            
            pix = Point(x,y)
            
            oldR = Red(pix)
            oldG = Green(pix)
            oldB = Blue(pix)
            
            newR = Round(oldr * factor / 255.0, #PB_Round_Nearest) * (255.0 / factor)
            newG = Round(oldg * factor / 255.0, #PB_Round_Nearest) * (255.0 / factor)
            newB = Round(oldb * factor / 255.0, #PB_Round_Nearest) * (255.0 / factor)
            
            Plot(x, y, RGB(newR, newG, newB))
            
            errR = oldR - newR
            errG = oldG - newG
            errB = oldB - newB
            
            c = Point(x + 1, y)
            r = Red(c)
            g = Green(c)
            b = Blue(c)
            r = r + errR * 7.0 / 16.0
            g = g + errG * 7.0 / 16.0
            b = b + errB * 7.0 / 16.0
            If r < 0 : r = 0 : ElseIf r > 255 : r = 255 : EndIf
            If g < 0 : g = 0 : ElseIf g > 255 : g = 255 : EndIf
            If b < 0 : b = 0 : ElseIf b > 255 : b = 255 : EndIf
            Plot(x + 1, y, RGB(r, g, b))
            
            c = Point(x - 1, y + 1)
            r = Red(c)
            g = Green(c)
            b = Blue(c)
            r = r + errR * 3.0 / 16.0
            g = g + errG * 3.0 / 16.0
            b = b + errB * 3.0 / 16.0
            If r < 0 : r = 0 : ElseIf r > 255 : r = 255 : EndIf
            If g < 0 : g = 0 : ElseIf g > 255 : g = 255 : EndIf
            If b < 0 : b = 0 : ElseIf b > 255 : b = 255 : EndIf
            Plot(x - 1, y + 1, RGB(r, g, b))
            
            c = Point(x, y + 1)
            r = Red(c)
            g = Green(c)
            b = Blue(c)
            r = r + errr * 5.0 / 16.0
            g = g + errg * 5.0 / 16.0
            b = b + errb * 5.0 / 16.0
            If r < 0 : r = 0 : ElseIf r > 255 : r = 255 : EndIf
            If g < 0 : g = 0 : ElseIf g > 255 : g = 255 : EndIf
            If b < 0 : b = 0 : ElseIf b > 255 : b = 255 : EndIf
            Plot(x, y + 1, RGB(r, g, b))
            
            c = Point(x + 1, y + 1)
            r = Red(c)
            g = Green(c)
            b = Blue(c)
            r = r + errr * 1.0 / 16.0
            g = g + errg * 1.0 / 16.0
            b = b + errb * 1.0 / 16.0
            If r < 0 : r = 0 : ElseIf r > 255 : r = 255 : EndIf
            If g < 0 : g = 0 : ElseIf g > 255 : g = 255 : EndIf
            If b < 0 : b = 0 : ElseIf b > 255 : b = 255 : EndIf
            Plot(x + 1, y + 1, RGB(r, g, b))
            
          Next
        Next
        
        StopDrawing()
      EndIf
      
    EndIf
  EndIf
  
  ProcedureReturn NewImg
  
EndProcedure


Define.i Image, DitheredImg

UseJPEGImageDecoder()

Image = LoadImage(#PB_Any, "kitten.jpg")

OpenWindow(0, 0, 0, ImageWidth(image) * 2, ImageHeight(image), "Demo", #PB_Window_ScreenCentered|#PB_Window_MinimizeGadget)
ImageGadget(0, 0, 0, 0, 0, ImageID(image))
ImageGadget(1, ImageWidth(image), 0, 0, 0, 0)

DitheredImg = DitherImage(image)
If DitheredImg
  SetGadgetState(1, ImageID(DitheredImg))
EndIf

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow

Re: Floyd–Steinberg Not Working Quite Right....

Posted: Wed Jul 15, 2020 6:03 pm
by em_uk
Thanks guys

Interesting as the types were specifically stated to be floats or ints in processing. I am trying to figure out why processing wants it one way and PB wants it another.

The source video was from here : https://www.youtube.com/watch?v=0L2n8Tg2FwI

Processing must be doing something extra when values are being passed from one type to another.

Re: Floyd–Steinberg Not Working Quite Right....

Posted: Wed Jul 15, 2020 6:21 pm
by infratec
The only question is:

What does the original Color() function?

May be it eliminates all 'illegal' values itself.

If you write you own Color() procdure with the value checks inside the code is identical.

Re: Floyd–Steinberg Not Working Quite Right....

Posted: Wed Jul 15, 2020 6:51 pm
by wilbert
Infratec is right.
PureBasic does not limit the input range but there are other programming languages which do limit the input range from 0 - 255 for integer values (or from 0.0 - 1.0 when r, g and b are expressed as floating point values).

Here's your original code with a color function that limits the input range.

Code: Select all

UseJPEGImageDecoder()


ExamineDesktops():InitSprite():InitKeyboard()
OpenWindow(0,DesktopWidth(0)/2-400,DesktopHeight(0)/2-300,800,600,"Demo")
OpenWindowedScreen(WindowID(0),0,0,800,600)
Global image = LoadImage(#PB_Any,"kitten.jpg")
Global copimg = CopyImage(image,#PB_Any)
Global Dim image.i(ImageWidth(image),ImageHeight(image))

Procedure Color(R, G, B)
  !movd xmm0, [p.v_R]
  !movd xmm1, [p.v_G]
  !movd xmm2, [p.v_B]
  !pxor xmm3, xmm3
  !punpckldq xmm0, xmm1
  !punpckldq xmm2, xmm3
  !punpcklqdq xmm0, xmm2
  !packssdw xmm0, xmm0
  !packuswb xmm0, xmm0
  !movd eax, xmm0
  ProcedureReturn
EndProcedure

Procedure ImageToArray()
  fact.i = 1
  ; fill array
 
  StartDrawing(ImageOutput(image))
  For y = 0 To ImageHeight(image)-2
    For x = 1 To ImageWidth(image)-2
      image(x,y) = Point(x,y)
    Next
  Next
  StopDrawing()
 
  StartDrawing(ImageOutput(copimg))
  For y = 0 To ImageHeight(copimg)-2
    For x = 1 To ImageWidth(copimg)-2
     
      col.i = image(x,y)
     
      oldr.f = Red (col)
      oldg.f = Green (col)
      oldb.f = Blue (col)
     
      newr.i = Round(fact * oldr / 255,#PB_Round_Nearest   )*(255/fact)
      newg.i = Round(fact * oldg / 255,#PB_Round_Nearest   )*(255/fact)
      newb.i = Round(fact * oldb / 255,#PB_Round_Nearest   )*(255/fact)

      image(x,y)=Color(newr,newg,newb)
     
      errr.f = oldr - newr
      errg.f = oldg - newg
      errb.f = oldb - newb
     
      c.i = image(x+1, y )
      r.f = Red(c)
      g.f = Green(c)
      b.f = Blue(c)
      r = r + errr * 7/ 16.0
      g = g + errg * 7/ 16.0
      b = b + errb * 7/ 16.0
      image(x+1,y) = Color(r,g,b)

      c = image(x-1,y+1)
      r = Red(c)
      g = Green(c)
      b = Blue(c)
      r = r + errr * 3/ 16.0
      g = g + errg * 3/ 16.0
      b = b + errb * 3/ 16.0
      image(x-1,y+1) = Color(r,g,b)
     
      c = image(x,y+1)
      r = Red(c)
      g = Green(c)
      b = Blue(c)
      r = r + errr * 5/ 16.0
      g = g + errg * 5/ 16.0
      b = b + errb * 5/ 16.0
      image(x,y+1) = Color(r,g,b)
     
      c = image(x+1, y+1 )
      r = Red(c)
      g = Green(c)
      b = Blue(c)
      r = r + errr * 1/ 16.0
      g = g + errg * 1/ 16.0
      b = b + errb * 1/ 16.0
      image(x+1,y+1) = Color(r,g,b)
     
    Next
  Next

  StopDrawing()
 
EndProcedure

Procedure UpdateImage()
 
  StartDrawing(ImageOutput(copimg))
 
  For y = 0 To ImageHeight(copimg)-1
    For x=0 To ImageWidth(copimg)-1
      Plot(x,y,image(x,y))
    Next
  Next
  StopDrawing()
 
EndProcedure


Procedure UpdateScreen()
 
  ClearScreen(0)
  ; draws the copy image
  StartDrawing(ScreenOutput())
  DrawImage(ImageID(copimg),0,0)
  StopDrawing()
  FlipBuffers()
  Delay(1)
 
 
EndProcedure


ImageToArray()
UpdateImage()

Repeat
 
  event=WaitWindowEvent()
 
  ExamineKeyboard()
 
  UpdateScreen()
   
 
Until KeyboardPushed(#PB_Key_Escape)

Re: Floyd–Steinberg Not Working Quite Right....

Posted: Wed Jul 15, 2020 7:12 pm
by em_uk
Excellent improvement wilbert, with added asm. Lovely.

Thanks again - this is something I will consider in future as its not something immediately apparent, to me anyway :)