Operasi penyelarasan histogram (meningkatkan kontras) sering digunakan untuk meningkatkan kualitas gambar. Dalam kerangka kerja artikel ini, konsep histogram , teori algoritma untuk penyelarasannya, dan contoh-contoh praktis dari pengoperasian algoritma ini akan dipertimbangkan . Untuk memfasilitasi pemrosesan gambar, kami akan menggunakan pustaka OpenCV . Selain itu, kami akan membuat analisis komparatif dari hasil algoritma kami dan algoritma yang sudah ada di dalam OpenCV .

Histogram adalah fungsi h (x) , yang mengembalikan jumlah total piksel yang kecerahannya sama dengan x .
Histogram h dari gambar halftone I diberikan oleh
, di mana m sesuai dengan interval nilai kecerahan
Di bawah ini adalah pseudo-code untuk menghitung histogram.
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;
};
}
Secara visual, histogram adalah persegi panjang, lebarnya sama dengan nilai maksimum yang dimungkinkan dari kecerahan suatu titik pada gambar asli. Untuk gambar skala abu-abu, kami akan bekerja dengan kisaran kecerahan titik dari 0 hingga 255, yang berarti bahwa lebar histogram akan sama dengan 256. Tinggi histogram dapat berupa apa saja, tetapi untuk kejelasan, kami akan bekerja dengan histogram persegi panjang.
, — 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.




Penulis artikel ini jauh dari aslinya dan tidak datang dengan sesuatu yang baru, menyajikan kepada publik cerita lain tentang bagaimana algoritma penyelarasan histogram diimplementasikan dan mengapa itu diperlukan. Tujuan utama artikel ini tidak hanya untuk berbicara tentang algoritma, tetapi juga untuk memberikan contoh-contoh praktis spesifik dan kode sumber yang dapat Anda salin dan verifikasi sendiri, dan mungkin percobaan. Tentu saja, algoritma histogram bukan satu-satunya yang digunakan untuk memfilter gambar. Tapi ini adalah topik masalah lainnya.