How to optimize this code?

Everything else that doesn't fall into one of the other PB categories.
User avatar
jacdelad
Addict
Addict
Posts: 2010
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: How to optimize this code?

Post by jacdelad »

So you mean I shouldn't call LimitMin and LimitMax within the loops? This should be doable.
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
idle
Always Here
Always Here
Posts: 5899
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: How to optimize this code?

Post by idle »

If they don't change per loop yes pull em out of the loop.
I did a test with the sepia routine my method was 3.5 times faster.
User avatar
jacdelad
Addict
Addict
Posts: 2010
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: How to optimize this code?

Post by jacdelad »

But they change for the border pixels. Otherwise I had to make separate loops for the border pixels.
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
idle
Always Here
Always Here
Posts: 5899
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: How to optimize this code?

Post by idle »

replace them with macros in that case.
User avatar
jacdelad
Addict
Addict
Posts: 2010
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: How to optimize this code?

Post by jacdelad »

Sorry, but how do I put this into a macro (we still don't have an If() function)? I can't think of a one line solution.
Best I can imagine is calculating it beforehand and then doing the for loops with variables (which contain the results).
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
Demivec
Addict
Addict
Posts: 4268
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: How to optimize this code?

Post by Demivec »

jacdelad wrote: Wed Jul 26, 2023 11:10 pm Sorry, but how do I put this into a macro (we still don't have an If() function)? I can't think of a one line solution.
Best I can imagine is calculating it beforehand and then doing the for loops with variables (which contain the results).
Maybe something like this would work:

Code: Select all

        Case #Bin_Binarization_BlockMode
          For x=0 To w-1
            For y=0 To h-1
              Pixel=Point(x,y)
              Values(x,y)=((Red(Pixel) * srgb\sr) + (Green(Pixel) * srgb\sg) + (Blue(Pixel) * srgb\sb)) * srgb\sa
            Next
          Next
          BlockSize=Int(*Binarization\BlockSize/2)
          ;precalculate x clamp values
          Dim xc(w - 1, 1) 
          For x = 0 To w - 1
              xc(x, 0)  = _LimitMin(x - BlockSize, 0)  
              xc(x, 1)  = _LimitMax(x + BlockSize, w - 1)
          Next
          ;precalculate y clamp values
          Dim yc(h - 1, 1) 
          For y =0  To h - 1
              yc(y, 0)  = _LimitMin(y - BlockSize, 0)  
              yc(y, 1)  = _LimitMax(y + BlockSize, h - 1)
          Next

          For x=0 To w-1
            For y=0 To h-1
              Count=0
              Counter=-1
              For xa= xc(x, 0) To xc(x, 1)
                For ya= yc(y, 0) To yc(y, 1)
                  Counter+1
                  Count+Values(xa,ya)
                Next
              Next
              If Values(x,y)*Counter>Count-Values(x,y)
                Plot(x,y,#White)
              Else
                Plot(x,y,#Black)
              EndIf
            Next
          Next
User avatar
jacdelad
Addict
Addict
Posts: 2010
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: How to optimize this code?

Post by jacdelad »

That's a good idea, thanks! I'll try this and make some benchmarks.
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
jacdelad
Addict
Addict
Posts: 2010
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: How to optimize this code?

Post by jacdelad »

Ok, so I optimized it as suggested from Demivec and idle. Also I added the Phansalkar algorithm, which offers a variety of parameters (that I don't fully understand).

Code: Select all

;Binarization algorithms (used parameters are written with CAPITAL LETTERS):
;- Relative:
;Converts the range of 8bit greyscale into a percentage scale. The given THRESHOLD defines which pixels are white (<threshold) or black (<=threshold).
;- Absolute:
;Same as relative, but THRESHOLD uses the whole 8bit range (0..255)
;- Range:
;Same as relative, but first finds the darkest and lightest pixel and narrows the range down. THRESHOLD is percentage within this found range.
;- Count:
;Creates a histogram and uses THRESHOLD as percentage (0..100) of how many pixels are drawn black.
;- Rasterize:
;Same as Absolute, but the picture is rasterized. This algorithm has no scientific foundation and I discovered it "accidently".
;It will be adapted IN the future, because For now it doesn't work as intended! More like a fun-mode.
;- BlockMode:
;Determines the threshold by adapting to the neighbourhood pixels. The BLOCKSIZE (3..255) defines the size of the block, e.g. a blocksize of 5 uses a 5x5 block.
;- OTSU:
;This algorithm doesn't need any parameters, because it defines the threshold by itself.
;It creates a histogram, finds the two highest peaks And SETS the threshold To the exact middle between them.
;- Niblack:
;Local intensity averagd and standard deviation. The BLOCKSIZE (3.255) defines the size of each block
;While a CONSTANT (Default=-0.2) defines the weight of the deviation. This algorithm tends To produce noisy results.
;- Sauvola:
;Improvement of the Niblack algorithm (less noisy). The BLOCKSIZE (3..255) defines the size of each block, CONSTANT is a measure for
;weighing the deviation (0.2..0.5, default=0.5) and DYNAMICRANGE defines the variance of the deviation (1..255, default=128).
;- Bernsen:
;Calculates a dynamic threshold based on the minimum and maximum intensities based on the neighbourhood pixels. The BLOCKSIZE (3..255) defines
;the size of each block while BACKGROUND defines whether the background is light or dark.
;Additionally And DYNAMICRANGE defines the local contrast limit (default=15).
;- Phansalkar:
;This algorithm is rather complex. It can behave like Sauvola, but is designed to deliver better results. To be honest I don't really understand all the parameters
;and it's advised to either experiment or leave them to their default values. BLOCKSIZE again defines the blocksize (3..255) of pixels to be looked around the current
;pixel. The MAGNITUDE effects the threshold (values between 0 and 1 behave like Sauvola, values higher than 5 may classify too many foreground pixels as
;background pixels, default is 3). PHANSALKARCONSTANT does...whatever, default is 10. The DYNAMICRANGE (0..255) is for the standard deviation
;(Default is 127) And CONSTANT is a parameter between 0.2 and 0.5 (default is 0.25).
;
;When using #Bin_Greyscale_Custom you can use fR, fG and fB to define the weight of each channel, otherwise use a predefined scheme.

EnableExplicit

Enumeration Binarization      ;THRESHOLD:
  #Bin_Binarization_Relative  ;0..100:  Range, percentage
  #Bin_Binarization_Absolute  ;0..255:  Range, 8bit value
  #Bin_Binarization_Range     ;0..100:  Range percentage, only within range of used colors
  #Bin_Binarization_Count     ;0..100:  Percent of pixels that are black
  #Bin_Binarization_Rasterize ;0..255:  Range, 8bit value, rasterize image
  #Bin_Binarization_BlockMode ;         Adapts to neighbour pixels, blocksize (always odd numbers, not lower than 3!) to control the block size; speed decreases drastically with bigger block sizes!
  #Bin_Binarization_OTSU      ;         Self adapting, doesn't need threshold
  #Bin_Binarization_Sauvola   ;         Mean/deviation calculation, very cpu consuming, higher value=darker result
  #Bin_Binarization_Bernsen   ;         Predecessor of Sauvola
  #Bin_Binarization_Niblack   ;         Adapts to neighbour pixels, but noisier than Sauvola, blocksize (always odd numbers, not lower than 3!) to control the block size; speed decreases drastically with bigger block sizes!
  #Bin_Binarization_Phansalkar;         Successor of Sauvola
EndEnumeration
Enumeration Greyscale
  #Bin_Greyscale_BT709
  #Bin_Greyscale_Equal
  #Bin_Greyscale_REC601
  #Bin_Greyscale_BT2100
  #Bin_Greyscale_RedChannel
  #Bin_Greyscale_GreenChannel
  #Bin_Greyscale_BlueChannel
  #Bin_Greyscale_Custom
EndEnumeration
#Bin_Background_Dark  = 255
#Bin_Background_Light =   0
Enumeration Sepia
  #Bin_Sepia_Simple
EndEnumeration

Structure Binarization
  Algorithm.a
  PreProcessing.a
  fR.f
  fG.f
  fB.f
  Threshold.a
  BlockSize.a
  Background.a
  Constant.f
  DynamicRange.a
  Magnitude.f
  PhansalkarConstant.a
EndStructure

Structure sRGB 
  sr.f
  sg.f
  sb.f 
  sa.f 
EndStructure   

Structure mRGB 
  StructureUnion 
    col.l 
    b.a
    g.a
    r.a 
    a.a 
  EndStructureUnion 
EndStructure 

Procedure CopyMemoryToImage(Memory, ImageNumber)
  
  Protected TemporaryDC, TemporaryBitmap.BITMAP, TemporaryBitmapInfo.BITMAPINFO
  
  TemporaryDC = CreateDC_("DISPLAY", #Null, #Null, #Null)
  
  GetObject_(ImageID(ImageNumber), SizeOf(BITMAP), TemporaryBitmap.BITMAP)
  
  TemporaryBitmapInfo\bmiHeader\biSize        = SizeOf(BITMAPINFOHEADER)
  TemporaryBitmapInfo\bmiHeader\biWidth       = TemporaryBitmap\bmWidth
  TemporaryBitmapInfo\bmiHeader\biHeight      = -TemporaryBitmap\bmHeight
  TemporaryBitmapInfo\bmiHeader\biPlanes      = 1
  TemporaryBitmapInfo\bmiHeader\biBitCount    = 32
  TemporaryBitmapInfo\bmiHeader\biCompression = #BI_RGB
  
  SetDIBits_(TemporaryDC, ImageID(ImageNumber), 0, TemporaryBitmap\bmHeight, Memory, TemporaryBitmapInfo, #DIB_RGB_COLORS)
  
  DeleteDC_(TemporaryDC)
  
EndProcedure
Procedure CopyImageToMemory(ImageNumber, Memory)
  
  Protected TemporaryDC, TemporaryBitmap.BITMAP, TemporaryBitmapInfo.BITMAPINFO
  
  TemporaryDC = CreateDC_("DISPLAY", #Null, #Null, #Null)
  
  GetObject_(ImageID(ImageNumber), SizeOf(BITMAP), TemporaryBitmap.BITMAP)
  
  
  TemporaryBitmapInfo\bmiHeader\biSize        = SizeOf(BITMAPINFOHEADER)
  TemporaryBitmapInfo\bmiHeader\biWidth       = TemporaryBitmap\bmWidth
  TemporaryBitmapInfo\bmiHeader\biHeight      = -TemporaryBitmap\bmHeight
  TemporaryBitmapInfo\bmiHeader\biPlanes      = 1
  TemporaryBitmapInfo\bmiHeader\biBitCount    = 32
  TemporaryBitmapInfo\bmiHeader\biCompression = #BI_RGB
  
  GetDIBits_(TemporaryDC, ImageID(ImageNumber), 0, TemporaryBitmap\bmHeight, Memory, TemporaryBitmapInfo, #DIB_RGB_COLORS)
  
  DeleteDC_(TemporaryDC)
  
EndProcedure
Procedure Greyscale(Image.i,Algorithm.a=#Bin_Greyscale_BT709,fR.f=0,fG.f=0,fB.f=0)
  Protected Result.i=-1,x.l,y.l,r.a,g.b,b.a,w.l,h.l,Pixel.l,Res.l
  Protected srgb.sRGB,*mem.mRGB,*out, size,ct,col.f,ImageOut        
  If IsImage(Image)
    
    ImageOut = CopyImage(0,-1)  
    size = (ImageWidth(image)*ImageHeight(image)) << 2 
    *mem = AllocateMemory(size) 
    *out = *mem 
    
    srgb\sa = 1.0 
    Select Algorithm
      Case #Bin_Greyscale_BT709
        srgb\sr = 0.2126 : srgb\sg = 0.7152 : srgb\sb = 0.0722
      Case #Bin_Greyscale_Equal
        srgb\sr = 1.0 : srgb\sg = 1.0 : srgb\sb = 1.0 : srgb\sa = 1.0 / 3.0 
      Case #Bin_Greyscale_REC601
        srgb\sr = 0.299 : srgb\sg = 0.587 : srgb\sb = 0.114 
      Case #Bin_Greyscale_BT2100
        srgb\sr = 0.2627 : srgb\sg = 0.678 : srgb\sb = 0.0593 
      Case #Bin_Greyscale_RedChannel
        srgb\sr = 1.0 : srgb\sg = 0 : srgb\sb = 0
      Case #Bin_Greyscale_GreenChannel
        srgb\sr = 0 : srgb\sg = 1.0 : srgb\sb = 0
      Case #Bin_Greyscale_BlueChannel
        srgb\sr = 0 : srgb\sg = 0 : srgb\sb = 1.0 
      Case #Bin_Greyscale_Custom
        srgb\sr = fr : srgb\sg = fg : srgb\sb = fb
    EndSelect
    
    CopyImageToMemory(Image,*mem)      
    
    While ct < size    
      col.f = ((*mem\r * srgb\sr) + (*mem\g * srgb\sg ) + (*mem\b * srgb\sb)) *  srgb\sa  
      *mem\r = col 
      *mem\g = col 
      *mem\b = col 
      *mem+4
      ct+4    
    Wend   
    
    CopyMemoryToImage(*out,Imageout) 
    
    FreeMemory(*out) 
    
  EndIf
  ProcedureReturn imageout
EndProcedure
Procedure Sepia(Image.i,Algorithm.a=#Bin_Sepia_Simple)
  Protected Result.i=-1,x.l,y.l,Pixel.l,R.a,G.a,B.a,wR.w,wG.w,wB.w,w.l=ImageWidth(Image)-1,h.l=ImageHeight(Image)-1
  If IsImage(Image)
    Result=CopyImage(Image,#PB_Any)
    If Result<>0
      StartDrawing(ImageOutput(Result))
      
      Select Algorithm
          
        Case #Bin_Sepia_Simple
          For x=0 To w
            For y=0 To h
              Pixel=Point(x,y)
              R=Red(Pixel)
              G=Green(Pixel)
              B=Blue(Pixel)
              wR=0.393*R + 0.769*G + 0.189*B
              If wR>255:wR=255:EndIf
              wG=0.349*R + 0.686*G + 0.168*B
              If wG>255:wG=255:EndIf
              wB=0.272*R + 0.534*G + 0.131*B
              If wB>255:wB=255:EndIf
              Plot(x,y,RGB(wR,wG,wB))
            Next
          Next
          
      EndSelect      
      StopDrawing()
    EndIf
  EndIf
  ProcedureReturn Result
EndProcedure
Procedure _LimitMin(a.l,b.l)
  If a>b
    ProcedureReturn a
  Else
    ProcedureReturn b
  EndIf
EndProcedure
Procedure _LimitMax(a.l,b.l)
  If a<b
    ProcedureReturn a
  Else
    ProcedureReturn b
  EndIf
EndProcedure

Procedure Binarize(Image.i,*Binarization.Binarization)
  Protected Result.i=-1,x.l,y.l,Pixel.l,r.a,g.a,b.a,Res.a,Min.a=255,Max.a,mw.l=ImageWidth(Image)-1,mh.l=ImageHeight(Image)-1,Count.l,Counter.l
  Protected srgb.srgb,xa.l,ya.l,Mean.a,Dev.q,Threshold.a=*Binarization\Threshold,BlockSize.a
  Protected Dim LimitX(mw,1),Dim LimitY(mh,1),Dim Values.a(mw,mh),Dim Deviation.a(0),Dim Count.l(255)
  
  Select *Binarization\Algorithm
    Case #Bin_Binarization_BlockMode
      Threshold=Threshold/2
    Case #Bin_Binarization_Relative
      Threshold=Threshold*2.55
  EndSelect
  
  If IsImage(Image)
    
    Result=CopyImage(Image,#PB_Any)
    If Result<>0
      
      srgb\sa = 1.0 
      Select *Binarization\PreProcessing
        Case #Bin_Greyscale_BT709
          srgb\sr = 0.2126 : srgb\sg = 0.7152 : srgb\sb = 0.0722
        Case #Bin_Greyscale_Equal
          srgb\sr = 1.0 : srgb\sg = 1.0 : srgb\sb = 1.0 : sRGB\sa = 1.0 / 3.0
        Case #Bin_Greyscale_REC601
          srgb\sr = 0.299 : srgb\sg = 0.587 : srgb\sb = 0.114 
        Case #Bin_Greyscale_BT2100
          srgb\sr = 0.2627 : srgb\sg = 0.678 : srgb\sb = 0.0593 
        Case #Bin_Greyscale_RedChannel
          srgb\sr = 1.0 : srgb\sg = 0 : srgb\sb = 0
        Case #Bin_Greyscale_GreenChannel
          srgb\sr = 0 : srgb\sg = 1.0 : srgb\sb = 0
        Case #Bin_Greyscale_BlueChannel
          srgb\sr = 0 : srgb\sg = 0 : srgb\sb = 1.0 
        Case #Bin_Greyscale_Custom
          srgb\sr = *Binarization\fR : srgb\sg = *Binarization\fG : srgb\sb = *Binarization\fB : srgb\sa=1/(*Binarization\fR+*Binarization\fG+*Binarization\fB)
      EndSelect
      
      StartDrawing(ImageOutput(Result))
      
      
      Select *Binarization\Algorithm
        Case #Bin_Binarization_Absolute,#Bin_Binarization_Relative
          For x=0 To mw
            For y=0 To mh
              Pixel=Point(x,y)
              If ((Red(Pixel) * srgb\sr) + (Green(Pixel) * srgb\sg) + (Blue(Pixel) * srgb\sb)) * srgb\sa > Threshold
                Plot(x,y,#White)
              Else
                Plot(x,y,#Black)
              EndIf
            Next
          Next
          
        Case #Bin_Binarization_Rasterize
          For x=0 To mw
            For y=0 To mh
              Pixel=Point(x,y)
              If ((Red(Pixel) * srgb\sr) + (Green(Pixel) * srgb\sg) + (Blue(Pixel) * srgb\sb)) * srgb\sa > Threshold
                Plot(x,y,#White)
              Else
                Plot(x,y,#Black)
              EndIf
              Threshold=Threshold*2.55
            Next
          Next
          
        Case #Bin_Binarization_Range
          For x=0 To mw
            For y=0 To mh
              Pixel=Point(x,y)
              Res = ((Red(Pixel) * srgb\sr) + (Green(Pixel) * srgb\sg) + (Blue(Pixel) * srgb\sb)) * srgb\sa  
              Values(x,y)=Res
              If Res<Min:Min=Res:EndIf
              If Res>Max:Max=Res:EndIf
            Next
          Next
          Threshold=Min+Threshold*(Max-Min)/100
          For x=0 To mw
            For y=0 To mh
              If Values(x,y)>Threshold
                Plot(x,y,#White)
              Else
                Plot(x,y,#Black)
              EndIf
            Next
          Next
          
        Case #Bin_Binarization_Count
          For x=0 To mw
            For y=0 To mh
              Pixel=Point(x,y)
              Res = ((Red(Pixel) * srgb\sr) + (Green(Pixel) * srgb\sg) + (Blue(Pixel) * srgb\sb)) * srgb\sa  
              Values(x,y)=Res
              Count(Res)+1
            Next
          Next
          Count=Threshold*(mw+1)*(mh+1)/100
          Counter=0
          For x=0 To 255
            Counter+Count(x)
            If Counter>Count
              Threshold=x-1
              Break
            EndIf
          Next
          For x=0 To mw
            For y=0 To mh
              If Values(x,y)>Threshold
                Plot(x,y,#White)
              Else
                Plot(x,y,#Black)
              EndIf
            Next
          Next
          
        Case #Bin_Binarization_OTSU
          For x=0 To mw
            For y=0 To mh
              Pixel=Point(x,y)
              Res = ((Red(Pixel) * srgb\sr) + (Green(Pixel) * srgb\sg) + (Blue(Pixel) * srgb\sb)) * srgb\sa  
              Values(x,y)=Res
              Count(Res)+1
            Next
          Next
          Count=0
          For x=0 To 255
            If Count(x)>Count
              Count=Count(x)
              Max=x
            EndIf
          Next
          Counter=0
          For x=255 To 0 Step -1
            If Count(x)>Counter And (Count(x)<Count Or x<>Max)
              Counter=Count(x)
              Min=x
            EndIf
          Next
          Threshold=Max+(Min-Max)/2
          For x=0 To mw
            For y=0 To mh
              If Values(x,y)>Threshold
                Plot(x,y,#White)
              Else
                Plot(x,y,#Black)
              EndIf
            Next
          Next
          
        Case #Bin_Binarization_BlockMode
          BlockSize=Int(*Binarization\BlockSize/2)
          For x=0 To mw
            LimitX(x,0)=_LimitMin(x-BlockSize,0)
            LimitX(x,1)=_LimitMax(x+BlockSize,mw)
            For y=0 To mh
              Pixel=Point(x,y)
              Values(x,y)=((Red(Pixel) * srgb\sr) + (Green(Pixel) * srgb\sg) + (Blue(Pixel) * srgb\sb)) * srgb\sa
            Next
          Next
          For y=0 To mh
            LimitY(y,0)=_LimitMin(y-BlockSize,0)
            LimitY(y,1)=_LimitMax(y+BlockSize,mh)
          Next
          For x=0 To mw
            For y=0 To mh
              Count=0
              Counter=-1
              For xa=LimitX(x,0) To LimitX(x,1)
                For ya=LimitY(y,0) To LimitY(y,1)
                  Counter+1
                  Count+Values(xa,ya)
                Next
              Next
              If Values(x,y)*Counter>Count-Values(x,y)
                Plot(x,y,#White)
              Else
                Plot(x,y,#Black)
              EndIf
            Next
          Next
          
        Case #Bin_Binarization_Sauvola
          ReDim Deviation(Int(Pow(*Binarization\BlockSize,2)-1))
          BlockSize=Int(*Binarization\BlockSize/2)
          For x=0 To mw
            LimitX(x,0)=_LimitMin(x-BlockSize,0)
            LimitX(x,1)=_LimitMax(x+BlockSize,mw)
            For y=0 To mh
              Pixel=Point(x,y)
              Values(x,y)=((Red(Pixel) * srgb\sr) + (Green(Pixel) * srgb\sg) + (Blue(Pixel) * srgb\sb)) * srgb\sa
            Next
          Next
          For y=0 To mh
            LimitY(y,0)=_LimitMin(y-BlockSize,0)
            LimitY(y,1)=_LimitMax(y+BlockSize,mh)
          Next
          For x=0 To mw
            For y=0 To mh
              Count=0
              Counter=0
              For xa=LimitX(x,0) To LimitX(x,1)
                For ya=LimitY(y,0) To LimitY(y,1)
                  Count+Values(xa,ya)
                  Deviation(counter)=Values(xa,ya)
                  Counter+1
                Next
              Next
              Mean=Count/Counter
              Dev=0
              Counter-1
              For xa=0 To Counter
                Dev+Pow(Deviation(xa)-Mean,2)
              Next
              If Values(x,y)>Mean*(1+*Binarization\Constant*((Pow(Dev/Counter,0.5)/*Binarization\DynamicRange)-1))
                Plot(x,y,#White)
              Else
                Plot(x,y,#Black)
              EndIf
            Next
          Next
          
        Case #Bin_Binarization_Bernsen
          BlockSize=Int(*Binarization\BlockSize/2)
          For x=0 To mw
            LimitX(x,0)=_LimitMin(x-BlockSize,0)
            LimitX(x,1)=_LimitMax(x+BlockSize,mw)
            For y=0 To mh
              Pixel=Point(x,y)
              Values(x,y)=((Red(Pixel) * srgb\sr) + (Green(Pixel) * srgb\sg) + (Blue(Pixel) * srgb\sb)) * srgb\sa
            Next
          Next
          For y=0 To mh
            LimitY(y,0)=_LimitMin(y-BlockSize,0)
            LimitY(y,1)=_LimitMax(y+BlockSize,mh)
          Next
          For x=0 To mw
            For y=0 To mh
              Min=255
              Max=0
              For xa=LimitX(x,0) To LimitX(x,1)
                For ya=LimitY(y,0) To LimitY(y,1)
                  If Values(xa,ya)<Min:Min=Values(xa,ya):EndIf
                  If Values(xa,ya)>Max:Max=Values(xa,ya):EndIf
                Next
              Next
              If Max-Min<*Binarization\DynamicRange
                Max=*Binarization\Background
              Else
                Max=(Min+Max)/2
              EndIf
              If Values(x,y)<Max
                Plot(x,y,#Black)
              Else
                Plot(x,y,#White)
              EndIf
            Next
          Next
          
        Case #Bin_Binarization_Niblack
          ReDim Deviation(Int(Pow(*Binarization\BlockSize,2)-1))
          BlockSize=Int(*Binarization\BlockSize/2)
          For x=0 To mw
            LimitX(x,0)=_LimitMin(x-BlockSize,0)
            LimitX(x,1)=_LimitMax(x+BlockSize,mw)
            For y=0 To mh
              Pixel=Point(x,y)
              Values(x,y)=((Red(Pixel) * srgb\sr) + (Green(Pixel) * srgb\sg) + (Blue(Pixel) * srgb\sb)) * srgb\sa
            Next
          Next
          For y=0 To mh
            LimitY(y,0)=_LimitMin(y-BlockSize,0)
            LimitY(y,1)=_LimitMax(y+BlockSize,mh)
          Next
          For x=0 To mw
            For y=0 To mh
              Count=0
              Counter=0
              For xa=LimitX(x,0) To LimitX(x,1)
                For ya=LimitY(y,0) To LimitY(y,1)
                  Count+Values(xa,ya)
                  Deviation(counter)=Values(xa,ya)
                  Counter+1
                Next
              Next
              Mean=Count/Counter
              Dev=0
              Counter-1
              For xa=0 To Counter
                Dev+Pow(Deviation(xa)-Mean,2)
              Next
              If Values(x,y)<Mean+*Binarization\Constant*Pow(Dev/Counter,0.5)
                Plot(x,y,#Black)
              Else
                Plot(x,y,#White)
              EndIf
            Next
          Next
          
        Case #Bin_Binarization_Phansalkar
          ReDim Deviation(Int(Pow(*Binarization\BlockSize,2)-1))
          BlockSize=Int(*Binarization\BlockSize/2)
          For x=0 To mw
            LimitX(x,0)=_LimitMin(x-BlockSize,0)
            LimitX(x,1)=_LimitMax(x+BlockSize,mw)
            For y=0 To mh
              Pixel=Point(x,y)
              Values(x,y)=((Red(Pixel) * srgb\sr) + (Green(Pixel) * srgb\sg) + (Blue(Pixel) * srgb\sb)) * srgb\sa
            Next
          Next
          For y=0 To mh
            LimitY(y,0)=_LimitMin(y-BlockSize,0)
            LimitY(y,1)=_LimitMax(y+BlockSize,mh)
          Next
          For x=0 To mw
            For y=0 To mh
              Count=0
              Counter=0
              For xa=LimitX(x,0) To LimitX(x,1)
                For ya=LimitY(y,0) To LimitY(y,1)
                  Count+Values(xa,ya)
                  Deviation(counter)=Values(xa,ya)
                  Counter+1
                Next
              Next
              Mean=Count/Counter
              Dev=0
              Counter-1
              For xa=0 To Counter
                Dev+Pow(Deviation(xa)-Mean,2)
              Next
              
              If Values(x,y) < Round((Mean * (1 + *Binarization\Magnitude * Exp(-1* *Binarization\PhansalkarConstant * Mean) + *Binarization\Constant * ((Dev/*Binarization\DynamicRange) - 1))),#PB_Round_Nearest)
                Plot(x,y,#Black)
              Else
                Plot(x,y,#White)
              EndIf
            Next
          Next
          
      EndSelect
      
      StopDrawing()
    EndIf
  EndIf
  ProcedureReturn Result
EndProcedure

CompilerIf #PB_Compiler_IsMainFile
  Global Bin,XML$,MyBinarization.Binarization
  
  Runtime Enumeration Windows
    #Window
  EndEnumeration
  Runtime Enumeration Gadgets
    #Image
    #Checkbox1
    #Combo1
    #Combo2
    #Text1
    #Text2
    #Text3
    #Text4
    #Text5
    #Text6
    #Text7
    #String1
    #String2
    #String3
    #String4
    #String6
    #String7
    #Button1
    #Button2
    #Button3
  EndEnumeration
  Enumeration XML
    #XML
  EndEnumeration
  Enumeration Dialogs
    #Dialog
  EndEnumeration
  
  Procedure LiveEvents()
    If GetGadgetState(#Button3)
      If Bin And IsImage(Bin):FreeImage(Bin):EndIf
      MyBinarization\Threshold=Val(GetGadgetText(#String1))
      MyBinarization\Algorithm=GetGadgetState(#Combo2)
      MyBinarization\PreProcessing=GetGadgetState(#Combo1)
      MyBinarization\BlockSize=Val(GetGadgetText(#String2))
      MyBinarization\Constant=Val(GetGadgetText(#String3))
      MyBinarization\DynamicRange=Val(GetGadgetText(#String4))
      If GetGadgetState(#Checkbox1)&#PB_Checkbox_Checked
        MyBinarization\Background=#Bin_Background_Dark
      Else
        MyBinarization\Background=#Bin_Background_Light
      EndIf
      Bin=Binarize(0,MyBinarization)
      SetGadgetState(#Image,ImageID(Bin))
    EndIf
  EndProcedure
  
  UseJPEGImageDecoder()
  LoadImage(0,OpenFileRequester("Load file","","*.jpg",0))
  
  XML$="<window id='#Window' name='MainWindow' text='Binarization Test' flags='#PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget'>"+
       "<hbox expand='item:1'>"+
       "<image id='#Image' width='400' height='400'/>"+     
       "<vbox expand='item:14'>"+
       "<combobox id='#Combo1' width='150'/>"+
       "<combobox id='#Combo2' width='150'/>"+
       "<hbox expand='item:1'>"+
       "<text id='#Text1' text='Threshold:'/>"+
       "<string id='#String1' text='50' width='50' flags='#PB_String_Numeric'/>"+
       "</hbox>"+
       "<hbox expand='item:1'>"+
       "<text id='#Text2' text='Blocksize:'/>"+
       "<string id='#String2' text='5' width='50' flags='#PB_String_Numeric'/>"+
       "</hbox>"+
       "<hbox expand='item:1'>"+
       "<text id='#Text3' text='Constant:'/>"+
       "<string id='#String3' text='0.2' width='50'/>"+
       "</hbox>"+
       "<hbox expand='item:1'>"+
       "<text id='#Text4' text='Dynamic Range:'/>"+
       "<string id='#String4' text='128' width='50' flags='#PB_String_Numeric'/>"+
       "</hbox>"+
       "<hbox expand='item:1'>"+
       "<text id='#Text6' text='Magnitude:'/>"+
       "<string id='#String6' text='3' width='50'/>"+
       "</hbox>"+
       "<hbox expand='item:1'>"+
       "<text id='#Text7' text='Phansalkar Constant:'/>"+
       "<string id='#String7' text='10' width='50' flags='#PB_String_Numeric'/>"+
       "</hbox>"+
       "<checkbox id='#Checkbox1' text='Background dark'/>"+
       "<button id='#Button1' text='Lets go!'/>"+
       "<button id='#Button2' text='Original'/>"+
       "<button id='#Button3' text='Go live!' flags='#PB_Button_Toggle'/>"+
       "<text id='#Text5' text=''/>"+
       "<empty/>"+
       "</vbox>"+
       "</hbox>"+
       "</window>"
  
  If IsImage(0)
    
    ParseXML(#Xml, XML$)
    XMLStatus(#XML)
    CreateDialog(#Dialog)
    OpenXMLDialog(#Dialog, #Xml, "MainWindow")
    
    AddGadgetItem(#Combo1,-1,"BT709")
    AddGadgetItem(#Combo1,-1,"Equal")
    AddGadgetItem(#Combo1,-1,"REC601")
    AddGadgetItem(#Combo1,-1,"BT2100")
    AddGadgetItem(#Combo1,-1,"Red Channel")
    AddGadgetItem(#Combo1,-1,"Green Channel")
    AddGadgetItem(#Combo1,-1,"Blue Channel")
    SetGadgetState(#Combo1,0)
    
    AddGadgetItem(#Combo2,-1,"Relative")
    AddGadgetItem(#Combo2,-1,"Absolute")
    AddGadgetItem(#Combo2,-1,"Range")
    AddGadgetItem(#Combo2,-1,"Count")
    AddGadgetItem(#Combo2,-1,"Rasterize")
    AddGadgetItem(#Combo2,-1,"BlockMode")
    AddGadgetItem(#Combo2,-1,"OTSU")
    AddGadgetItem(#Combo2,-1,"Sauvola")
    AddGadgetItem(#Combo2,-1,"Bernsen")
    AddGadgetItem(#Combo2,-1,"Niblack")
    AddGadgetItem(#Combo2,-1,"Phansalkar")
    SetGadgetState(#Combo2,0)
    
    BindGadgetEvent(#Combo1,@LiveEvents(),#PB_EventType_Change)
    BindGadgetEvent(#Combo2,@LiveEvents(),#PB_EventType_Change)
    BindGadgetEvent(#String1,@LiveEvents(),#PB_EventType_Change)
    SetGadgetState(#Image,ImageID(0))
    RefreshDialog(#Dialog)
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Break
        Case #PB_Event_Gadget
          Select EventGadget()
            Case #Button1;Do binarization
              If Bin And IsImage(Bin):FreeImage(Bin):EndIf
              MyBinarization\Threshold=Val(GetGadgetText(#String1))
              MyBinarization\Algorithm=GetGadgetState(#Combo2)
              MyBinarization\PreProcessing=GetGadgetState(#Combo1)
              MyBinarization\BlockSize=Val(GetGadgetText(#String2))
              MyBinarization\Constant=Val(GetGadgetText(#String3))
              MyBinarization\DynamicRange=Val(GetGadgetText(#String4))
              MyBinarization\Magnitude=Val(GetGadgetText(#String6))
              MyBinarization\PhansalkarConstant=Val(GetGadgetText(#String7))
              If GetGadgetState(#Checkbox1)&#PB_Checkbox_Checked
                MyBinarization\Background=#Bin_Background_Dark
              Else
                MyBinarization\Background=#Bin_Background_Light
              EndIf
              Bin=Binarize(0,MyBinarization)
              ;bin = Greyscale(0,GetGadgetState(#Combo1))
              SetGadgetState(#Image,ImageID(Bin))
            Case #Button2;Revert to original image
              SetGadgetState(#Image,ImageID(0))
            Case #Button3;Live view
              If GetGadgetState(#Button3)
                DisableGadget(#Button1,#True)
                If Bin And IsImage(Bin):FreeImage(Bin):EndIf
                MyBinarization\Threshold=Val(GetGadgetText(#String1))
                MyBinarization\Algorithm=GetGadgetState(#Combo2)
                MyBinarization\PreProcessing=GetGadgetState(#Combo1)
                MyBinarization\BlockSize=Val(GetGadgetText(#String2))
                MyBinarization\Constant=Val(GetGadgetText(#String3))
                MyBinarization\DynamicRange=Val(GetGadgetText(#String4))
                MyBinarization\Magnitude=Val(GetGadgetText(#String6))
                MyBinarization\PhansalkarConstant=Val(GetGadgetText(#String7))
                If GetGadgetState(#Checkbox1)&#PB_Checkbox_Checked
                  MyBinarization\Background=#Bin_Background_Dark
                Else
                  MyBinarization\Background=#Bin_Background_Light
                EndIf
                Bin=Binarize(0,MyBinarization)
                SetGadgetState(#Image,ImageID(Bin))
              Else
                DisableGadget(#Button1,#False)
              EndIf
          EndSelect
      EndSelect
    ForEver
  Else
    MessageRequester("Error","Image could not be loaded!",#PB_MessageRequester_Error)  
  EndIf
CompilerEndIf
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
Post Reply