Werty Posted April 12 Share Posted April 12 (edited) I'm trying to make a 1bit (two colors) floyd-steinberg dithering on images but I'm getting artifacts/blemishes, I have tried hundreds of permutations so now I have to ask you guys if you can spot the mistake(s). Pseudo code from Wikipedia... https://en.wikipedia.org/wiki/Floyd–Steinberg_dithering Quote for each y from top to bottom for each x from left to right oldpixel := pixel[x][y] newpixel := find_closest_palette_color(oldpixel) pixel[x][y] := newpixel quant_error := oldpixel - newpixel pixel[x+1][y ] := pixel[x+1][y ] + 7/16 * quant_error pixel[x-1][y+1] := pixel[x-1][y+1] + 3/16 * quant_error pixel[x ][y+1] := pixel[x ][y+1] + 5/16 * quant_error pixel[x+1][y+1] := pixel[x+1][y+1] + 1/16 * quant_error find_closest_palette_color(oldpixel) = round(oldpixel / 255) My code... expandcollapse popup#include<GDIPlus.au3> HotKeySet("{ESC}", "_exit") _GDIPlus_Startup() Global $Image = _GDIPlus_BitmapCreateFromFile ("graytest.png") Global $Width = _GDIPlus_ImageGetWidth($Image), $Height = _GDIPlus_ImageGetHeight($Image) $Gui= GUICreate("Floyd-Steinberg Dithering", $Width, $Height) GUISetState() $Graphics = _GDIPlus_GraphicsCreateFromHWND($Gui) _GDIPlus_GraphicsDrawImageRect ($Graphics,$Image, 0, 0, $Width, $Height) For $y = 0 To $Height - 1 For $x = 0 To $Width - 1 $oldpixel = Dec(Hex(_GDIPlus_BitmapGetPixel($Image, $X, $Y), 2)) $newpixel = Round($oldpixel / 255) ? 0 : 1; _GDIPlus_BitmapSetPixel($Image, $X, $Y, String("0xFF" & Hex($newpixel*255, 2) & Hex($newpixel*255, 2) & Hex($newpixel*255, 2))) $quant_error = $oldpixel - $newpixel ;~ Consolewrite($quant_error & " " & @crlf) ;-------Floyd-Steinberg _GDIPlus_BitmapSetPixel($Image, $X+1, $Y, _GDIPlus_BitmapGetPixel($Image, $X+1, $Y ) + 7/16 * $quant_error) _GDIPlus_BitmapSetPixel($Image, $X-1, $Y+1, _GDIPlus_BitmapGetPixel($Image, $X-1, $Y+1) + 3/16 * $quant_error) _GDIPlus_BitmapSetPixel($Image, $X, $Y+1, _GDIPlus_BitmapGetPixel($Image, $X, $Y+1) + 5/16 * $quant_error) _GDIPlus_BitmapSetPixel($Image, $X+1, $Y+1, _GDIPlus_BitmapGetPixel($Image, $X+1, $Y+1) + 1/16 * $quant_error) ;-------Sierra2 ;~ _GDIPlus_BitmapSetPixel($Image, $X+1, $Y , _GDIPlus_BitmapGetPixel($Image, $X+1, $Y ) + 4/16 * $quant_error) ;~ _GDIPlus_BitmapSetPixel($Image, $X+2, $Y , _GDIPlus_BitmapGetPixel($Image, $X+2, $Y ) + 3/16 * $quant_error) ;~ _GDIPlus_BitmapSetPixel($Image, $X-1, $Y+1, _GDIPlus_BitmapGetPixel($Image, $X-1, $Y+1) + 1/16 * $quant_error) ;~ _GDIPlus_BitmapSetPixel($Image, $X, $Y+1, _GDIPlus_BitmapGetPixel($Image, $X, $Y+1) + 2/16 * $quant_error) ;~ _GDIPlus_BitmapSetPixel($Image, $X+1, $Y+1, _GDIPlus_BitmapGetPixel($Image, $X+1, $Y+1) + 3/16 * $quant_error) ;~ _GDIPlus_BitmapSetPixel($Image, $X+2, $Y+1, _GDIPlus_BitmapGetPixel($Image, $X+2, $Y+1) + 2/16 * $quant_error) ;~ _GDIPlus_BitmapSetPixel($Image, $X+3, $Y+1, _GDIPlus_BitmapGetPixel($Image, $X+3, $Y+1) + 1/16 * $quant_error) Next _GDIPlus_GraphicsDrawImageRect($Graphics, $Image, 0, 0, $Width, $Height); to see the errors quick instead of waiting for the whole image Next _GDIPlus_GraphicsDrawImageRect($Graphics, $Image, 0, 0, $Width, $Height) While GUIGetMsg <> - 3 Sleep(10) WEnd Func _exit() Exit EndFunc ;-------------------------------------------------------------------------------------------------------------- ;~ from Wikipedia article https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering ;~ for each y from top to bottom ;~ for each x from left to right ;~ oldpixel := pixel[x][y] ;~ newpixel := find_closest_palette_color(oldpixel) ;~ pixel[x][y] := newpixel ;~ quant_error := oldpixel - newpixel ;~ pixel[x+1][y ] := pixel[x+1][y ] + 7/16 * quant_error ;~ pixel[x-1][y+1] := pixel[x-1][y+1] + 3/16 * quant_error ;~ pixel[x ][y+1] := pixel[x ][y+1] + 5/16 * quant_error ;~ pixel[x+1][y+1] := pixel[x+1][y+1] + 1/16 * quant_error ;~ find_closest_palette_color(oldpixel) = round(oldpixel / 255) ;-------------------------------------------------------------------------------------------------------------- If you comment the floyd-steinberg lines and uncomment the Sierra2 lines you can see that's much better without artifacts in the middle of the images, though it still has artifacts on the left side of the image, but it's mostly floyd I need working, any help? (dont mind the slow gdi+, I'll be using struct or c-dll when/if i get it working.) /edit, forgot the pic, but try with any pic /edit2, I will only be feeding it 8bit grayscale images) Edited April 12 by Werty Some guy's script + some other guy's script = my script! Link to comment Share on other sites More sharing options...
ioa747 Posted April 12 Share Posted April 12 (edited) <delete> I didn't modify anything, I just ran it and this with Sierra2 <delete> Edited April 14 by ioa747 I know that I know nothing Link to comment Share on other sites More sharing options...
Werty Posted April 12 Author Share Posted April 12 I should ofcourse have posted some comparison images, it's supposed to look something like this (from paintshop pro)... And another example, first pic is the original, second made in paintshop pro, third my result, easy to spot the artifacts mine has. Some guy's script + some other guy's script = my script! Link to comment Share on other sites More sharing options...
ioa747 Posted April 12 Share Posted April 12 (edited) with the help of copilot I got this far, the result is better but it is blue. I couldn't understand why. maybe you'll get an edge expandcollapse popup; https: ;---------------------------------------------------------------------------------------- #AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7 #include <GDIPlus.au3> #include <Math.au3> #include <Color.au3> ; Start GDI+ engine _GDIPlus_Startup() ; Load the image Global $hImage = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\graytest.png") ; Get image dimensions Global $iWidth = _GDIPlus_ImageGetWidth($hImage) Global $iHeight = _GDIPlus_ImageGetHeight($hImage) Global $Gui = GUICreate("Floyd-Steinberg Dithering", $iWidth, $iHeight) GUISetState() _Floyd_Steinberg_dithering() ;********************************** While 1 Switch GUIGetMsg() Case -3 ;$GUI_EVENT_CLOSE ExitLoop EndSwitch Sleep(10) WEnd ;********************************** ;-------------------------------------------------------------------------------------------------------------------------------- Func _Floyd_Steinberg_dithering() Local $Graphics = _GDIPlus_GraphicsCreateFromHWND($Gui) _GDIPlus_GraphicsDrawImageRect($Graphics, $hImage, 0, 0, $iWidth, $iHeight) Local $iOldColor, $iOldGray, $iNewColor, $iError ; Apply Floyd–Steinberg dithering For $y = 0 To $iHeight - 1 For $x = 0 To $iWidth - 1 ; Get the current pixel's color $iOldColor = _GDIPlus_BitmapGetPixel($hImage, $x, $y) $iOldGray = _ColorToGray($iOldColor) ; Find the closest color (black or white) $iNewColor = BitOR((($iOldGray < 128) ? 0xFF000000 : 0xFFFFFFFF), 0xFF) _GDIPlus_BitmapSetPixel($hImage, $x, $y, $iNewColor) ; Calculate the quantization error $iError = $iOldGray - _ColorToGray($iNewColor) ; Distribute the error to the neighboring pixels _DistributeError($hImage, $x + 1, $y, $iError * 7 / 16) _DistributeError($hImage, $x - 1, $y + 1, $iError * 3 / 16) _DistributeError($hImage, $x, $y + 1, $iError * 5 / 16) _DistributeError($hImage, $x + 1, $y + 1, $iError * 1 / 16) Next Next _GDIPlus_GraphicsDrawImageRect($Graphics, $hImage, 0, 0, $iWidth, $iHeight) ; Save the dithered image _GDIPlus_ImageSaveToFile($hImage, @ScriptDir & "\graytest_fs.png") ; Clean up resources _GDIPlus_BitmapDispose($hImage) _GDIPlus_Shutdown() EndFunc ;==>_Floyd_Steinberg_dithering ;-------------------------------------------------------------------------------------------------------------------------------- Func _ColorToGray($iColor) ; Extract RGB components Local $iR = BitAND(BitShift($iColor, 16), 0xFF) Local $iG = BitAND(BitShift($iColor, 8), 0xFF) Local $iB = BitAND($iColor, 0xFF) ; Return the grayscale value Return ($iR + $iG + $iB) / 3 EndFunc ;==>_ColorToGray ;-------------------------------------------------------------------------------------------------------------------------------- Func _DistributeError($hBitmap, $x, $y, $iError) ; Ensure the coordinates are within the image bounds If $x < 0 Or $x >= _GDIPlus_ImageGetWidth($hBitmap) Or $y < 0 Or $y >= _GDIPlus_ImageGetHeight($hBitmap) Then Return ; Get the current pixel's color Local $iCurrentColor = _GDIPlus_BitmapGetPixel($hBitmap, $x, $y) Local $iCurrentGray = _ColorToGray($iCurrentColor) ; Add the error to the current pixel's gray value Local $iNewGray = $iCurrentGray + $iError ; Clamp the value between 0 and 255 $iNewGray = _Max(0, _Min(255, $iNewGray)) Local $aColor[] = [$iNewGray, $iNewGray, $iNewGray] ; Set the new pixel color Local $iNewColor = _ColorSetRGB($aColor) _GDIPlus_BitmapSetPixel($hBitmap, $x, $y, $iNewColor) EndFunc ;==>_DistributeError ;-------------------------------------------------------------------------------------------------------------------------------- Edited April 12 by ioa747 packing I know that I know nothing Link to comment Share on other sites More sharing options...
Werty Posted April 12 Author Share Posted April 12 Thanks but sorry to say that it's actually much worse than mine, there are too many large areas that are blank, i need it evenly distributed as in the sample images i posted. I've been trying for days now, but not giving up, maybe I should try separating it all into funcs as in your code, though as i wrote earlier, I will only be feeding it 8bit grayscale so no need for the 24bit support your code has. I'll examine your script more thoroughly, I already tried clamping but will again, and look into the error distribution thingie. But please, anyone. 46 minutes ago, ioa747 said: I couldn't understand why. try adding a consolewrite below the iNewColor and you will see why, copilots code doesnt work, or atleast is faulty. ; Set the new pixel color $iNewColor = _ColorSetRGB($aColor) consolewrite(Hex($iNewcolor, 8) & @crlf) _GDIPlus_BitmapSetPixel($hBitmap, $x, $y, $iNewColor) EndFunc ;==>_DistributeError ioa747 1 Some guy's script + some other guy's script = my script! Link to comment Share on other sites More sharing options...
Solution AndyG Posted April 14 Solution Share Posted April 14 (edited) Hi, the reason for these "artefacts" is the accumulation of all rounding errors caused by the use of byte "colours" aka AARRGGBB. You will get better results when you calculate only with floating numbers. Unfortunately AutoIt is not able to "cast" a hex value into a float number (or i cannot remember it ^^), DEC() is able to cast hex into double but double is 64bit and does`nt fit into a 32bit "Pixel" AARRGGBB First to do is to store "floats" instead of AARRGGBB within the whole image Get the pixel value via _GDIPlus_BitmapGetPixel(), write this value into a dword struct which is on the same address as a float struct and read the dword (aka float) $floatstruct = DllStructCreate("float") $dwordstruct = DllStructCreate("dword", DllStructGetPtr($floatstruct)) ;AARRGGBB to float For $y = 0 To $Height - 1 For $x = 0 To $Width - 1 $oldPixel = Dec(Hex(_GDIPlus_BitmapGetPixel($Image, $x, $y), 2)) / 255 ;get float number between 0 and 1 DllStructSetData($floatstruct, 1, $oldPixel) ;write into float struct $oldPixel = DllStructGetData($dwordstruct, 1) ;read as dword (aka "pixel" AARRGGBB) _GDIPlus_BitmapSetPixel($Image, $x, $y, $oldPixel) ;store the float number as "pixel" AARRGGBB Next Next Now the "image" is an array of floats. In the next step you have to read the floating point numbers (aka oldpixel) via _GDIPlus_BitmapGetPixel(), calculate if more or less than 0.5 and set the newpixel value and set the the black or white pixel in the image and the quant_error For $y = 0 To $Height - 1 For $x = 0 To $Width - 1 $oldPixel = _GDIPlus_BitmapGetPixel($Image, $x, $y) ;pixel as dword DllStructSetData($dwordstruct, 1, $oldPixel) ;write into dwordstruct (place of floatstruct) $oldPixel = DllStructGetData($floatstruct, 1) ;read float If $oldPixel <= 0.5 Then $newpixel = 0 Else $newpixel = 1 EndIf _GDIPlus_BitmapSetPixel($Image, $x, $y, String("0xFF" & Hex($newpixel * 255, 2) & Hex($newpixel * 255, 2) & Hex($newpixel * 255, 2))) $quant_error = $oldPixel - $newpixel Now Floyd-Steinberg: As mentioned before, other languages can handle images and floating point numbers, I transferred dwords and floats via DllStructs. ;-------Floyd-Steinberg $pixel = _GDIPlus_BitmapGetPixel($Image, $x + 1, $y);get pixel integer/DWORD AARRGGBB DllStructSetData($dwordstruct, 1, $pixel) ;set into DWORD struct $col = DllStructGetData($floatstruct, 1) ;read float from DWORD $float = $col + (7 / 16 * $quant_error) ;calculate with float DllStructSetData($floatstruct, 1, $float) ;write into float struct $pixel = DllStructGetData($dwordstruct, 1) ;get dword from float and _GDIPlus_BitmapSetPixel($Image, $x + 1, $y, $pixel) ;write the "float" as a "pixel" which leads to the script: expandcollapse popup;$aligncomment=60 #include <GDIPlus.au3> HotKeySet("{ESC}", "_exit") _GDIPlus_Startup() Global $Image = _GDIPlus_BitmapCreateFromFile("graytest.png") ;~ Global $Image = _GDIPlus_BitmapCreateFromFile("dithercompare.png") ;~ Global $Image = _GDIPlus_BitmapCreateFromFile("test50.png") Global $Width = _GDIPlus_ImageGetWidth($Image), $Height = _GDIPlus_ImageGetHeight($Image) ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $Height = ' & $Height & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $Width = ' & $Width & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console $Gui = GUICreate("Floyd-Steinberg Dithering", $Width, $Height) GUISetState() $Graphics = _GDIPlus_GraphicsCreateFromHWND($Gui) _GDIPlus_GraphicsDrawImageRect($Graphics, $Image, 0, 0, $Width, $Height) $floatstruct = DllStructCreate("float") $dwordstruct = DllStructCreate("dword", DllStructGetPtr($floatstruct)) ;AARRGGBB to float For $y = 0 To $Height - 1 For $x = 0 To $Width - 1 $oldPixel = Dec(Hex(_GDIPlus_BitmapGetPixel($Image, $x, $y), 2)) / 255 ;get float number between 0 and 1 DllStructSetData($floatstruct, 1, $oldPixel) ;write into float struct $oldPixel = DllStructGetData($dwordstruct, 1) ;read as dword (aka "pixel" AARRGGBB) _GDIPlus_BitmapSetPixel($Image, $x, $y, $oldPixel) ;store the float number as "pixel" AARRGGBB Next Next _GDIPlus_GraphicsDrawImageRect($Graphics, $Image, 0, 0, $Width, $Height) ;show image with "floats" For $y = 0 To $Height - 1 For $x = 0 To $Width - 1 $oldPixel = _GDIPlus_BitmapGetPixel($Image, $x, $y) ;pixel as dword DllStructSetData($dwordstruct, 1, $oldPixel) ;write into dwordstruct (place of floatstruct) $oldPixel = DllStructGetData($floatstruct, 1) ;read float If $oldPixel <= 0.5 Then $newpixel = 0 Else $newpixel = 1 EndIf _GDIPlus_BitmapSetPixel($Image, $x, $y, String("0xFF" & Hex($newpixel * 255, 2) & Hex($newpixel * 255, 2) & Hex($newpixel * 255, 2))) $quant_error = $oldPixel - $newpixel ;-------Floyd-Steinberg $pixel = _GDIPlus_BitmapGetPixel($Image, $x + 1, $y);get pixel integer/DWORD AARRGGBB DllStructSetData($dwordstruct, 1, $pixel) ;set into DWORD struct $col = DllStructGetData($floatstruct, 1) ;read float from DWORD $float = $col + (7 / 16 * $quant_error) ;calculate with float DllStructSetData($floatstruct, 1, $float) ;write into float struct $pixel = DllStructGetData($dwordstruct, 1) ;get dword from float and _GDIPlus_BitmapSetPixel($Image, $x + 1, $y, $pixel) ;write the "float" as a "pixel" $pixel = _GDIPlus_BitmapGetPixel($Image, $x - 1, $y + 1) DllStructSetData($dwordstruct, 1, $pixel) ;set into DWORD struct $col = DllStructGetData($floatstruct, 1) ;read float from DWORD $float = $col + (3 / 16 * $quant_error) ;calculate with float DllStructSetData($floatstruct, 1, $float) ;write into float struct $pixel = DllStructGetData($dwordstruct, 1) ;get dword from float and _GDIPlus_BitmapSetPixel($Image, $x - 1, $y + 1, $pixel) $pixel = _GDIPlus_BitmapGetPixel($Image, $x, $y + 1) DllStructSetData($dwordstruct, 1, $pixel) ;set into DWORD struct $col = DllStructGetData($floatstruct, 1) ;read float from DWORD $float = $col + (5 / 16 * $quant_error) ;calculate with float DllStructSetData($floatstruct, 1, $float) ;write into float struct $pixel = DllStructGetData($dwordstruct, 1) ;get dword from float and _GDIPlus_BitmapSetPixel($Image, $x, $y + 1, $pixel) $pixel = _GDIPlus_BitmapGetPixel($Image, $x + 1, $y + 1) DllStructSetData($dwordstruct, 1, $pixel) ;set into DWORD struct $col = DllStructGetData($floatstruct, 1) ;read float from DWORD $float = $col + (1 / 16 * $quant_error) ;calculate with float DllStructSetData($floatstruct, 1, $float) ;write into float struct $pixel = DllStructGetData($dwordstruct, 1) ;get dword from float and _GDIPlus_BitmapSetPixel($Image, $x + 1, $y + 1, $pixel) Next _GDIPlus_GraphicsDrawImageRect($Graphics, $Image, 0, 0, $Width, $Height) ; to see the errors quick instead of waiting for the whole image Next _GDIPlus_GraphicsDrawImageRect($Graphics, $Image, 0, 0, $Width, $Height) While GUIGetMsg <> -3 Sleep(10) WEnd Func _exit() Exit EndFunc ;==>_exit Interesting side effect: If an image is filled with 50% gray the result of Floyd-Steinberg should be a checkboard pattern. In the "real world", floating point numbers have restrictions, mentioned hundrets of thousands of times here in this forum.... So at the end, in the "real world" (of floating point numbers) there is an error which shows some artefacts in the "checkboard" pattern ( 50% gray as test50.png). These errors are inevitably and independant of the used computer language. Floyd Steinberg dithering.zip Edited April 14 by AndyG Werty and ioa747 1 1 Link to comment Share on other sites More sharing options...
Werty Posted April 14 Author Share Posted April 14 (edited) Beautiful, exactly what I need, much appreciated, very close to the PaintShopPro results I've been using. I kinda figured it had something to do with floating point, reading around many websites, but from there to actually fixing it... 😛 Now to figure out how to speed it up, I usually use c-dll's though rather simple ones using TCC, but maybe it'll work out, Thanks again, much appreciated. Edited April 15 by Werty Some guy's script + some other guy's script = my script! Link to comment Share on other sites More sharing options...
Werty Posted April 15 Author Share Posted April 15 (edited) I put the image in a struct and looped the dither stuff, faster but no preview. expandcollapse popup#include <GDIPlus.au3> HotKeySet("{ESC}", "_exit") _GDIPlus_Startup() Global $Floyd[12] = [7, 3, 5, 1, 1, -1, 0, 1, 0, 1, 1, 1] Global $Image = _GDIPlus_BitmapCreateFromFile("graytest.png") Global $Width = _GDIPlus_ImageGetWidth($Image), $Height = _GDIPlus_ImageGetHeight($Image) $Gui = GUICreate("Floyd-Steinberg Dithering", $Width, $Height) GUISetState() $Graphics = _GDIPlus_GraphicsCreateFromHWND($Gui) _GDIPlus_GraphicsDrawImageRect($Graphics, $Image, 0, 0, $Width, $Height) $Bitmap1 = _GDIPlus_BitmapLockBits($Image, 0, 0, $Width, $Height, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB) $Pixels1 = DllStructCreate("dword[" & $Width * $Height & "];", DllStructGetData($Bitmap1, "Scan0")) $floatstruct = DllStructCreate("float") $dwordstruct = DllStructCreate("dword", DllStructGetPtr($floatstruct)) ;AARRGGBB to float For $y = 0 To $Height - 1 $RowOffset = $y * $Width + 1 For $x = 0 To $Width - 1 $oldPixel = Dec(Hex(DllStructGetData($Pixels1, 1, $x + $RowOffset), 2)) / 255 DllStructSetData($floatstruct, 1, $oldPixel) ;write into float struct $oldPixel = DllStructGetData($dwordstruct, 1) ;read as dword (aka "pixel" AARRGGBB) DllStructSetData($Pixels1, 1, $oldPixel, $x + $RowOffset) ;store the float number as "pixel" AARRGGBB Next Next For $y = 0 To $Height - 1 $RowOffset = $y * $Width + 1 For $x = 0 To $Width - 1 $oldpixel = DllStructGetData($Pixels1, 1, $x + $RowOffset) DllStructSetData($dwordstruct, 1, $oldPixel) ;write into dwordstruct (place of floatstruct) $oldPixel = DllStructGetData($floatstruct, 1) ;read float $newpixel = ($oldpixel <= 0.5) ? 0:1 DllStructSetData($Pixels1, 1, "0xFF" & Hex($newpixel * 255, 2) & Hex($newpixel * 255, 2) & Hex($newpixel * 255, 2), $x + $RowOffset) $quant_error = $oldPixel - $newpixel ;-------Floyd-Steinberg For $Loop = 0 To 3 $pixel = DllStructGetData($Pixels1, 1, ($x + $Floyd[$Loop+4]) + ($y + $Floyd[$Loop+8]) * $Width + 1);get pixel integer/DWORD AARRGGBB DllStructSetData($dwordstruct, 1, $pixel) ;set into DWORD struct $col = DllStructGetData($floatstruct, 1) ;read float from DWORD $float = $col + ($Floyd[$Loop] / 16 * $quant_error) ;calculate with float DllStructSetData($floatstruct, 1, $float) ;write into float struct $pixel = DllStructGetData($dwordstruct, 1) ;get dword from float and DllStructSetData($Pixels1, 1, $pixel, ($x + $Floyd[$Loop+4]) + ($y + $Floyd[$Loop+8]) * $Width + 1) ;write the "float" as a "pixel" Next Next Next _GDIPlus_BitmapUnlockBits($Image, $Bitmap1) _GDIPlus_GraphicsDrawImageRect($Graphics, $Image, 0, 0, $Width, $Height) While GUIGetMsg <> -3 Sleep(10) WEnd Func _exit() Exit EndFunc Edited April 15 by Werty Danyfirex 1 Some guy's script + some other guy's script = my script! Link to comment Share on other sites More sharing options...
UEZ Posted April 16 Share Posted April 16 (edited) @Werty I updated Just check out the example _GDIPlus_BitmapApplyFilter_Indexed.au3. This time I added also a x64 DLL but not fully tested. The result should look like this here with Floyd-Steinberg dithering: Edited April 16 by UEZ Danyfirex, ioa747 and Werty 3 Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
AndyG Posted April 16 Share Posted April 16 Hey UEZ, nice one as always ^^ I found a very cool python script here https://scipython.com/blog/floyd-steinberg-dithering/ After some tests I have realised that I can get similar results with your script when multiplying the $iColors with 3 (approximately^^). But with low numbers of $iColors, something strange is happening: Global $iColors = 16, $iDitherMode =1 Changing to $iColors = 17, $iDitherMode =1 , the result looks much better. What is the reason for that behaviour? Link to comment Share on other sites More sharing options...
Werty Posted April 17 Author Share Posted April 17 13 hours ago, UEZ said: I updated Thanks, awesome work as usual, unfortunately I know nothing about freebasic and kinda need it to be "my own" code, with help and inspiration from others ofcourse, this is just a small part of a bigger project, but I will certainly be using it till I get around to attempting making a c-dll, that will include other stuff also, I've just been busy with other parts after getting this atleast working so I could get on with other stuff. But damn it's fast. Some guy's script + some other guy's script = my script! Link to comment Share on other sites More sharing options...
Werty Posted April 17 Author Share Posted April 17 (edited) How about going the other way, more than 256 grays... Guess how many grays this image has. Spoiler 1401 grays Or make your own... expandcollapse popup#Include <GDIPlus.au3> HotKeySet("{ESC}", "_exit") _GDIPlus_Startup() $Image = _GDIPlus_BitmapCreateFromFile("image.png") $Width = _GDIPlus_ImageGetWidth($Image) $Height = _GDIPlus_ImageGetHeight($Image) $GUI = GUICreate("AmazingGrays", $Width, $Height);, -1, -1) GUISetState() $Graphics = _GDIPlus_GraphicsCreateFromHWND($Gui) _GDIPlus_GraphicsDrawImageRect ($Graphics,$Image, 0, 0, $Width, $Height) $Bitmap = _GDIPlus_BitmapLockBits($Image, 0, 0, $Width, $Height, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB) $Pixels = DllStructCreate("dword[" & $Width * $Height & "];", DllStructGetData($Bitmap, "Scan0")) For $Loop = 1 To $Width * $Height $Pixel = DllStructGetData($Pixels, 1, $Loop) $Red = Dec(StringMid(Hex($Pixel, 6), 1, 2)) $Green = Dec(StringMid(Hex($Pixel, 6), 3, 2)) $Blue = Dec(StringMid(Hex($Pixel, 6), 5, 2)) Local $R = 0, $G = 0, $B = 0 $LUMA = (($Red*.3) + ($Green*.59) + ($Blue*.11)/3) $AGrays = $LUMA - Int($LUMA) $LUMA = Int($LUMA) Switch StringFormat("%.2f", $AGrays) Case .05 To .18 $B = 1 Case .19 To .34 $G = 1 Case .35 To .50 $B = 1 $G = 1 Case .51 To .66 $R = 1 Case .67 To .82 $R = 1 $B = 1 Case .83 To .95 $R = 1 $G = 1 EndSwitch DllStructSetData($Pixels, 1, "0xFF" & Hex($LUMA + $R, 2) & Hex($LUMA + $G, 2) & Hex($LUMA + $B, 2), $Loop) Next _GDIPlus_BitmapUnlockBits($Image, $Bitmap) _GDIPlus_GraphicsDrawImageRect($Graphics, $Image, 0, 0, $Width, $Height) Do Until GUIGetMsg() = - 3 Func _exit() Exit EndFunc It's using "near gray" colors, like (155,155,154) looks gray eventhough it's not pure, also called "pseudo gray" or "fake gray", good for photographers that wanna print high resolution images as grayscale on big posters. /edit Comparison between standard averaging on the left that came to 246 grays and AmazingGrays on the right came to 1512 grays. Edited April 17 by Werty Some guy's script + some other guy's script = my script! Link to comment Share on other sites More sharing options...
UEZ Posted April 17 Share Posted April 17 The color reducing code is based on the GDIPlus function GdipGetImagePalette which is unfortunately not the best algorithm to choose the best colors. Additionally it doesn't utilize the full color range. For example if you create a 8 bit bitmap it doesn't use the full color scope of 256 colors. The color formats which I use is 2-bit, 4-bit and 8 bit -> 2, 16 and 256 colors. Nothing in between will be calculated. @AndyG that is the reason why it looks better when $iColors is > 16. It will set it to 256 color bitmap (8 bit). @Werty the DLL is using heavily GDIPlus functions and the For/Next loops are much faster in Freebasic than in Autoit. In the DLL I added also a function call "_GDIPlus_BitmapCreateGreyscale" which converts the image to 256 greyscale colors using (iR * 213 + iG * 715 + iB * 72) / 1000 calculation. Let me add your "fake"grey to the DLL... Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
Werty Posted April 17 Author Share Posted April 17 7 minutes ago, UEZ said: Let me add your "fake"grey to the DLL... If it was a question then sure, please go ahead. Some guy's script + some other guy's script = my script! Link to comment Share on other sites More sharing options...
UEZ Posted April 17 Share Posted April 17 12 hours ago, Werty said: If it was a question then sure, please go ahead. Done. Check out Download the 7-Zip archive and look for _GDIPlus_BitmapApplyFilter_FakeGreyscale.au3 example. Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
Werty Posted April 17 Author Share Posted April 17 (edited) Nice, how does the code look in freebasic, not that I'm gonna start with freebasic, just curious. I noticed we dont get same results, I take it you didnt use LUMA but the " (iR * 213 + iG * 715 + iB * 72) / 1000" you mentioned, quite a difference, i get... Eagle.jpg: yours 1020 grays, mine 1654 Face, png: yours 978, mine 1576 Maybe a switch parameter where the user can select between "Average", "LUMA", "Yours" and whatever other there might be. Edited April 17 by Werty Some guy's script + some other guy's script = my script! Link to comment Share on other sites More sharing options...
UEZ Posted April 17 Share Posted April 17 Here the function in FB: expandcollapse popupFunction _GDIPlus_BitmapCreateFakeGreyscale(hImage As Any Ptr, bGDI As BOOL = False) As Any Ptr Export Dim As Single iW, iH Dim As Double fGreys, fLuma Dim As Any Ptr hBitmap_Greyscale, hGDIBitmap Dim As BitmapData tBitmapData, tBitmapData_Greyscale Dim As Long iX, iY, iRowOffset, iColor, c, iR, iG, iB GdipGetImageDimension(hImage, @iW, @iH) Dim As RECT tRect = Type(0, 0, iW - 1, iH - 1) GdipCreateBitmapFromScan0(iW, iH, 0, PixelFormat32bppARGB, 0, @hBitmap_Greyscale) GdipBitmapLockBits(hBitmap_Greyscale, Cast(Any Ptr, @tRect), ImageLockModeWrite, PixelFormat32bppARGB, @tBitmapData_Greyscale) GdipBitmapLockBits(hImage, Cast(Any Ptr, @tRect), ImageLockModeRead, PixelFormat32bppARGB, @tBitmapData) For iY = 0 To iH - 1 iRowOffset = iY * iW For iX = 0 To iW - 1 iColor = Cast(ULong Ptr, tBitmapData.Scan0)[iRowOffset + iX] iR = (iColor Shr 16) And &hFF iG = (iColor Shr 8) And &hFF iB = iColor And &hFF fLuma = (iR * 213 + iG * 715 + iB * 72) / 1000 'fLuma = ((iR * 0.3) + (iG * 0.59) + (iB * 0.11) / 3) fGreys = fLuma - CUByte(fLuma) fLuma = CUByte(fLuma) iR = 0 iG = 0 iB = 0 Select Case fGreys Case 0.05 To 0.18 iB = 1 Case 0.19 To 0.34 iG = 1 Case 0.35 To 0.50 iB = 1 iG = 1 Case 0.51 To 0.66 iR = 1 Case 0.67 To 0.82 iR = 1 iB = 1 Case 0.83 To 0.95 iR = 1 iG = 1 End Select Cast(ULong Ptr, tBitmapData_Greyscale.Scan0)[iRowOffset + iX] = &hFF000000 Or ((fLuma + iR) Shl 16) Or ((fLuma + iG) Shl 8) Or (fLuma + iB) Shl 0 Next Next GdipBitmapUnlockBits(hBitmap_Greyscale, @tBitmapData_Greyscale) GdipBitmapUnlockBits(hImage, @tBitmapData) If bGDI Then GdipCreateHBITMAPFromBitmap(hBitmap_Greyscale, @hGDIBitmap, &hFF000000) GdipDisposeImage(hBitmap_Greyscale) Return hGDIBitmap EndIf Return hBitmap_Greyscale End Function I will play around with different values... Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
Werty Posted April 17 Author Share Posted April 17 (edited) Earlier i adjusted this part... Select Case fGreys Case 0.05 To 0.18 iB = 1 Case 0.19 To 0.34 iG = 1 Case 0.35 To 0.50 iB = 1 iG = 1 Case 0.51 To 0.66 iR = 1 Case 0.67 To 0.82 iR = 1 iB = 1 Case 0.83 To 0.95 iR = 1 iG = 1 End Select ..to use only 2 decimal places, if the decimal places are longer than 2 it misses some going from case to case, scroll up and see the changes. Edited April 17 by Werty Some guy's script + some other guy's script = my script! Link to comment Share on other sites More sharing options...
UEZ Posted April 17 Share Posted April 17 I changed it to 2 decimals, too. Now more colors will be used. Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
Werty Posted April 17 Author Share Posted April 17 That's more like it. now I get... Eagle 1785 Face 1690 Some guy's script + some other guy's script = my script! Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now