3
\$\begingroup\$

This code is intended to be used with an image manipulation library that I'm working on, and the goal is to take the raw byte[] data from an image, and transform it into a 2D array where each position on the array has a value corresponding to the pixel coordinates of the image.

Comments concerning how to do this faster or more aligned with F# standards, or concerning style and formatting, are very much appreciated. I'm still learning F# so I need all the help I can get.

let Transform2D (pArrayWidth:int) (pArray:byte[]) = 
    let tHeight = pArray.Length / pArrayWidth
    let tImageArray = Array.zeroCreate<byte[]> tHeight

    let mutable tStart = 0
    let mutable tFinish = tStart + pArrayWidth - 1        // 0 indexed array

    for tRowIndex in [ 0 .. tHeight ] do
        tImageArray.[tRowIndex] <- pArray.[tStart .. tFinish]
        tStart <- tStart + pArrayWidth
        tFinish <- tFinish + pArrayWidth

    tImageArray
\$\endgroup\$
  • \$\begingroup\$ So, the image is a greyscale 8-bit bitmap? (Otherwise, one byte per pixel wouldn't make sense.) \$\endgroup\$ – svick Jul 18 '14 at 2:37
  • \$\begingroup\$ Ummmmm, actually now that you mentioned that I think I might need to go review that bit of information. \$\endgroup\$ – Kenneth Posey Jul 18 '14 at 2:38
  • \$\begingroup\$ Does the transformation part seem ok though, even if I need to increase the width that I'm slicing? \$\endgroup\$ – Kenneth Posey Jul 18 '14 at 16:26
1
\$\begingroup\$

You should consider whether byte[][] is really the best type to represent a bitmap in F#.

On one hand, arrays are inherently mutable and don't work well with pattern matching, so you might want to consider using list<list<byte>>, especially if performance is not critical.

On the other hand, a rectangular array of bytes is more accurately modeled as byte[,] (2D array), not byte[][] (array of arrays, a.k.a. jagged array). Though again, this is going to be slower than the original option.


for tRowIndex in [ 0 .. tHeight ] do

This has to be tHeight - 1, otherwise your code will fail with an exception.


let mutable tStart = 0
let mutable tFinish = tStart + pArrayWidth - 1        // 0 indexed array

for tRowIndex in [ 0 .. tHeight ] do
    tImageArray.[tRowIndex] <- pArray.[tStart .. tFinish]
    tStart <- tStart + pArrayWidth
    tFinish <- tFinish + pArrayWidth

tImageArray

You can use sequence expressions to create arrays in a functional way. Here, that would look something like:

[|
    for tRowIndex in [ 0 .. tHeight - 1 ] do
        let tStart = tRowIndex * pArrayWidth
        let tFinish = tStart + pArrayWidth - 1
        yield pArray.[tStart .. tFinish]
|]

In both cases, a very similar syntax (for .. in) is used, but my version doesn't use any mutable variables and is very similar to list comprehensions in other functional languages.

\$\endgroup\$
  • \$\begingroup\$ I'm actually accustomed to using list comprehensions in python, but I'm trying to adjust to F# from a C# mindset, so it's taking a bit of effort to see problems in a new way. I have a bit of confusion understanding what you mean concerning performance issues with the 2D array vs an array of arrays, are you saying that the byte[][] is fastest, byte[,] is fastest, or the list<list<byte>> is fastest? \$\endgroup\$ – Kenneth Posey Jul 20 '14 at 0:14
  • 1
    \$\begingroup\$ @KortPleco If you know C#, then you should know LINQ, which is also quite similar to list comprehensions (using yet another syntax). And I meant that byte[][] is going to be fastest. \$\endgroup\$ – svick Jul 20 '14 at 9:05
  • \$\begingroup\$ great, thanks for the clarification. This is going to be very speed sensitive, so that's important for me to know. =) \$\endgroup\$ – Kenneth Posey Jul 20 '14 at 18:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.