غالبًا ما تُستخدم عملية محاذاة الرسم البياني (زيادة التباين) لزيادة جودة الصورة. في إطار هذه المقالة ، سيتم النظر في مفهوم المدرج التكراري ، ونظرية الخوارزمية لمواءمتها ، والأمثلة العملية لتشغيل هذه الخوارزمية. لتسهيل معالجة الصور ، سنستخدم مكتبة OpenCV . أيضا ، سنقوم بإجراء تحليل مقارن لنتائج الخوارزمية الخاصة بنا والخوارزمية المضمنة بالفعل في OpenCV .

الرسم البياني هو دالة h (x) ، والتي تُرجع العدد الإجمالي لوحدات البكسل التي يساوي سطوعها x .
الرسم البياني h لصورة الألوان النصفية I يتم إعطاؤه بواسطة
، حيث يتوافق m مع فواصل قيم السطوع
يوجد أدناه رمز زائف لحساب الرسم البياني.
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;
};
}
بصريا ، الرسم البياني هو مستطيل ، عرضه يساوي أقصى قيمة ممكنة لسطوع نقطة في الصورة الأصلية. بالنسبة للصور ذات التدرج الرمادي ، سنعمل مع مجموعة من سطوع النقاط من 0 إلى 255 ، مما يعني أن عرض الرسم البياني سيكون مساوياً لـ 256. يمكن أن يكون ارتفاع الرسم البياني أيًا ، ولكن للوضوح ، سنعمل مع الرسوم البيانية المستطيلة.
من وجهة نظر المبرمج ، فإن الرسم البياني عبارة عن صفيف أحادي البعد من البعد 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.




كاتب المقالة أبعد ما يكون عن الأصل ولم يأت بأي شيء جديد ، يقدم للجمهور قصة أخرى حول كيفية تنفيذ خوارزمية محاذاة الرسم البياني ولماذا هناك حاجة إليها. لم يكن الغرض الرئيسي من المقالة هو التحدث فقط عن الخوارزمية ، ولكن أيضًا لإعطاء أمثلة عملية محددة ورموز مصدر يمكنك نسخها والتحقق منها بنفسك ، وربما تجربتها. بالطبع ، خوارزمية الرسم البياني ليست هي الوحيدة المستخدمة لتصفية الصور. ولكن هذا هو موضوع قضايا أخرى.