I've been working on this module with the assistance of codereview, and based on the responses and my own research, this is my project so far. The goal behind the code is that it starts with a .NET bitmap object, extracts the byte array of image data from the bitmap, then transforms it into an array of arrays or matrix.
The idea behind putting the bitmap data into the matrix is that I want to be able to use a simple representation of a coordinate, like Array[10][20], to get the RGB value for the pixel located at the (10, 20) coordinate. Rather than using a proper 2D array, I'm choosing to use an array of arrays, or "jagged" array, for speed concerns.
Your assistance with sanity, style, F# conventions, and any other problems you spot is appreciated in helping further refine this algorithm.
open System
open System.Drawing
open System.Drawing.Imaging
open System.Runtime.InteropServices
module ImageSearch =
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(tImageDataArray, 0, tBitmapData.Scan0, tImageArrayLength)
pBitmap.UnlockBits(tBitmapData)
pBitmap.Width, tImageDataArray
let Transform2D ( (pArrayWidth:int), (pArray:byte[]) ) =
let tHeight = pArray.Length / ( pArrayWidth * 3 ) // 3 bytes per RGB
let tImageArray = [|
for tHeightIndex in [ 0 .. tHeight - 1] do
let tStart = tHeightIndex * pArrayWidth
let tFinish = tStart + pArrayWidth - 1
yield [|
for tWidthIndex in [tStart .. 3 .. tFinish] do
yield ( pArray.[tWidthIndex]
, pArray.[tWidthIndex + 1]
, pArray.[tWidthIndex + 2] )
|]
|]
tImageArray
let SearchBitmap (pSmallBitmap:Bitmap) (pLargeBitmap:Bitmap) =
let tSmallArray = Transform2D <| LoadBitmapIntoArray pSmallBitmap
let tLargeArray = Transform2D <| LoadBitmapIntoArray pLargeBitmap
let mutable tMatchFound = false
1 Answer 1
By convention, F# function names are camelCase
, for example transform2D
instead of Transform2D
.
Hungarian notation is dead and buried. For example, use bitmap
instead of pBitmap
.
If ImageSearch
is a top-level module, declarations do not have to be indented.
Your for...in
expressions can be written without brackets. For example, instead of
for tHeightIndex in [ 0 .. tHeight - 1] do
write
for heightIndex in 0 .. height - 1 do
Similarly, instead of
for tWidthIndex in [tStart .. 3 .. tFinish] do
write
for widthIndex in start .. 3 .. finish do
This is not just a style issue, the different versions actually compile to different IL.
The final expression of a function is its return value, so instead of
let tHeight = ... let tImageArray = [| ... |] tImageArray
you can write
let height = ...
[| ... |]
(削除)
In type annotations, it's conventional to have a space on either side of the :
, for example instead of
let Transform2D ( (pArrayWidth:int), (pArray:byte[]) ) =
write
let transform2D (width : int, array : byte[]) =
(削除ここまで)
I can't actually back up this last point. MSDN and FSharp.Core don't seem to stick to one style, even in the same function
let isProperSuperset (x:Set<'T>) (y: Set<'T>) = SetTree.psubset x.Comparer y.Tree x.Tree
-
\$\begingroup\$ "This is not just a style issue, the different versions actually compile to different IL." What is the significance of the different way it compiles? \$\endgroup\$Ken– Ken2014年07月22日 16:19:35 +00:00Commented Jul 22, 2014 at 16:19
-
1\$\begingroup\$ @KennethPosey it's the difference between a sequence and a list. One major difference is that a list must have all its elements in memory at once. As a silly example, try timing
printfn "%A" (Seq.length {0L .. 10000000L}
compared toprintfn "%A" (Seq.length [0L .. 10000000L])
. \$\endgroup\$mjolka– mjolka2014年07月23日 02:34:03 +00:00Commented Jul 23, 2014 at 2:34 -
\$\begingroup\$ so to make sure I'm understanding this correctly, the sequence option (using for-in-do without square brackets) is likely going to perform better than the list option when iterating? \$\endgroup\$Ken– Ken2014年07月23日 16:15:30 +00:00Commented Jul 23, 2014 at 16:15
-
\$\begingroup\$ I just ran it through
100000000L
for each of those two, and the sequence completed nearly instantly while the list hung for several seconds then overflowed. That answers my question then. Thanks a ton for the clarification. \$\endgroup\$Ken– Ken2014年07月23日 16:21:23 +00:00Commented Jul 23, 2014 at 16:21
let mutable tMatchFound = false
, and then? \$\endgroup\$