F#,二进制图像的形态

介绍


应用于二值图像时,形态学用于描述该图像的任何属性。数学形态学的运算用于集合的变换。在这方面,它们方便地用于处理二进制图像并进行后续分析。


本文将讨论F#上下文中数学(二进制)形态的基本操作。并且,在本文的结尾,我将给出一个结果分析的示例,该示例可用于机器视觉算法中。


本文将使用我在前一篇文章中讨论的有关相关组件的标记算法分析的材料。您可以在这里熟悉本文:F#,一种用于标记图像的已连接组件的算法


二进制形态的基本操作


为了进行二进制形态运算,我们需要


  • 原始二进制图像(B)
  • 结构元素(S)
    结构元素是对某些形状区域的描述。该区域可以具有任何大小和形状,可以表示为二进制图像

以下是一些矩形,圆盘和圆环形式的结构元素示例。


Box(3,5)=


Disk(5,5)=0000


Ring(5,5)=0000000000000


结构元素是一些遮罩,用于定义将在其上执行二进制形态学操作的图像区域。


结构元素也有一个起点通常它位于面罩的中央,但可以在任何地方。结构元素的原点必须与二进制图像的当前像素一致,以便对其进行转换。


考虑二进制形态的基本操作。


B S B ⊕ S

BS=bBSb


. S . , , .


.


Figure 1: B


00000000000000000000000000000000000000


Figure 2: S



Figure 3: B ⊕ S


00000000


F#, .


let (>.) value checkValue = if value > checkValue then checkValue else value
let (<.) value checkValue = if value < checkValue then checkValue else value

let subArrayOfMaks array2d mask (centerX, centerY) (maskCenterX, maskCenterY) = 
        let (rows, cols) = Matrix.sizes {values = array2d}
        let (maskRows, maskCols) = Matrix.sizes {values = mask}

        let x1 = centerX - maskCenterX
        let y1 = centerY - maskCenterY
        let x2 = x1 + maskCols - 1
        let y2 = y1 + maskRows - 1

        (x1 <. 0, x2 >. (cols - 1), y1 <. 0, y2 >. (rows - 1))

let upbuilding array2d mask (maskCenterX, maskCenterY) =

        let sub source mask (x, y) (maskCenterX, maskCenterY) =
            let (x1, x2, y1, y2) = subArrayOfMaks source mask (x, y) (maskCenterX, maskCenterY)
            source.[y1..y2, x1..x2] <- mask

        let copy = (Matrix.cloneO {values = array2d}).values
        array2d |> Array2D.iteri (fun y x v -> if v = 1 then sub copy mask (x, y) (maskCenterX, maskCenterY))

        copy

upbuilding


let upbuilding array2d mask (maskCenterX, maskCenterY) =

, .


>. <.


let (>.) value checkValue = if value > checkValue then checkValue else value
let (<.) value checkValue = if value < checkValue then checkValue else value


value
checkValue — ( )


value ( ) checkValue, checkValue, — value. nil Swift (??), , ( ).
,


2 <. 3 //  3


3 <. 2 //  3

, .


subArrayOfMaks


let subArrayOfMaks array2d mask (centerX, centerY) (maskCenterX, maskCenterY) = 

.


array2d — ( )
mask — ()
(centerX, centerY) — ,
(maskCenterX, maskCenterY) — . , . , .


upbuilding


array2d
mask
(maskCenterX, maskCenterY)


let upbuilding array2d mask (maskCenterX, maskCenterY) =

        //      ,        
        let sub source mask (x, y) (maskCenterX, maskCenterY) =
            //    
            let (x1, x2, y1, y2) = subArrayOfMaks source mask (x, y) (maskCenterX, maskCenterY)
            //    
            source.[y1..y2, x1..x2] <- mask

        //    ,  
        let copy = (Matrix.cloneO {values = array2d}).values
        //      
        array2d |> Array2D.iteri (fun y x v -> if v = 1 then sub copy mask (x, y) (maskCenterX, maskCenterY))

        //    
        copy

, .


B S B ⊖ S

BS=b|b+sBsS


"" . , .


( , Figure 1).


Figure 4: B ⊖ S


0000000000000000000000000000000000000000000000000000000000


F#. , F#


