Floyd–Steinberg Not Working Quite Right....

Just starting out? Need help? Post your questions and find answers here.
User avatar
em_uk
Enthusiast
Enthusiast
Posts: 366
Joined: Sun Aug 08, 2010 3:32 pm
Location: Manchester UK

Floyd–Steinberg Not Working Quite Right....

Post 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!
----

R Tape loading error, 0:1
User avatar
Derren
Enthusiast
Enthusiast
Posts: 313
Joined: Sat Jul 23, 2011 1:13 am
Location: Germany

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

Post 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?
User avatar
em_uk
Enthusiast
Enthusiast
Posts: 366
Joined: Sun Aug 08, 2010 3:32 pm
Location: Manchester UK

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

Post by em_uk »

That is a good point, however you can ignore the grey scale option and it should turn out like this :

Image
----

R Tape loading error, 0:1
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

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

Post 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
Windows (x64)
Raspberry Pi OS (Arm64)
infratec
Always Here
Always Here
Posts: 6869
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

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

Post 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
User avatar
em_uk
Enthusiast
Enthusiast
Posts: 366
Joined: Sun Aug 08, 2010 3:32 pm
Location: Manchester UK

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

Post 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.
----

R Tape loading error, 0:1
infratec
Always Here
Always Here
Posts: 6869
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

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

Post 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.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

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

Post 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)
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
em_uk
Enthusiast
Enthusiast
Posts: 366
Joined: Sun Aug 08, 2010 3:32 pm
Location: Manchester UK

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

Post 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 :)
----

R Tape loading error, 0:1
Post Reply