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
-
\$\begingroup\$ So, the image is a greyscale 8-bit bitmap? (Otherwise, one byte per pixel wouldn't make sense.) \$\endgroup\$svick– svick2014年07月18日 02:37:12 +00:00Commented Jul 18, 2014 at 2:37
-
\$\begingroup\$ Ummmmm, actually now that you mentioned that I think I might need to go review that bit of information. \$\endgroup\$Ken– Ken2014年07月18日 02:38:45 +00:00Commented Jul 18, 2014 at 2:38
-
\$\begingroup\$ Does the transformation part seem ok though, even if I need to increase the width that I'm slicing? \$\endgroup\$Ken– Ken2014年07月18日 16:26:51 +00:00Commented Jul 18, 2014 at 16:26
1 Answer 1
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.
-
\$\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\$Ken– Ken2014年07月20日 00:14:48 +00:00Commented Jul 20, 2014 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– svick2014年07月20日 09:05:14 +00:00Commented Jul 20, 2014 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\$Ken– Ken2014年07月20日 18:20:00 +00:00Commented Jul 20, 2014 at 18:20