After I tried the code from the first post I had an idea for another way to convert images to monochrome.
The 2x2 option calculates the average intensity of a 2x2 block of pixels and converts to 5 different intensity levels.
The 3x3 option calculates the average intensity of a 3x3 block of pixels and converts to 10 different intensity levels.
I changed the code so the size of the original image is preserved.
The saved image is now done with .png format which is much smaller than .bmp.
[Edit] Improved code and added better images.
[Edit2] Added Sierra Lite dithering option provided by Wilbert.
Click each thumbnail image to view full sized.
Code: Select all
; Convert RGB image to 1 bit monochrome
UseJPEGImageDecoder()
UsePNGImageDecoder()
UsePNGImageEncoder()
#MainWin = 0
#Image_Image_0 = 0
#Image_Image_1 = 1
#White = $FFFFFF
#Black = $000000
;{ Gadgets
Enumeration
#ImgGad_RGB
#ImgGad_BW
#BTN_LOAD
#BTN_SAVE
#Trackbar_Cspan
#TrackBar_Threshold
#CheckBox_Rnd
#Option_BW
#Option_Dither1
#Option_Dither2
#Option_Gray5
#Option_Gray10
#StatusBar
EndEnumeration
;}
Global sourceFile$, mode = #Option_BW, imgW, imgH
; black-white
Procedure.l BW_Img(Img)
Protected Gree, Width, Height, x, y, Pixel, cp
If Not IsImage(Img) : ProcedureReturn #False : EndIf
Gree = 255 - GetGadgetState(#TrackBar_Threshold)
StartDrawing(ImageOutput(Img))
Width = ImageWidth(Img) - 1
Height = ImageHeight(Img) - 1
For y = 0 To Height
For x = 0 To Width
cp = Point(x, y)
Pixel = (Red(cp)<<1 + Green(cp)<<2 + Blue(cp)) / 7
If Pixel > Gree
Plot(x, y, #White)
Else
Plot(x, y, #Black)
EndIf
Next x
Next y
StopDrawing()
ProcedureReturn #True
EndProcedure
Procedure Dither1(Img)
Protected.i cc_128 = 256 - GetGadgetState(#TrackBar_Threshold)
Protected.i DitherError, cc, cl, rr, gg, bb
StartDrawing(ImageOutput(Img))
Width = ImageWidth(Img) - 1
Height = ImageHeight(Img) - 1
For j = 0 To Height
For i = 0 To Width
; Dithering Algorithm
cl = Point(i, j)
cc = (Red(cl)*2 + Green(cl)*3 + Blue(cl)) / 6
If cc <= cc_128
cc = cc * 100 / cc_128 + 10
Else
cc = (cc - cc_128) * 109 / (255 - cc_128) + 146
EndIf
cc - 128
DitherError + cc * 32 / 31
If DitherError <= 0 Or cl < 10
Plot (i, j, #Black)
DitherError + 128
Else
Plot (i, j, #White)
DitherError - 127
EndIf
DitherError * 31 / 32
Next
Next
StopDrawing()
EndProcedure
Procedure Dither2(Img); Sierra Lite
Protected l, x, y, w = ImageWidth(Img), h = ImageHeight(Img)
Protected error, errorMul = GetGadgetState(#TrackBar_Threshold)
Dim errorBuffer(w)
StartDrawing(ImageOutput(Img))
While y < h
x = 0
error = 0
While x < w
error + errorBuffer(x + 1)
l = Point(x, y)
l = (Red(l) * 77 + Green(l) * 150 + Blue(l) * 29) >> 8 + error
If l >= 128
Plot(x, y, #White)
If l > 255
error = 0
Else
error = l - 255
EndIf
Else
Plot(x, y, #Black)
If l < 0
error = 0
Else
error = l
EndIf
EndIf
error = (error * errorMul) >> 9
errorBuffer(x + 1) = error >> 1
errorBuffer(x) + errorBuffer(x + 1)
x + 1
Wend
y + 1
Wend
StopDrawing()
EndProcedure
Procedure.l Gray5(Img) ; by BasicallyPure
; convert 2x2 block of pixels into 5 shades of intensity
DataSection
BP:
Data.l %00000000, %10001000, %10011001, %11101110, %11111111
EndDataSection
Protected Width, Height, Pixel, x, y, p, cp
Protected lum = GetGadgetState(#TrackBar_Threshold) - 128
Protected Rflag = GetGadgetState(#CheckBox_Rnd)
If IsImage(Img)
StartDrawing(ImageOutput(Img))
Width = ImageWidth(Img) - 2
Height = ImageHeight(Img) - 2
For y = 0 To Height Step 2
For x = 0 To Width Step 2
Pixel = 0
; ------- get average intensity for a 2x2 block ----------
For i = x To x+1
For j = y To y+1
cp = Point(i,j)
Pixel + Red(cp)<<1 + Green(cp)<<2 + Blue(cp)
Next j
Next i
Pixel / 28 + lum
; --------------------------------------------------------
If Pixel > 255 : Pixel = 255
ElseIf Pixel < 0 : Pixel = 0
EndIf
p = PeekL(?BP + (Pixel/52)<<2)
If Rflag : p >> Random(3) : EndIf
For i = x To x+1
For j = y To y+1
If p & 1 : Plot(i, j, #White)
Else : Plot(i, j, #Black)
EndIf
p >> 1
Next j
Next i
Next x
Next y
StopDrawing()
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
Procedure.l Gray10(Img) ; by BasicallyPure
; convert 3x3 block of pixels into 10 shades of intensity
Protected inc = GetGadgetState(#Trackbar_Cspan)
Protected Rflag = GetGadgetState(#CheckBox_Rnd)
Protected lum = GetGadgetState(#TrackBar_Threshold) - 128
Protected Width, Height, Pixel, x, y, i, j, p, cp
Protected L1, L2, L3, L4, L5, L6, L7, L8, L9
Macro Plot3x3(pattern)
p = pattern
If Rflag : p >> Random(8) : EndIf
For i = x To x+2
For j = y To y+2
If p & 1 : Plot(i,j,#White) : Else : Plot(i,j,#Black) : EndIf
p >> 1
Next j
Next i
EndMacro
If IsImage(Img)
L1 = (127 - inc*4) : L2 = (127 - inc*3) : L3 = (127 - inc*2)
L4 = (127 - inc ) : L5 = (127 ) : L6 = (127 + inc )
L7 = (127 + inc*2) : L8 = (127 + inc*3) : L9 = (127 + inc*4)
StartDrawing(ImageOutput(Img))
Width = ImageWidth(Img) - 3
Height = ImageHeight(Img) - 3
For y = 0 To Height Step 3
For x = 0 To Width Step 3
Pixel = 0
For i = x To x+2 ; get average intensity for a 3x3 block
For j = y To y+2
cp = Point(i,j)
Pixel + Red(cp)<<1 + Green(cp)<<2 + Blue(cp)
Next j
Next i
Pixel / 63 + lum
If Pixel > 255 : Pixel = 255
ElseIf Pixel < 0 : Pixel = 0
EndIf
If Pixel > L9 : Plot3x3(%111111111111111111) ; White
ElseIf Pixel > L8 : Plot3x3(%111101111111101111) ; Gray1
ElseIf Pixel > L7 : Plot3x3(%011111110011111110) ; Gray2
ElseIf Pixel > L6 : Plot3x3(%110011101110011101) ; Gray3
ElseIf Pixel > L5 : Plot3x3(%101010101101010101) ; Gray4
ElseIf Pixel > L4 : Plot3x3(%010101010010101010) ; Gray5
ElseIf Pixel > L3 : Plot3x3(%001010100001010100) ; Gray6
ElseIf Pixel > L2 : Plot3x3(%100000001100000001) ; Gray7
ElseIf Pixel > L1 : Plot3x3(%000010000000010000) ; Gray8
Else : Plot3x3(%000000000000000000) ; Black
EndIf
Next x
Next y
StopDrawing()
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
Procedure Convert()
If IsImage(0) : CopyImage(0, 1)
Select mode
Case #Option_BW : BW_Img(1)
Case #Option_Dither1 : Dither1(1)
Case #Option_Dither2 : Dither2(1)
Case #Option_Gray10 : Gray10(1)
Case #Option_Gray5 : Gray5(1)
Default : ProcedureReturn 0
EndSelect
SetGadgetState(#ImgGad_BW, ImageID(1))
ProcedureReturn 1
EndIf
EndProcedure
Procedure INIT_GUI()
ExamineDesktops()
DW = DesktopWidth(0)
flags = #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_TitleBar
If OpenWindow(#MainWin, 0, 0, DW, DW/2, "Converting an RGB image to 1 bit-per-pixel monochrome", flags)
imgW = (WindowWidth(#MainWin) - 30) / 2
imgH = imgW * 0.75
CreateStatusBar(#StatusBar, WindowID(#MainWin))
WH = imgH + 110 + StatusBarHeight(#StatusBar)
ResizeWindow(#MainWin,0,0,#PB_Ignore,WH)
SetWindowColor(#MainWin, $D0E0E0)
AddStatusBarField(#PB_Ignore)
ImageGadget(#ImgGad_RGB, 10, 35, imgW, imgH, 0, #PB_Image_Border)
ImageGadget(#ImgGad_BW, imgW+20, 35, imgW, imgH, 0, #PB_Image_Border)
TrackBarGadget(#TrackBar_Threshold, imgW+20, imgH+45, imgW*0.6, 25, 0, 255)
SetGadgetState(#TrackBar_Threshold,128)
TrackBarGadget(#Trackbar_Cspan,imgW*1.6+30,imgH+45,imgW*0.3,25,5,25,#PB_TrackBar_Ticks)
SetGadgetState(#Trackbar_Cspan,15)
ButtonGadget(#BTN_LOAD, 10, 5, 100, 25, "Load")
ButtonGadget(#BTN_SAVE, imgW+20, 5, 100, 25, "Save as PNG")
OptionGadget(#Option_BW, imgW+20, imgH+80, 50, 25, "B & W")
SetGadgetState(#Option_BW,#True)
OptionGadget(#Option_Dither1, imgW+80, imgH+80, 60, 25, "Dither1")
OptionGadget(#Option_Dither2, imgW+150, imgH+80, 60, 25, "Dither2")
OptionGadget(#Option_Gray5, imgW+220, imgH+80, 50, 25, "2 x 2")
OptionGadget(#Option_Gray10, imgW+280, imgH+80, 50, 25, "3 x 3")
CheckBoxGadget(#CheckBox_Rnd,imgW+340, imgH+80, 80, 25, "Randomize")
EndIf
EndProcedure
Procedure Save()
Static Pattern$ = "Image .png|*.png"
If IsImage(1) And sourceFile$ <> ""
File$ = SaveFileRequester("Enter filename to save",
RemoveString(GetFilePart(sourceFile$),"."+GetExtensionPart(sourceFile$))+
"_B&W.png",Pattern$,0)
If LoadImage(1,sourceFile$)
Select mode
Case #Option_BW : BW_Img(1)
Case #Option_Dither1 : Dither1(1)
Case #Option_Dither2 : Dither2(1)
Case #Option_Gray10 : Gray10(1)
Case #Option_Gray5 : Gray5(1)
Default : ProcedureReturn 0
EndSelect
SaveImage(1, File$ , #PB_ImagePlugin_PNG, 0, 1)
Convert()
EndIf
EndIf
EndProcedure
Procedure Load()
Static Pattern$ = "Image .bmp, .jpg, .png|*.bmp;*.jpg;*.png|All files (*.*)|*.*"
Protected w, h, Ratio.f
sourceFile$ = OpenFileRequester("", "", Pattern$, 0)
If sourceFile$ > ""
If LoadImage(0, sourceFile$)
StatusBarText(#StatusBar, 0, sourceFile$)
w = ImageWidth(0) : h = ImageHeight(0)
If w > imgW Or h > imgH
Ratio = h / w
If Ratio < 0.75 ;size by width
Ratio = imgW / w
Else ; size by height
Ratio = imgH / h
EndIf
ResizeImage(0, w * Ratio, h * Ratio)
EndIf
SetGadgetState(#ImgGad_RGB, ImageID(0))
Convert()
EndIf
EndIf
EndProcedure
INIT_GUI()
;- Event loop
;{
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Break
Case #PB_Event_Gadget : nGadget = EventGadget()
Select nGadget
Case #Trackbar_Cspan To #Option_Gray10
If nGadget >= #Option_BW : mode = nGadget : EndIf
Convert()
Case #BTN_SAVE : Save()
Case #BTN_LOAD : Load()
EndSelect
EndSelect
ForEver
;}