Page 1 of 1

2D: Rate image sharpness

Posted: Mon Jul 29, 2024 10:02 am
by dige
Hi folks, while sorting and deleting holiday photos, I had the idea that it would be very helpful to have a sharpness rating for each picture. Especially if a series was taken and only the best (sharpest?) image should be kept.

So far I have used the image size for this, because the JPG encoder can compress blurred images better and in this case the image with the largest file size is the sharpest.

Has anyone ever made such considerations or had experience with them that could be shared here?

The AI claims there are the following 3 methods:

- Laplacian variance method
- Tenengrad method
- Brenner gradient method

The functions spit out completely different values, which would still have to be transformed to a scale.

What do you think, does that make sense?

Code: Select all

UseJPEGImageDecoder()

; Diese Methode berechnet die Summe der quadratischen Differenzen zwischen benachbarten Pixeln.

Procedure.d CalculateBrennerGradient(ImageID)
  Protected x, y, brenner, gradient
  Protected sum.d, count.d

  If StartDrawing(ImageOutput(ImageID))
    For y = 0 To ImageHeight(ImageID) - 1
      For x = 0 To ImageWidth(ImageID) - 3
        gradient = Red(Point(x + 2, y)) - Red(Point(x, y))
        brenner + gradient * gradient
        count + 1
      Next
    Next
    StopDrawing()
  EndIf

  ProcedureReturn brenner
EndProcedure

; Berechnet die Laplacian-Varianz des Bildes.

Procedure.d CalculateLaplacianVariance(ImageID)
  Protected x, y, pixel, laplacian, variance, mean
  Protected Dim laplaceArray.d(1023, 1023)
  Protected sum.d, sumSq.d, count.d

  If StartDrawing(ImageOutput(ImageID))
    For y = 1 To ImageHeight(ImageID) - 2
      For x = 1 To ImageWidth(ImageID) - 2
        ; Laplacian kernel: [[0, 1, 0], [1, -4, 1], [0, 1, 0]]
        laplacian = Red(Point(x, y + 1)) + Red(Point(x, y - 1)) + Red(Point(x + 1, y)) + Red(Point(x - 1, y)) - 4 * Red(Point(x, y))
        laplaceArray(x, y) = laplacian
        sum + laplacian
        sumSq + laplacian * laplacian
        count + 1
      Next
    Next
    StopDrawing()
  EndIf

  mean = sum / count
  variance = (sumSq / count) - (mean * mean)
  ProcedureReturn variance
EndProcedure

; Tenengrad Methode:
; Diese Methode verwendet den Sobel-Operator, um die Kanten im Bild zu erkennen und die Schärfe zu bewerten.

Procedure.d CalculateTenengrad(ImageID)
  Protected x, y, gx, gy, gradient, meanGradient
  Protected sum.d, count.d

  If StartDrawing(ImageOutput(ImageID))
    For y = 1 To ImageHeight(ImageID) - 2
      For x = 1 To ImageWidth(ImageID) - 2
        ; Sobel kernels
        gx = -Red(Point(x - 1, y - 1)) - 2 * Red(Point(x - 1, y)) - Red(Point(x - 1, y + 1)) + Red(Point(x + 1, y - 1)) + 2 * Red(Point(x + 1, y)) + Red(Point(x + 1, y + 1))
        gy = -Red(Point(x - 1, y - 1)) - 2 * Red(Point(x, y - 1)) - Red(Point(x + 1, y - 1)) + Red(Point(x - 1, y + 1)) + 2 * Red(Point(x, y + 1)) + Red(Point(x + 1, y + 1))
        gradient = Sqr(gx * gx + gy * gy)
        sum + gradient
        count + 1
      Next
    Next
    StopDrawing()
  EndIf

  meanGradient = sum / count
  ProcedureReturn meanGradient
EndProcedure

Procedure.d NormalizeValue(value.d, min.d, max.d, newMin.d, newMax.d)
  ProcedureReturn (value - min) / (max - min) * (newMax - newMin) + newMin
EndProcedure


OpenWindow(0, 0, 0, 900, 800, "")


; Load and convert image to grayscale
LoadImage(0, "C:\Temp\test_blur.jpg")
ImageGadget(0, 10, 10, ImageWidth(0), ImageHeight(0), ImageID(0))
;ConvertImage(0, #PB_Image_Grayscale)

meanGradient = CalculateTenengrad(0)
Debug "Tenengrad: " + StrD(meanGradient)


variance = CalculateLaplacianVariance(0)
Debug "Laplacian Varianz: " + StrD(variance)


brenner = CalculateBrennerGradient(0)
Debug "Brenner Gradient: " + StrD(brenner)


LoadImage(0, "C:\Temp\test_sharp.jpg")
ImageGadget(0, 10, 10, ImageWidth(0), ImageHeight(0), ImageID(0))

meanGradient = CalculateTenengrad(0)
Debug "Tenengrad: " + StrD(meanGradient)


variance = CalculateLaplacianVariance(0)
Debug "Laplacian Varianz: " + StrD(variance)

brenner = CalculateBrennerGradient(0)
Debug "Brenner Gradient: " + StrD(brenner)




Re: 2D: Rate image sharpness

Posted: Mon Jul 29, 2024 1:04 pm
by RASHAD
Hi dige
I used image and blurred copy of the same image
Tested with your snippet
I preferred Tenengrad method :)

Re: 2D: Rate image sharpness