//    ,     array2d   mask?
let contains array2d mask =
        if Matrix.isEquallySized (Matrix.ofArray2D array2d) (Matrix.ofArray2D mask) then
            not (array2d
                 |> Array2D.mapi (fun x y v -> if mask.[x, y ] = 0 then 1
                                               elif mask.[x, y] = v then 1
                                               else 0)
                 |> Seq.cast<int>
                 |> Seq.contains 0)
        else false

// ,   
let erosion array2d mask (maskCenterX, maskCenterY) =

        let sub source (dest: int [,]) mask (x, y) (maskCenterX, maskCenterY) =
            let (x1, x2, y1, y2) = subArrayOfMaks source mask (x, y) (maskCenterX, maskCenterY)
            let subArray2d = source.[y1..y2, x1..x2]

            if contains subArray2d mask then
                dest.[y, x] <- 1

        let copy = (Matrix.cloneO {values = array2d}).values
        array2d |> Array2D.iteri (fun y x v -> if v = 1 then sub array2d copy mask (x, y) (maskCenterX, maskCenterY))

        copy

, . , , .


.


B S B • S

BS=(BS)S


F#


let closure array2d mask (centerX, centerY) = 
        let step1 = upbuilding array2d mask (centerX, centerY)
        let step2 = erosion step1 mask (centerX, centerY)

        step2


Figure 5: B • S


000000000000000000000000000000000000


B S B ◦ S

BS=(BS)S


F#


let opening array2d mask (centerX, centerY) = 
        let step1 = erosion array2d mask (centerX, centerY)
        let step2 = upbuilding step1 mask (centerX, centerY)

        step2

Figure 6: B ◦ S


00000000000000000000000000000000000000000000



, , , . , . , .


. , . , .


Figure 7:


000000000000000000000000000000000000000000000000000000000000000000000000000000000


Figure 8:



, , ( , ). ,


Figure 9:


000000000000000000000000000000000000000000000000000000000000000000000000000000000000000


F#


//    
let origin = array2D [[0;0;0;0;0;0;0;0;0;0]
                      [0;0;1;0;0;0;0;0;0;0]
                      [0;1;1;1;0;0;0;1;0;0]
                      [0;0;0;0;0;0;0;0;1;0]
                      [0;0;0;0;1;0;0;0;0;0]
                      [0;0;0;0;1;1;0;0;1;0]
                      [0;0;0;1;1;1;1;0;1;0]
                      [0;0;0;1;0;0;0;0;1;0]
                      [0;0;0;0;0;0;0;1;1;0]
                      [0;0;0;0;0;0;0;0;0;0]]

//  
let mask = array2D [[1]
                    [1]
                    [1]]

//      
let marked = Algorithms.markingOfConnectedComponents origin
//  
let erosion = Algorithms.erosion origin mask (0, 1)

printfn "origin = \n %A" origin
printfn "marked = \n %A" marked
printfn "erosion =\n %A" erosion

//        .       ()
let set = erosion |> Seq.cast<int> |> Seq.filter ((<>)0) |> Set.ofSeq

//     ,     
let copy = (Matrix.cloneO {values = marked}).values

//         
erosion
|> Array2D.iteri (fun row col v -> if v = 1 then
                            //     
                            let label = marked.[row, col]
                            if not (set.Contains label) then
                                //       ,       
                                marked |> Array2D.iteri (fun row1 col1 v1 -> if v1 = label then copy.[row1, col1] <- 1)
                         )

printfn "copy =\n %A" copy


origin = 
 [[0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 1; 0; 0; 0; 0; 0; 0; 0]
 [0; 1; 1; 1; 0; 0; 0; 1; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 1; 0]
 [0; 0; 0; 0; 1; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 1; 1; 0; 0; 1; 0]
 [0; 0; 0; 1; 1; 1; 1; 0; 1; 0]
 [0; 0; 0; 1; 0; 0; 0; 0; 1; 0]
 [0; 0; 0; 0; 0; 0; 0; 1; 1; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]]
marked = 
 [[0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 1; 0; 0; 0; 0; 0; 0; 0]
 [0; 1; 1; 1; 0; 0; 0; 3; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 4; 0]
 [0; 0; 0; 0; 5; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 5; 5; 0; 0; 6; 0]
 [0; 0; 0; 5; 5; 5; 5; 0; 6; 0]
 [0; 0; 0; 5; 0; 0; 0; 0; 6; 0]
 [0; 0; 0; 0; 0; 0; 0; 6; 6; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]]
