This is my firstish (heavily rewritten) go at the completed project that I've been working on with CodeReview assistance, so further advice is appreciated! See here, here and here for the past history of the project.
I strongly suspect there's a much more functional way to manage my loops, so assistance with that area especially is what I would like.
I have a separate set of unit tests (visible here) which confirm that this code works with my two sample image sets.
namespace MathAlgorithms
module ArrayFunctions =
// Generic because it makes testing easier, yay math
let SearchSubset (tSmallArray:'a[][]) (tLargeArray:'a[][]) (pCoordinate:(int * int)) =
let tSmallHeight = tSmallArray.Length
let tSmallWidth = tSmallArray.[0].Length
let tHeightIndex = fst pCoordinate
let tWidthIndex = snd pCoordinate
let mutable tSmallHeightIndex = 0
let mutable tSmallWidthIndex = 0
let mutable tMatch = true
try
while ( tSmallHeightIndex < tSmallHeight - 1 ) && tMatch do
while ( tSmallWidthIndex < tSmallWidth - 1 ) && tMatch do
let tLargeCurrentValue = tLargeArray.[tHeightIndex + tSmallHeightIndex].[tWidthIndex + tSmallWidthIndex]
let tSmallCurrentValue = tSmallArray.[tSmallHeightIndex].[tSmallWidthIndex]
if tSmallCurrentValue = tLargeCurrentValue then
tSmallWidthIndex <- tSmallWidthIndex + 1
else
tMatch <- false
tSmallWidthIndex <- 0
tSmallHeightIndex <- tSmallHeightIndex + 1
tMatch
with
| _ -> false
namespace FsharpImaging
open System
open System.Drawing
open System.Drawing.Imaging
open System.Runtime.InteropServices
open MathAlgorithms
module ImageFunctions =
let LoadBitmapIntoArray (pBitmap:Bitmap) =
let tBitmapData = pBitmap.LockBits( Rectangle(Point.Empty, pBitmap.Size),
ImageLockMode.ReadOnly,
PixelFormat.Format24bppRgb )
let tImageArrayLength = Math.Abs(tBitmapData.Stride) * pBitmap.Height
let tImageDataArray = Array.zeroCreate<byte> tImageArrayLength
Marshal.Copy(tBitmapData.Scan0, tImageDataArray, 0, tImageArrayLength)
pBitmap.UnlockBits(tBitmapData)
( pBitmap.Width, pBitmap.Height, tBitmapData.Stride ), tImageDataArray
// Notes:
// Image pixel data is stored BGR ( blue green red )
// Image data is padded to be divisible by 4 (int32 width)
let Transform2D ( (pDimension:int*int*int), (pArray:byte[]) ) =
let tWidth, tHeight, tStride = pDimension
[|
for tHeightIndex in 0 .. ( tHeight - 1 ) do
let tStart = tHeightIndex * tStride
let tFinish = ( tStart + tWidth * 3 ) - 1
yield [|
for tWidthIndex in tStart .. 3 .. tFinish do
yield ( pArray.[tWidthIndex]
, pArray.[tWidthIndex + 1]
, pArray.[tWidthIndex + 2] )
|]
|]
module ImageSearch =
open ImageFunctions
let SearchBitmap (pSmallBitmap:Bitmap) (pLargeBitmap:Bitmap) =
let tSmallArray = Transform2D <| LoadBitmapIntoArray pSmallBitmap
let tLargeArray = Transform2D <| LoadBitmapIntoArray pLargeBitmap
let tSearchWidth = pLargeBitmap.Width - pSmallBitmap.Width
let tSearchHeight = pLargeBitmap.Height - pSmallBitmap.Height
let mutable tHeightIndex = 0
let mutable tWidthIndex = 0
let mutable tMatch = false
let mutable tContinue = true
while ( tHeightIndex < tSearchHeight - 1 ) && tContinue do
while ( tWidthIndex < tSearchWidth - 1 ) && tContinue do
let tCurrentValue = tLargeArray.[tHeightIndex].[tWidthIndex]
let tFirstSmallPixel = tSmallArray.[0].[0]
if tCurrentValue = tFirstSmallPixel then
tMatch <- ArrayFunctions.SearchSubset tSmallArray tLargeArray ( tHeightIndex, tWidthIndex )
if tMatch then tContinue <- false
if tMatch = false && tContinue = true then
tWidthIndex <- tWidthIndex + 1
if tMatch = false && tContinue = true then
tWidthIndex <- 0 // Reset to search next row
tHeightIndex <- tHeightIndex + 1
tMatch, tWidthIndex, tHeightIndex
namespace MainProgram
open System.Drawing
open System.Drawing.Imaging
open System.Diagnostics
open FsharpImaging
module Main =
[<EntryPoint>]
let main (args:string[]) =
use tSmallBitmap = new Bitmap("searchimage.bmp")
use tLargeBitmap = new Bitmap("containingimage.bmp")
let tSuccess, xCoord, yCoord = ImageSearch.SearchBitmap tSmallBitmap tLargeBitmap
// Must return from function
0