Posted: Tue Jul 30, 2024 8:57 am
by dige
Thx for testing, RASHAD :D

I have now found another use case where the rating was very helpful. I have pictures from my mobile phone in .HEIC format and wanted to check how high the loss of image quality is when I convert them to JPG. And which image converter does the best job.

While Tenengrad found no difference, Laplacian Variance clearly showed that even JPG with 100% quality does not match the original.

And Irfanview was the best converter in the end, which I will now integrate via batch...

Re: 2D: Rate image sharpness

Posted: Tue Jul 30, 2024 12:20 pm
by RASHAD
You are absolutely right
Tested with different JPG compression

Code: Select all

UseJPEGImageDecoder()

; Diese Methode berechnet die Summe der quadratischen Differenzen zwischen benachbarten Pixeln.

Procedure.d CalculateBrennerGradient(ImageID)
  Protected x, y, brenner, gradient
  Protected sum.d, count.d
  
  If StartDrawing(ImageOutput(ImageID))
    For y = 0 To ImageHeight(ImageID) - 1
      For x = 0 To ImageWidth(ImageID) - 3
        gradient = Red(Point(x + 2, y)) - Red(Point(x, y))
        brenner + gradient * gradient
        count + 1
      Next
    Next
    StopDrawing()
  EndIf
  
  ProcedureReturn brenner
EndProcedure

; Berechnet die Laplacian-Varianz des Bildes.

Procedure.d CalculateLaplacianVariance(ImageID)
  Protected x, y, pixel, laplacian, variance, mean 
  Protected sum.d, sumSq.d, count.d
  
  If ImageWidth(imageid) > ImageHeight(imageid)
    size = ImageWidth(imageid)
  Else
    size = ImageHeight(imageid)
  EndIf
  
  Protected Dim laplaceArray.d(size,size)    
  
  If StartDrawing(ImageOutput(ImageID))
    For y = 1 To ImageHeight(ImageID) - 2
      For x = 1 To ImageWidth(ImageID) - 2
        ; Laplacian kernel: [[0, 1, 0], [1, -4, 1], [0, 1, 0]]
        laplacian = Red(Point(x, y + 1)) + Red(Point(x, y - 1)) + Red(Point(x + 1, y)) + Red(Point(x - 1, y)) - 4 * Red(Point(x, y))
        laplaceArray(x, y) = laplacian
        sum + laplacian
        sumSq + laplacian * laplacian
        count + 1
      Next
    Next
    StopDrawing()
  EndIf
  
  mean = sum / count
  variance = (sumSq / count) - (mean * mean)
  ProcedureReturn variance
EndProcedure

; Tenengrad Methode:
; Diese Methode verwendet den Sobel-Operator, um die Kanten im Bild zu erkennen und die Schärfe zu bewerten.

Procedure.d CalculateTenengrad(ImageID)
  Protected x, y, gx, gy, gradient, meanGradient
  Protected sum.d, count.d
  
  If StartDrawing(ImageOutput(ImageID))
    For y = 1 To ImageHeight(ImageID) - 2
      For x = 1 To ImageWidth(ImageID) - 2
        ; Sobel kernels
        gx = -Red(Point(x - 1, y - 1)) - 2 * Red(Point(x - 1, y)) - Red(Point(x - 1, y + 1)) + Red(Point(x + 1, y - 1)) + 2 * Red(Point(x + 1, y)) + Red(Point(x + 1, y + 1))
        gy = -Red(Point(x - 1, y - 1)) - 2 * Red(Point(x, y - 1)) - Red(Point(x + 1, y - 1)) + Red(Point(x - 1, y + 1)) + 2 * Red(Point(x, y + 1)) + Red(Point(x + 1, y + 1))
        gradient = Sqr(gx * gx + gy * gy)
        sum + gradient
        count + 1
      Next
    Next
    StopDrawing()
  EndIf
  
  meanGradient = sum / count
  ProcedureReturn meanGradient
EndProcedure

Procedure.d NormalizeValue(value.d, min.d, max.d, newMin.d, newMax.d)
  ProcedureReturn (value - min) / (max - min) * (newMax - newMin) + newMin
EndProcedure

;Load And convert image To grayscale
LoadImage(0, GetTemporaryDirectory()+"test100.jpg")
OpenWindow(0, 0, 0, ImageWidth(0)+20,ImageHeight(0)+20, "",#PB_Window_ScreenCentered)
ImageGadget(0, 10, 10, ImageWidth(0), ImageHeight(0), ImageID(0))
;ConvertImage(0, #PB_Image_Grayscale)

meanGradient = CalculateTenengrad(0)
Debug "Tenengrad: " + StrD(meanGradient)

variance = CalculateLaplacianVariance(0)
Debug "Laplacian Varianz: " + StrD(variance)

brenner = CalculateBrennerGradient(0)
Debug "Brenner Gradient: " + StrD(brenner)

LoadImage(0, GetTemporaryDirectory()+"test70.jpg")
ImageGadget(0, 10, 10, ImageWidth(0), ImageHeight(0), ImageID(0))

meanGradient = CalculateTenengrad(0)
Debug "Tenengrad: " + StrD(meanGradient)

variance = CalculateLaplacianVariance(0)
Debug "Laplacian Varianz: " + StrD(variance)

brenner = CalculateBrennerGradient(0)
Debug "Brenner Gradient: " + StrD(brenner)