October 25, 2021, 10:26:53

Author Topic: Paper Scan or Foto Scan Threshold optimization  (Read 482 times)

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 364
    • Midimaster Music Education Software
Paper Scan or Foto Scan Threshold optimization
« on: March 22, 2021, 13:56:17 »
This worklog shows the resulting code of a users question here on the forum. He needed an algorithm for finding the best result of paper scanning. His scanner provides an 256-grayscaled PNG file of 30000x20000pix. But often the white of the paper was scanned as already gray. The scanner offers an (bad) algo to convert this grayscale-png into a 1bit-black-and-white-png by using the same threshold level allover the 600MPixel. The result was poor.

Now here is a code who enables to fine adjust thresholds for free definable areas of the PNG. The user starts with the grayscaled-PNG and a first allover threshold of 100. Then he defines rectangles with the mouse for fixing individual thresholds. On a preview you can see the effect immediately. When clicking the right mouse also the main image is updated. At the end the user saves the result PNG.

The code is a executable BlitzMax-NG code, which also may run on BlitzMax1.50. The code is public domain.
It already work with an test image I add as a link here:
http://www.midimaster.de/fremde/OriginalD.png

Code: BlitzMax
  1. SuperStrict
  2. Import MaxGUI.Drivers
  3.  
  4. 'two screen windows for the pixmaps:
  5. Const PREVIEW_X%=114,PREVIEW_Y%=90, TARGET_X%=800, TARGET_Y%=40
  6. Const MOUSE_LEFT%=1, MOUSE_RIGHT%=2, MENU_LOAD%=1, MENU_SAVE%=2, TURN_LEFT%=-1, TURN_RIGHT%=1
  7. Global OriginalX%, OriginalY%, PreviewWidth%, PreviewHeight%, WasRotated%=0, Threshold%, Refresh%=True
  8. Global ScalingFaktor#
  9.  
  10. Global Window:TGadget, Canvas:TGadget
  11.  
  12. CreateGui
  13.  
  14. 'create all necessary Pixmaps:
  15. Global Target:TPixmap,  Original:TPixmap, Small:TImage
  16.  
  17. ' now create the preview pixmaps
  18. Global Preview:TPixmap, PreviewBackup:TPixmap, PreviewImage:TImage
  19.  
  20. 'now lets define the threshold array:
  21. Global Thresholds:Byte[600,800]
  22.  
  23.  
  24. 'now let us define a first rectangle over the whole preview:
  25. Global MausRect:WorkRect=workRect.Create(), MausX%, MausY%
  26.  
  27. LoadPNG("OriginalD.png")
  28.  
  29. While WaitEvent()
  30.         Select EventID()
  31.                 Case EVENT_GADGETPAINT
  32.                                 Local Fenster:TGraphics =CanvasGraphics(Canvas)
  33.                                 SetGraphics Fenster
  34.                                 DrawBackground
  35.                                 If Preview<>Null
  36.                                         DrawPreviewImage
  37.                                         ShowResultImage
  38.                                 EndIf
  39.                                 Flip 0
  40.                 Case EVENT_WINDOWCLOSE, EVENT_APPTERMINATE
  41.                                 FreeGadget canvas
  42.                                 End
  43.                 Case EVENT_MENUACTION
  44.                                 Select EventData()
  45.                                         Case MENU_LOAD
  46.                                                 Local Url$=RequestFile("Load Grayscale Image", "png", False , "")
  47.                                                 If      Upper(ExtractExt(Url))="PNG"
  48.                                                         LoadPNG(Url)
  49.                                                 EndIf
  50.                                         Case MENU_SAVE
  51.                                                 If Original<>Null
  52.                                                         Local Url$=RequestFile("Save 1bit-Image", "png", True , "")
  53.                                                         If      Upper(ExtractExt(Url))="PNG"
  54.                                                                 SaveResult Url
  55.                                                         EndIf
  56.                                                 EndIf
  57.                                 End Select
  58.                                 RedrawGadget canvas
  59.  
  60.                 Case EVENT_MOUSEDOWN
  61.                         If EventData()=MOUSE_LEFT
  62.                                 MausRect.Define EventX(),EventY()
  63.                         ElseIf EventData()=MOUSE_RIGHT
  64.                        
  65.                                 DoFinalThreshold
  66.                         EndIf  
  67.                         RedrawGadget canvas
  68.  
  69.                 Case EVENT_MOUSEWHEEL
  70.                                 SetMouseThreshold(EventData())
  71.                                 RedrawGadget canvas
  72.  
  73.                 Case EVENT_MOUSEUP
  74.                         If EventData()=MOUSE_LEFT
  75.                                 If MausRect.State=1
  76.                                         MausRect.Define EventX(),EventY()
  77.                                 EndIf
  78.                         EndIf  
  79.  
  80.                 Case EVENT_MOUSEMOVE
  81.                                 MausX=EventX()
  82.                                 MausY=EventY()
  83.                                 OriginalX=(MausX-PREVIEW_X)*ScalingFaktor-400
  84.                                 OriginalY=(MausY-PREVIEW_Y)*ScalingFaktor-400
  85.                                 RedrawGadget canvas
  86.                 Default
  87.                         Print  "EVENT= " +              EventID()      
  88.         End Select
  89. Wend
  90.  
  91.  
  92. Function DrawBackground()
  93.                 'paints the GUI
  94.                 SetViewport 0,0,1800,1000
  95.                 SetScale 1,1
  96.                 SetClsColor 1,71,71            
  97.                 Cls
  98.                 DrawRect 4, PREVIEW_Y-54,60,910
  99.                 DrawRect PREVIEW_X-44, PREVIEW_Y-54,708,910
  100.                 DrawRect TARGET_X-4,TARGET_Y-4,1800-TARGET_X-12,910
  101.                 SetColor 1,1,1
  102.                 DrawRect 8, PREVIEW_Y-50,52,900
  103.                 DrawRect PREVIEW_X-40, PREVIEW_Y-50,700,900
  104.                 DrawRect TARGET_X,TARGET_Y,1800-TARGET_X-20,902
  105.                 DrawScala
  106. End Function
  107.  
  108.  
  109. Function DrawScala()
  110.                 'paints the scale on the left side
  111.                 SetColor 255,255,255           
  112.                 For Local i%=0 To 250 Step 10  
  113.                         DrawText i,13, PREVIEW_Y+i*3
  114.                 Next  
  115.                 DrawText "<<", 40,PREVIEW_Y-2+Threshold*3
  116. End Function
  117.  
  118.  
  119. Function DrawPreviewImage()
  120.                 ' main loop
  121.                 SetViewport PREVIEW_X,PREVIEW_Y,600,800
  122.                 SetColor 255,255,255
  123.                 DrawImage PreviewImage,PREVIEW_X,PREVIEW_Y
  124.                 MausRect.Draw
  125. End Function
  126.  
  127.  
  128. Function SetMouseThresHold(Direction%) 
  129.                 ' listen to the mousewheel and updates the treshold array and the preview window
  130.                 Threshold = Max(0, Min(255, Threshold + Direction))
  131.                 FixPartialThresHold
  132.                 ManipulatePreviewPixmap()
  133. End Function    
  134.  
  135.  
  136. Function FixPartialThresHold()
  137.                 ' changes the threshold array inside the limit of the mouse-rect
  138.                 If MausRect.State>0 Return
  139.                 For Local x%=0 To MausRect.Width-1
  140.                         For Local y%=0 To MausRect.Height-1
  141.                                 Thresholds[MausRect.X+X,MausRect.Y+Y] = Threshold
  142.                         Next
  143.                 Next
  144. End Function
  145.  
  146.  
  147. Function LoadPNG(Png_URL$)
  148.                 'load the Image and create all necessary Pixmaps:
  149.                 Original=LoadPixmap(PNG_URL)
  150.                 Original=ConvertPixmap(Original,PF_I8)
  151.  
  152.  
  153.                 If CheckRotationNeeded(Original.Width, Original.Height)
  154.                         Original=TurnPixmap(Original,TURN_RIGHT)
  155.                         WasRotated=True
  156.                 Else
  157.                         WasRotated=False
  158.                 EndIf
  159.                 Target=CopyPixmap(Original)
  160.  
  161.  
  162.                 ' now create the preview pixmaps
  163.                 Preview=CopyPixmap(Original)
  164.                 ' this defines the PreviewWidth and PreviewHeight of the preview:
  165.                 CheckRatio(Preview.Width, Preview.Height)
  166.                 Preview=ResizePixmap(Preview, PreviewWidth, PreviewHeight)
  167.                 PreviewBackup=CopyPixmap(Preview)
  168.                 PreviewImage=LoadImage(Preview)
  169.                 MausRect= WorkRect.Create()
  170.  
  171.                 'this calculates the scaling difference between preview and original pixmap
  172.                 ScalingFaktor = Divide(Original.Height,PreviewHeight)
  173.  
  174.                 'now lets define a first threshold:
  175.                 SetStartupThreshold(100)
  176.                 Refresh=True
  177.  
  178. End Function
  179.  
  180.  
  181. Function ManipulatePreviewPixmap()
  182.                 'changes the preview window depending on the threshold array
  183.                 Local zeit%=MilliSecs()
  184.                 For Local y%=0 To PreviewHeight-1
  185.                         Local Pitch%=Preview.Pitch*y
  186.                         For Local x%=0 To PreviewWidth-1
  187.                                 Local r:Byte= PreviewBackup.Pixels[Pitch+X]
  188.                                 If r>ThresHolds[X,Y]
  189.                                         Preview.Pixels[Pitch+X]=255
  190.                                 Else
  191.                                         Preview.Pixels[Pitch+X]=0
  192.                                 EndIf
  193.                         Next
  194.                 Next
  195.                 PreviewImage=LoadImage(Preview)
  196.                 Print "time needed preview=" + (MilliSecs()-Zeit)
  197. End Function
  198.  
  199.  
  200. Function ShowResultImage()
  201.                 'only displays the correct part of the big original pixmap depending on the mouse position
  202.                 Global LastX%, LastY%
  203.                 If (LastX<>OriginalX) Or (LastY<>OriginalY) Or Refresh=True
  204.                         Refresh=False
  205.                         Local X%=Max(0,Min(OriginalX , Original.Width-980))
  206.                         Local Y%=Max(0,Min(OriginalY , Original.Height-900))
  207.                         LastX = X
  208.                         LastY = Y
  209.                         Local w%=Min(980,Original.Width)
  210.                         Local h%=Min(900,Original.Height)
  211.                         Small=LoadImage(PixmapWindow(Target,X,Y,w,h))
  212.                 EndIf
  213.                         SetColor 255,255,255
  214.                         SetScale 1,1
  215.                         SetViewport TARGET_X,TARGET_Y,1800-TARGET_X-20,1000-TARGET_Y-58
  216.                         DrawImage small,TARGET_X,TARGET_Y
  217. End Function
  218.  
  219.  
  220. Function DoFinalThreshold()    
  221.                 If original=Null Return
  222.                 ' calculates the original pixmap depending on the threshold array
  223.                 Local Zeit%=MilliSecs()
  224.                 For Local y%=0 To  Original.Height-1
  225.                         Local RasterY%=Y/ScalingFaktor
  226.                         Local Peek% = y*Original.Pitch
  227.                         For Local x%=0 To Original.Width-1
  228.                                 Local RasterX%=X/ScalingFaktor
  229.  
  230.                                 Local Threshold%= ThresHolds[RasterX,RasterY]
  231.                                 Local r:Byte= Original.Pixels[Peek+X]
  232.                                 If r>ThresHold
  233.                                         Target.Pixels[Peek+X]=255
  234.                                 Else
  235.                                         Target.Pixels[Peek+X]=0
  236.                                 EndIf
  237.                         Next
  238.                 Next
  239.                 Refresh=True
  240.                 Print "time needed original=" + (MilliSecs()-Zeit)
  241. End Function
  242.  
  243.  
  244. Function SaveResult(Url$)
  245.                 'saves the original pixmap as PNG
  246.                 Local SavePixmap:TPixmap=CopyPixmap(Target)
  247.                 If WasRotated=True
  248.                         SavePixMap=TurnPixmap(SavePixMap,TURN_RIGHT)
  249.                 EndIf
  250.                 SavePixmapPNG(SavePixMap,URL,5)
  251. End Function
  252.  
  253.  
  254. Function CheckRatio(Width#, Height#)
  255.                 'special for alsmost square scans
  256.                 Local Ratio#=Width/Height
  257.                 If Ratio>0.75
  258.                         PreviewWidth=600
  259.                         PreviewHeight=600*ratio
  260.                 Else
  261.                         PreviewHeight=800
  262.                         PreviewWidth=800*Ratio
  263.                 EndIf
  264. End Function
  265.  
  266.  
  267. Function CheckRotationNeeded%(Width#, Height#)
  268.                 ' checks if scan is in portrait mode
  269.                 If Width>Height
  270.                         Return True
  271.                 EndIf
  272.                 Return False
  273. End Function
  274.  
  275.  
  276. Function TurnPixMap:TPixmap(Source:TPixmap,Direction%=1)
  277.         ' turn pixmaps from landscape to portrait mode an back
  278.         ' direction: 1=90° to the right, -1=90° to the left
  279.        
  280.         Local Target:TPixmap, H%, W%, TargetPitch%, SourcePitch%
  281.         Target=CreatePixmap(Source.Height,Source.Width,Source.Format)
  282.         Target.ClearPixels(0)
  283.         TargetPitch=Target.Pitch
  284.         SourcePitch=Source.Pitch       
  285.         H=Target.Height
  286.         W=Target.Width
  287.  
  288.         For Local y%=0 To H-1
  289.                 For Local X%=0 To W-1
  290.                         If Direction=TURN_RIGHT
  291.                                 '90° right
  292.                                 Target.Pixels[(W-X-1) + Y*TargetPitch] = Source.Pixels[Y+X*SourcePitch]                        
  293.                         Else
  294.                                 '90° left
  295.                                 Target.Pixels[(H-Y-1)*TargetPitch + X] = Source.Pixels[Y+X*SourcePitch]                        
  296.                         EndIf
  297.                 Next
  298.         Next
  299.         Return Target
  300. End Function
  301.  
  302.  
  303. Function Divide#(value1#, Value2#)
  304.                 ' return a float result from a integer division
  305.                 Return Value1/Value2
  306. End Function
  307.  
  308.  
  309.  
  310. Type WorkRect
  311.                         ' this type cares about the mouse-rectangle
  312.                         ' use: 1. bweofre main loop once: create()
  313.                         '      2. first mousedown must call define() -> defines left top corner
  314.                         '      3. move around to find next corner
  315.                         '      4. release the mousebutton cmust all define() again -> defines right bottom corner
  316.                         '      5. next mousedown start again at 2.
  317.                         Field X%, Y%, Width%, Height%, State%=0
  318.                        
  319.                         Function Create:WorkRect()
  320.                                 Local loc:WorkRect=New WorkRect
  321.                                 loc.X=0
  322.                                 loc.Y=0
  323.                                 loc.Width=PreviewWidth
  324.                                 loc.Height=PreviewHeight
  325.                                 loc.State=0
  326.                                 Return loc
  327.                         End Function
  328.                        
  329.                        
  330.                         Method Define(Mx%, My%)
  331.                                 Select state
  332.                                         Case 0
  333.                                                 X=mx
  334.                                                 Y=my
  335.                                                 State=1
  336.                                         Case 1
  337.                                                 X=Max(0,X-PREVIEW_X)
  338.                                                 Y=Max(0,y-PREVIEW_Y)
  339.                                                 Width= Min(PreviewWidth,Mx-PREVIEW_X)-X
  340.                                                 Height=Min(PreviewHeight,My-PREVIEW_Y)-Y
  341.                                                 State=0
  342.                                 End Select             
  343.                         End Method      
  344.                        
  345.                        
  346.                         Method Draw()
  347.                                 'care about all states of drawing of the mouse rect
  348.                                 SetColor 255,255,0
  349.                                 Select State
  350.                                         Case 1
  351.                                                 Local mX% = Max(X+6,MausX)
  352.                                                 Local my% = Max(Y+6,MausY)
  353.                                                 DrawOpenRect X, Y, mx-x, my-y,2
  354.                                         Case 0
  355.                                                 DrawOpenRect X+PREVIEW_X, Y+ PREVIEW_Y,  Width, Height,2
  356.                                 End Select
  357.                         End Method
  358. End Type
  359.  
  360.  
  361. Function DrawOpenRect(X%,Y%,Width%,Height%, Thick%)
  362.                 ' draws a non filled rectangle
  363.                 DrawRect X      , Y       , Width , Thick
  364.                 DrawRect X+Width-Thick, Y , Thick , Height
  365.                 DrawRect X, Y+Height-Thick, Width , Thick
  366.                 DrawRect X      , Y       , Thick , Height
  367. End Function
  368.  
  369.  
  370. Function SetStartupThreshold(StartValue%)
  371.                 ' sets a startup threshold over the whole picture
  372.                 ThresHold = Max(0,Min(255,StartValue))
  373.                 For Local y%=0 To 799
  374.                         For Local x%=0 To 599
  375.                                 Thresholds[x,y]=Threshold
  376.                         Next
  377.                 Next
  378.                 ManipulatePreviewPixmap
  379.                 DoFinalThreshold()     
  380. End Function
  381.  
  382.  
  383. Function CreateGui()
  384.                 Local flags%=WINDOW_DEFAULT|WINDOW_CLIENTCOORDS
  385.                 Window= CreateWindow("GRAYSCALE-SCANNER ", 50,50,1800,1000, Null , WINDOW_DEFAULT|WINDOW_CLIENTCOORDS)
  386.                 Canvas=CreateCanvas(0 , 0 , GadgetWidth(Desktop()), GadgetHeight(Desktop()) , Window)
  387.                 SetGadgetLayout Canvas , 1,1,1,1
  388.                 Local Menu:TGadget= CreateMenu("File",0, WindowMenu(Window))
  389.                 CreateMenu ("Load" , MENU_LOAD, Menu,0,0)
  390.                 CreateMenu ("Save" , MENU_SAVE, Menu,0,0)
  391.                 UpdateWindowMenu window
  392. End Function
  393.  
  394.  

« Last Edit: March 22, 2021, 14:18:30 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 364
    • Midimaster Music Education Software
Tutorial Description of the part of the code above
« Reply #1 on: March 22, 2021, 13:57:09 »
Here are some interesting parts of the code above with explanations:

Thanks to Derron for his support in speeding up the performance of the code.

The threshold values are stored in an 600x800 array. This means each pixel in the preview window has its own threshold level.
Code: BlitzMax
  1. Function SetStartupThreshold(StartValue%)
  2.                 ' sets a startup threshold over the whole picture
  3.                 ThresHold = Max(0,Min(255,StartValue))
  4.                 For Local y%=0 To 799
  5.                         For Local x%=0 To 599
  6.                                 Thresholds[x,y]=Threshold
  7.                         Next
  8.                 Next
  9. End Function
  10.  
  11. Function SetMouseThresHold(Direction%) 
  12.                 ' listen to the mousewheel and updates the treshold array and the preview window
  13.                 Threshold = Max(0, Min(255, Threshold + Direction))
  14.                 FixPartialThresHold
  15. End Function    
  16.  
  17.  
  18. Function FixPartialThresHold()
  19.                 ' changes the threshold array inside the limit of the mouse-rect
  20.                 If MausRect.State>0 Return
  21.                 For Local x%=0 To MausRect.Width-1
  22.                         For Local y%=0 To MausRect.Height-1
  23.                                 Thresholds[MausRect.X+X,MausRect.Y+Y] = Threshold
  24.                         Next
  25.                 Next
  26. End Function
  27.  


The Thresholds%[]-array  is the base for calculation the preview scan window:


Code: BlitzMax
  1. Function DrawPreviewImage()
  2.                 SetViewport PREVIEW_X,PREVIEW_Y,600,800
  3.                 SetColor 255,255,255
  4.                 DrawImage PreviewImage,PREVIEW_X,PREVIEW_Y
  5. End Function
  6.  
  7. Function ManipulatePreviewPixmap()
  8.                 'changes the preview window depending on the threshold array
  9.                 For Local y%=0 To PreviewHeight-1
  10.                         For Local x%=0 To PreviewWidth-1
  11.                                 Local r:Byte= PreviewBackup.ReadPixel(X,Y)
  12.                                 If r>ThresHolds[X,Y]
  13.                                         Preview.WritePixel(X,Y,255)
  14.                                 Else
  15.                                         Preview.WritePixel(X,Y,0)
  16.                                 EndIf
  17.                         Next
  18.                 Next
  19.                 PreviewImage=LoadImage(Preview)
  20. End Function
  21.  

In the main code you will find a version of the function  ManipulatePreviewPixmap(), which is 3 times faster because it uses direct Byte-Pointer.

The big main image is processed the same way, but only if the user presses the right mouse. That's because the performance of the preview is below 10msec, but a 30000x10000pix image needs 1000msec.



To optimize the two windows on the screen the source image will be turned around if it is in landscape mode. So here is another interesting algo to turn pixmaps 90°:
Code: BlitzMax
  1. Function TurnPixMap:TPixmap(Source:TPixmap,Direction%=1)
  2.         ' turn pixmaps from landscape to portrait mode an back
  3.         ' direction: 1=90° to the right, -1=90° to the left
  4.        
  5.         Local Target:TPixmap, H%, W%, TargetPitch%, SourcePitch%
  6.        
  7.         Target=CreatePixmap(Source.Height,Source.Width,Source.Format)
  8.         Target.ClearPixels(0)
  9.         TargetPitch=Target.Pitch
  10.         SourcePitch=Source.Pitch       
  11.         H=Target.Height
  12.         W=Target.Width
  13.  
  14.         For Local y%=0 To H-1
  15.                 For Local X%=0 To W-1
  16.                         If Direction=TURN_RIGHT
  17.                                 '90° right
  18.                                 Target.Pixels[(W-X-1) + Y*TargetPitch] = Source.Pixels[Y+X*SourcePitch]                        
  19.                         Else
  20.                                 '90° left
  21.                                 Target.Pixels[(H-Y-1)*TargetPitch + X] = Source.Pixels[Y+X*SourcePitch]                        
  22.                         EndIf
  23.                 Next
  24.         Next
  25.         Return Target
  26. End Function
  27.  

This  code again uses fast Byte-Pointer instead of ReaPixel()-function for a 3 times faster performance.


Another interesting part may be the displaying of the big image. As it is to big, you can only display a part of this 30000x20000pix image. So this function "cuts" a small image out of the pixmap:
Code: BlitzMax
  1. Function ShowResultImage()
  2.         'only displays the correct part of the big original pixmap depending on the mouse position
  3.         If Refresh=True
  4.                         Refresh=False
  5.                         Local X%=Max(0,Min(OriginalX , Original.Width-980))
  6.                         Local Y%=Max(0,Min(OriginalY , Original.Height-900))
  7.                         Local w%=Min(980,Original.Width)
  8.                         Local h%=Min(900,Original.Height)
  9.                         Small=LoadImage(PixmapWindow(Target,X,Y,w,h))
  10.         EndIf
  11. ....
  12.  


The last interesting code snipplet is the "elastic band algo" which displays a not filled rectangle in the canvas defined by the mouse:
Code: BlitzMax
  1. Global MausX%, MausY%
  2. Type WorkRect
  3.                         ' this type cares about the mouse-rectangle
  4.                         ' use: 1. before main loop once: create()
  5.                         '      2. first mousedown calls define() -> defines left top corner
  6.                         '      3. move around to find next corner
  7.                         '      4. release the mousebutton calls define() again -> defines right bottom corner
  8.                         '      5. next mousedown start again at 2.
  9.                         Field X%, Y%, Width%, Height%, State%=0
  10.                        
  11.                         Function Create:WorkRect()
  12.                                 Local loc:WorkRect=New WorkRect
  13.                                 loc.X=0
  14.                                 loc.Y=0
  15.                                 loc.Width=PreviewWidth
  16.                                 loc.Height=PreviewHeight
  17.                                 loc.State=0
  18.                                 Return loc
  19.                         End Function
  20.                        
  21.                        
  22.                         Method Define(Mx%, My%)
  23.                                 Select state
  24.                                         Case 0
  25.                                                 X=mx
  26.                                                 Y=my
  27.                                                 State=1
  28.                                         Case 1
  29.                                                 X=Max(0,X-PREVIEW_X)
  30.                                                 Y=Max(0,y-PREVIEW_Y)
  31.                                                 Width= Min(PreviewWidth,Mx-PREVIEW_X)-X
  32.                                                 Height=Min(PreviewHeight,My-PREVIEW_Y)-Y
  33.                                                 State=0
  34.                                 End Select             
  35.                         End Method      
  36.                        
  37.                        
  38.                         Method Draw()
  39.                                 'care about all states of drawing of the mouse rect
  40.                                 SetColor 255,255,0
  41.                                 Select State
  42.                                         Case 1
  43.                                                 Local mX% = Max(X+6,MausX)
  44.                                                 Local my% = Max(Y+6,MausY)
  45.                                                 DrawOpenRect X, Y, mx-x, my-y,2
  46.                                         Case 0
  47.                                                 DrawOpenRect X+PREVIEW_X, Y+ PREVIEW_Y,  Width, Height,2
  48.                                 End Select
  49.                         End Method
  50. End Type
  51.  
  52.  
  53. Function DrawOpenRect(X%,Y%,Width%,Height%, Thick%)
  54.                 ' draws a non filled rectangle
  55.                 DrawRect X      , Y       , Width , Thick
  56.                 DrawRect X+Width-Thick, Y , Thick , Height
  57.                 DrawRect X, Y+Height-Thick, Width , Thick
  58.                 DrawRect X      , Y       , Thick , Height
  59. End Function
  60.  

Feel free to ask questions and offer ideas to optimize the code.
« Last Edit: March 22, 2021, 14:32:29 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Derron

  • Hero Member
  • *****
  • Posts: 3674
Re: Paper Scan or Foto Scan Threshold optimization
« Reply #2 on: March 22, 2021, 16:37:25 »
As said: there should be no need to turn the pixmap 90°. The pixmap is the actual pixel data which is loaded from the image file - or maybe exported to one.
(Nonetheless the code is interesting)

If you just want to _draw_ an image rotated, use SetRotation(90) and you are done - GPU then handles rotating the _texture_ when presenting it to your beloved computer screen.


bye
Ron

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 364
    • Midimaster Music Education Software
Re: Paper Scan or Foto Scan Threshold optimization
« Reply #3 on: March 22, 2021, 17:28:34 »
As the user interacts with the displayed image, and the pixmap is always transformed to the same direction, it is less code to check the interaction when the pixmap is brought to the same direction.

It would be much more work to calculate the resulting coordinates of threshold array and preview pixmap and result pixmap, if the displayed image needs not to have the same direction as the pixmaps. Now you have to write a lot of different code lines for different orientations on many passages of the code.

As the preview always is 600x800, also the threshold array can stay at 600x800. and it is very comfortable, if now the original pixmap has the same direction. At the end when the user press save, the result pixmap is turned back to the original direction.
   
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Derron

  • Hero Member
  • *****
  • Posts: 3674
Re: Paper Scan or Foto Scan Threshold optimization
« Reply #4 on: March 22, 2021, 17:50:04 »
I thought of that too (like "precomputing"). Just ensure to really do it only "twice" - when loading and saving. It hogs a lot of RAM (30.000*10.000*bitsPerPixel). But yay, if it works ... then better skip trying to overoptimize stuff.


bye
Ron

Offline Ryszard

  • Jr. Member
  • **
  • Posts: 47
Re: Paper Scan or Foto Scan Threshold optimization
« Reply #5 on: March 22, 2021, 21:37:31 »
 Great.  Thank you very much I will test, see what the weak points are.  Maybe I'll do the saving in Tiff.

Offline Ryszard

  • Jr. Member
  • **
  • Posts: 47
Re: Paper Scan or Foto Scan Threshold optimization
« Reply #6 on: March 23, 2021, 19:58:36 »
Very cool that you add comments to the code. I will learn a little bit. It will be easier to understand the code if there is a comment after each part. It is not easy to find some easily digestible information on these topics.

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal