Die Histogrammausrichtungsoperation (zunehmender Kontrast) wird hĂ€ufig verwendet, um die BildqualitĂ€t zu erhöhen. Im Rahmen dieses Artikels werden das Konzept eines Histogramms , die Theorie eines Algorithmus fĂŒr seine Ausrichtung und praktische Beispiele fĂŒr die Funktionsweise dieses Algorithmus betrachtet . Um die Bildverarbeitung zu vereinfachen, verwenden wir die OpenCV-Bibliothek . AuĂerdem werden wir eine vergleichende Analyse der Ergebnisse unseres Algorithmus und des Algorithmus durchfĂŒhren, der bereits in OpenCV integriert ist .

Das Histogramm ist eine Funktion h (x) , die die Gesamtzahl der Pixel zurĂŒckgibt, deren Helligkeit gleich x ist .
Das Histogramm h des Halbtonbildes I ist gegeben durch
wobei m den Intervallen der Helligkeitswerte entspricht
Unten finden Sie den Pseudocode zur Berechnung des Histogramms.
procedure histogram(I,H);
{
for i := 0 to MaxVal
H[i] := 0;
for L := 0 to MaxRow
for P := 0 to MaxCol {
grayval := I[r,c];
H[grayval] := H[grayval] + 1;
};
}
Visuell ist das Histogramm ein Rechteck, dessen Breite dem maximal möglichen Wert der Helligkeit eines Punkts im Originalbild entspricht. FĂŒr Graustufenbilder arbeiten wir mit einem Helligkeitsbereich von Punkten von 0 bis 255, was bedeutet, dass die Breite des Histogramms 256 betrĂ€gt. Die Höhe des Histogramms kann beliebig sein, aber aus GrĂŒnden der Klarheit werden wir mit rechteckigen Histogrammen arbeiten.
, â 256 ( ), . .
1. h(x)

. â cdf(x) .
, OpenCV , , . , , . F# OpenCVSharp4.
, Visual Studio, . , MacOS, , "runtimes/osx-x64/native/libOpenCvSharpExtern.dylib" , .
Unable to load shared library 'OpenCvSharpExtern' or one of its dependencies
,
open OpenCvSharp
open Microsoft.FSharp.NativeInterop
// mat - Mat OpenCV
let getHistogram (mat: Mat) =
//
let hx = Array.zeroCreate<int> 256
//
mat.ForEachAsByte(fun value _ ->
let v = int (NativePtr.get value 0)
hx.[v] <- hx.[v] + 1)
//
hx
, . X
, X. 1 ( ).
, cdf(x)
let getCdx hx = // hx -
//
hx |> Array.mapi (fun x _ -> if x > 0 then hx.[0..(x-1)] |> Array.sum else 0)
1 , . ( ), . , , .
2.
2.