erosion =
 [[0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 1; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 1; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 1; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]]
copy =
 [[0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 1; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 1; 1; 0; 0; 1; 0]
 [0; 0; 0; 1; 1; 1; 1; 0; 1; 0]
 [0; 0; 0; 1; 0; 0; 0; 0; 1; 0]
 [0; 0; 0; 0; 0; 0; 0; 1; 1; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]]

, , ,


F#


Figure 10:


A=00000,B=000000


A B

AB


Figure 11:


0000


A B

AB


Figure 12:


0000000


A

Ac


Figure 13:


0000


A B

A/B


Figure 13:


0000000


F#


// 
let union array2d1 array2d2 = 
        if Matrix.isEquallyArraySized array2d1 array2d2 then
            array2d1 |> Array2D.mapi (fun x y v -> v ||| array2d2.[x, y])
        else failwith "array2d1 is not equal to array2d2"

// 
let intersection array2d1 array2d2 = 
        if Matrix.isEquallyArraySized array2d1 array2d2 then
            array2d1 |> Array2D.mapi (fun x y v -> v &&& array2d2.[x, y])
        else failwith "array2d1 is not equal to array2d2"

//  (    )
let inverse array2d =
        array2d |> Array2D.mapi (fun _ _ v -> v ^^^ 1)

let complement array2d = 
        inverse array2d

// 
let difference array2d1 array2d2 = 
        if Matrix.isEquallyArraySized array2d1 array2d2 then
            array2d1 |> Array2D.mapi (fun x y v -> if v <> array2d2.[x, y] then v else 0)
        else failwith "array2d1 is not equal to array2d2"


, . , . .


Figure 14:


000000000000000000000000100000000000


Figure 15:


0000


F#


let borderAllocation array2d mask (maskCenterX, maskCenterY) = 
        let e = erosion array2d mask (maskCenterX, maskCenterY)
        let border = difference array2d e
        border


000000000000000000000000000000010000000000000000



. , . , . , , . , . , , .


, , : ,


0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000


F#


//     
    let multyBinImage = array2D [[0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0]
                                 [0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0]
                                 [0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0]
                                 [0;0;0;0;0;0;0;0;0;0;1;1;1;1;0;0]
                                 [1;1;1;1;0;0;0;0;0;1;1;1;1;1;1;0]
                                 [1;1;1;1;0;0;0;0;1;1;1;1;1;1;1;1]
                                 [1;1;1;1;0;0;0;0;1;1;1;1;1;1;1;1]
                                 [1;1;1;1;0;0;0;0;1;1;1;1;1;1;1;1]
                                 [1;1;1;1;0;0;0;0;0;1;1;1;1;1;1;0]
                                 [1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;0]
                                 [1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;0]
                                 [1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;0]
                                 [1;1;1;1;0;0;1;1;1;0;0;0;0;0;0;0]
                                 [1;1;1;1;0;0;1;1;1;0;0;0;0;0;0;0]
                                 [1;1;1;1;0;0;1;1;1;0;0;0;0;0;0;0]
                                 [1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;0]]

,


//   
let markedMultyImages = Algorithms.markingOfConnectedComponents mulyBinImage
printfn "marked = \n %A" markedMultyImages