, .
:
cdf(x) â X
cdf_min â ,
pixels â
255 â . 255
round â
F# :
// cdf_min
let cdxMin = cdx |> Array.filter (fun v -> v > 0) |> Array.min
//
let totalPixels = src.Rows * src.Cols // src - Mat
for y in 0..src.Rows do
for x in 0..src.Cols do
//
let s = int(src.At<byte>(y, x))
//
let fx = (float(cdx.[s]) - float(cdxMin))/(float(totalPixels - 1))*255.
//
equalizeImage.Circle(x, y, 1, new Scalar(double(fx)))
, . . , OpenCV .
// Learn more about F# at http://fsharp.org
open OpenCvSharp
open Microsoft.FSharp.NativeInterop
//
let getHistogram (mat: Mat) =
let hx = Array.zeroCreate<int> 256
mat.ForEachAsByte(fun value _ ->
let v = int (NativePtr.get value 0)
hx.[v] <- hx.[v] + 1)
hx
//
let getCdx hx =
hx |> Array.mapi (fun x _ -> if x > 0 then hx.[0..(x-1)] |> Array.sum else 0)
//
let drawHistogramAndCdx hx cdx (mat: Mat) =
let histoWidth = 256
let histoHeight = 256
//
let cdxMax = cdx |> Array.max
// ( )
//
let cdxK = float(histoHeight)/float(cdxMax)
let histMax = hx |> Array.max
let histK = float(histoHeight)/float(histMax)
let histMat = new Mat(histoWidth, histoHeight, MatType.CV_8UC4)
hx
|> Array.iteri (fun x v ->
let histDy = int(float(v)*histK)
let cdxDy = int(float(cdx.[x])*cdxK)
// h(x)
mat.Line(x, histoHeight-1, x, histoHeight-1-histDy, Scalar.White)
// cdx(x)
mat.Circle(x, histoHeight-cdxDy, 1, Scalar.Blue))
[<EntryPoint>]
let main argv =
let histoWidth = 256
let histoHeight = 256
let src = Cv2.ImRead("cat.jpg", ImreadModes.Grayscale)
let equalizeImage = new Mat(src.Rows, src.Cols, MatType.CV_8UC1)
// calculate histogram h(x)
let hx = getHistogram src
// calculate cdf(x) = h(0) + h(1) + .. + h(x)
let cdx = getCdx hx
// draw histogram
let histMat = new Mat(histoWidth, histoHeight, MatType.CV_8UC4)
drawHistogramAndCdx hx cdx histMat
// equalize the histogram
let cdxMin = cdx |> Array.filter (fun v -> v > 0) |> Array.min
let totalPixels = src.Rows * src.Cols
for y in 0..src.Rows do
for x in 0..src.Cols do
let s = int(src.At<byte>(y, x))
let fx = (float(cdx.[s]) - float(cdxMin))/(float(totalPixels - 1))*255.
//equalizeImage.Set<Scalar>(y, x, new Scalar(double(fx)))
equalizeImage.Circle(x, y, 1, new Scalar(double(fx)))
// calculate equalize histogram
let hx2 = getHistogram equalizeImage
let cdx2 = getCdx hx2
let histMat2 = new Mat(histoWidth, histoHeight, MatType.CV_8UC4)
drawHistogramAndCdx hx2 cdx2 histMat2
// opencv equalize histogram
let opencCVImage = new Mat(src.Rows, src.Cols, MatType.CV_8UC1)
let in1 = InputArray.Create(src)
let in2 = OutputArray.Create(opencCVImage)
Cv2.EqualizeHist(in1, in2)
// get opencv histogram
let hx3 = getHistogram opencCVImage
let cdx3 = getCdx hx3
let histMat3 = new Mat(histoWidth, histoHeight, MatType.CV_8UC4)
drawHistogramAndCdx hx3 cdx2 histMat3
// show results
use w1 = new Window("original image", src)
use w2 = new Window("original histogram", histMat)
use w3 = new Window("custom equalize image", equalizeImage)
use w4 = new Window("custom equalize histogram", histMat2)
use w5 = new Window("opencv equalize image", opencCVImage)
use w6 = new Window("opencv equalize histogram", histMat3)
Cv2.WaitKey() |> ignore
0 // return an integer exit code
3.
, , OpenCV.
( ), â , â OpenCV.




Der Autor des Artikels ist alles andere als originell und hat nichts Neues erfunden. Er prĂ€sentierte der Ăffentlichkeit eine andere Geschichte darĂŒber, wie der Histogramm-Ausrichtungsalgorithmus implementiert wird und warum er benötigt wird. Der Hauptzweck des Artikels bestand nicht nur darin, ĂŒber den Algorithmus zu sprechen, sondern auch spezifische praktische Beispiele und Quellcodes anzugeben, die Sie selbst kopieren und ĂŒberprĂŒfen und möglicherweise experimentieren können. NatĂŒrlich ist der Histogrammalgorithmus nicht der einzige, der zum Filtern von Bildern verwendet wird. Dies ist jedoch das Thema anderer Themen.