marked = 
 [[0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 1; 1; 1; 1; 0; 0]
 [2; 2; 2; 2; 0; 0; 0; 0; 0; 1; 1; 1; 1; 1; 1; 0]
 [2; 2; 2; 2; 0; 0; 0; 0; 1; 1; 1; 1; 1; 1; 1; 1]
 [2; 2; 2; 2; 0; 0; 0; 0; 1; 1; 1; 1; 1; 1; 1; 1]
 [2; 2; 2; 2; 0; 0; 0; 0; 1; 1; 1; 1; 1; 1; 1; 1]
 [2; 2; 2; 2; 0; 0; 0; 0; 0; 1; 1; 1; 1; 1; 1; 0]
 [2; 2; 2; 2; 0; 0; 0; 0; 0; 0; 1; 1; 1; 1; 0; 0]
 [2; 2; 2; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [2; 2; 2; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
 [2; 2; 2; 2; 0; 0; 5; 5; 5; 0; 0; 0; 0; 0; 0; 0]
 [2; 2; 2; 2; 0; 0; 5; 5; 5; 0; 0; 0; 0; 0; 0; 0]
 [2; 2; 2; 2; 0; 0; 5; 5; 5; 0; 0; 0; 0; 0; 0; 0]
 [2; 2; 2; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]]

(1, 2, 5)


, — , , .


//   

// groups key - , value -    ,   
let groups = Dictionary<int, (int*int) list>()
markedMultyImages
|> Array2D.iteri (fun row col v -> if v <> 0 then
                                       if not (groups.ContainsKey(v)) then
                                            groups.Add(v, [row, col])
                                       else
                                            groups.[v] <- (row, col)::groups.[v])

printfn "groups = \n %A" groups


groups = 
 seq
  [[1, [(9, 13); (9, 12); (9, 11); ... ]];
   [2, [(15, 3); (15, 2); (15, 1); ... ]];
   [5, [(14, 8); (14, 7); (14, 6); ... ]]]


  1. ( )
  2. ( )
  3. ( )

let mapAreas = groups
               |> Seq.map (|KeyValue|)
               |> Map.ofSeq

//   
let areas = mapAreas
            |> Map.map (fun k v -> v |> List.length)
printfn "areas =\n %A" areas

areas — , — , —


areas =
 map [(1, 44); (2, 48); (5, 9)]

, , 1 44 , 2 — 48 , 5 — 9 .


, , , 1 , . , . , — ,


//     
    let subarrays = mapAreas
                    |> Map.map (fun k v ->
                                    let minRow = v |> List.map (fst) |> List.min
                                    let maxRow = v |> List.map (fst) |> List.max
                                    let minCol = v |> List.map (snd) |> List.min
                                    let maxCol = v |> List.map (snd) |> List.max

                                    let dimRow = maxRow - minRow + 2 + 1 // add two zeros on left and right sides
                                    let dimCol = maxCol - minCol + 2 + 1

                                    let array2d = Array2D.create dimRow dimCol 0
                                    v |> List.iter (fun elem -> (array2d.[(fst elem) - minRow + 1, (snd elem) - minCol + 1] <- multyBinImage.[fst elem, snd elem]))

                                    array2d
                               )

    printfn "subarrays =\n %A" subarrays


subarrays =
 map
  [(1, [[0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
        [0; 0; 0; 1; 1; 1; 1; 0; 0; 0]
        [0; 0; 1; 1; 1; 1; 1; 1; 0; 0]
        [0; 1; 1; 1; 1; 1; 1; 1; 1; 0]
        [0; 1; 1; 1; 1; 1; 1; 1; 1; 0]
        [0; 1; 1; 1; 1; 1; 1; 1; 1; 0]
        [0; 0; 1; 1; 1; 1; 1; 1; 0; 0]
        [0; 0; 0; 1; 1; 1; 1; 0; 0; 0]
        [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]]);

   (2, [[0; 0; 0; 0; 0; 0]
         [0; 1; 1; 1; 1; 0]
         [0; 1; 1; 1; 1; 0]
         [0; 1; 1; 1; 1; 0]
         [0; 1; 1; 1; 1; 0]
         [0; 1; 1; 1; 1; 0]
         [0; 1; 1; 1; 1; 0]
         [0; 1; 1; 1; 1; 0]
         [0; 1; 1; 1; 1; 0]
         [0; 1; 1; 1; 1; 0]
         [0; 1; 1; 1; 1; 0]
         [0; 1; 1; 1; 1; 0]
         [0; 1; 1; 1; 1; 0]
         [0; 0; 0; 0; 0; 0]]);

   (5, [[0; 0; 0; 0; 0]
        [0; 1; 1; 1; 0]
        [0; 1; 1; 1; 0]
        [0; 1; 1; 1; 0]
        [0; 0; 0; 0; 0]])]

. — .


//       
    let erosionMaks = array2D [[0;1;0]
                               [1;1;1]
                               [0;1;0]]

    let borders = subarrays
                  |> Map.map (fun label array2d -> Algorithms.borderAllocation array2d erosionMaks (1, 1))
    let bordersLength = borders |> Map.map (fun label elem -> elem |> Seq.cast<int> |> Seq.filter ((<>)0) |> Seq.length)

    printfn "borders =\n %A" borders
    printfn "borders length =\n %A" bordersLength


borders =
 map
  [(1, [[0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
        [0; 0; 0; 1; 1; 1; 1; 0; 0; 0]
        [0; 0; 1; 0; 0; 0; 0; 1; 0; 0]
        [0; 1; 0; 0; 0; 0; 0; 0; 1; 0]
        [0; 1; 0; 0; 0; 0; 0; 0; 1; 0]
        [0; 1; 0; 0; 0; 0; 0; 0; 1; 0]
        [0; 0; 1; 0; 0; 0; 0; 1; 0; 0]
        [0; 0; 0; 1; 1; 1; 1; 0; 0; 0]
        [0; 0; 0; 0; 0; 0; 0; 0; 0; 0]]);

(2, [[0; 0; 0; 0; 0; 0]
      [0; 1; 1; 1; 1; 0]
      [0; 1; 0; 0; 1; 0]
      [0; 1; 0; 0; 1; 0]
      [0; 1; 0; 0; 1; 0]
      [0; 1; 0; 0; 1; 0]
      [0; 1; 0; 0; 1; 0]
      [0; 1; 0; 0; 1; 0]
      [0; 1; 0; 0; 1; 0]
      [0; 1; 0; 0; 1; 0]
      [0; 1; 0; 0; 1; 0]
      [0; 1; 0; 0; 1; 0]
      [0; 1; 1; 1; 1; 0]
      [0; 0; 0; 0; 0; 0]]);

   (5, [[0; 0; 0; 0; 0]
        [0; 1; 1; 1; 0]
        [0; 1; 0; 1; 0]
        [0; 1; 1; 1; 0]
        [0; 0; 0; 0; 0]])]

borders length =
 map [(1, 18); (2, 28); (5, 8)]

1 18 , 2 — 28 5 — 8 .


.


//    
    let centers = borders
                   |> Map.map (fun k v ->
                                 let centerRow = v |> Array2D.mapi (fun r _ _ -> float r) |> Seq.cast<float> |> Seq.average // list.average requires float
                                 let centerCol = v |> Array2D.mapi (fun _ c _ -> float c) |> Seq.cast<float> |> Seq.average
                                 (int centerCol, int centerRow)
                              )
    printfn "centers =\n %A" centers


centers =
 map [(1, (4, 4)); (2, (2, 6)); (5, (2, 2))]

.


C=μRσR


μR —
σR —



μR=1Kk=1K1||(rk,ck)(r¯,c¯)||


(r_k, c_k) —
(r, c) —



//    
    let diff point1 point2 = 
        let square x = x * x
        let s1 = square ((fst point1) - (fst point2))
        let s2 = square ((snd point1) - (snd point2))
        sqrt(double (s1 + s2))

μR .


//  ,  key - , value -   (double)
let mr = borders
         |> Map.map (fun label v ->
                        let aver = v
                                    |> Array2D.mapi (fun r c v -> diff (r, c) centers.[label])
                                    |> Seq.cast<double>
                                    |> Seq.sum
                        aver / (double v.Length)
                 )

σR


σR=(1Kk=1K1[||(rk,ck)(r¯,c¯)||μR]2)1/2


let qr = borders
     |> Map.map (fun label v ->
                    let aver = v
                            |> Array2D.mapi (fun r c v ->
                                                let t1 = diff (r, c) centers.[label]
                                                let t2 = (t1 - mr.[label]) * (t1 - mr.[label])
                                                t2
                                            )
                            |> Seq.cast<double>
                            |> Seq.sum
                            |> sqrt
                aver / (double v.Length)
         )

//  .   ,   
    let roundness = mr |> Map.map (fun label v -> v / qr.[label])
    printfn "roundness =\n %A" roundness


roundness =
 map [(1, 25.06054796); (2, 20.37134197); (5, 13.43281947)]

, 1 . .



() () F#. . , . , F# , . , , F# . , . .


本文中讨论的算法本身可以在这里找到。在测试存储库中,有文章最后一部分的实现,其中搜索了图像内最圆形的对象。
二进制形态学算法


All Articles