I want to make a lazy loading of image pixels to the 3 dimensional array of integers.
For example in simple way it looks like this:
for i=0 to Width
for j=0 to Height
let point=image.GetPixel(i,j)
pixels.[0,i,j] <- point.R
pixels.[1,i,j] <- point.G
pixels.[2,i,j] <- point.B
How it can be made in lazy way?
What would be slow is the call to GetPixel. If you want to call it only as needed, you could use something like this:
open System.Drawing
let lazyPixels (image:Bitmap) =
let Width = image.Width
let Height = image.Height
let pixels : Lazy<byte>[,,] = Array3D.zeroCreate 3 Width Height
for i = 0 to Width-1 do
for j = 0 to Height-1 do
let point = lazy image.GetPixel(i,j)
pixels.[0,i,j] <- lazy point.Value.R
pixels.[1,i,j] <- lazy point.Value.G
pixels.[2,i,j] <- lazy point.Value.B
pixels
GetPixel will be called at most once for every pixel, and then reused for the other components.
Another way of approaching this problem would be to do a bulk-load of the entire image. This will be a lot quicker than calling GetPixel over and over again.
open System.Drawing
open System.Drawing.Imaging
let pixels (image:Bitmap) =
let Width = image.Width
let Height = image.Height
let rect = new Rectangle(0,0,Width,Height)
// Lock the image for access
let data = image.LockBits(rect, ImageLockMode.ReadOnly, image.PixelFormat)
// Copy the data
let ptr = data.Scan0
let stride = data.Stride
let bytes = stride * data.Height
let values : byte[] = Array.zeroCreate bytes
System.Runtime.InteropServices.Marshal.Copy(ptr,values,0,bytes)
// Unlock the image
image.UnlockBits(data)
let pixelSize = 4 // <-- calculate this from the PixelFormat
// Create and return a 3D-array with the copied data
Array3D.init 3 Width Height (fun i x y ->
values.[stride * y + x * pixelSize + i])
(adopted from the C# sample on Bitmap.LockBits)
What do you mean by lazy?
An array is not a lazy data type, which means that if you want to use arrays, you need to load all pixels during the initialization. If we were using single-dimensional array, an alternative would be to use seq<_> which is lazy (but you can access elements only sequentially). There is nothing like seq<_> for multi-dimensional arrays, so you'll need to use something else.
Probably the closest option would be to use three-dimensional array of lazy values (Lazy<int>[,,]). This is an array of delayed thunks that access pixels and are evaluated only when you actually read the value at the location. You could initialize it like this:
for i=0 to Width
for j=0 to Height
let point = lazy image.GetPixel(i,j)
pixels.[0,i,j] <- lazy point.Value.R
pixels.[1,i,j] <- lazy point.Value.G
pixels.[2,i,j] <- lazy point.Value.B
The snippet creates a lazy value that reads the pixel (point) and then three lazy values to get the individual color components. When accessing color component, the point value is evaluated (by accessing Value).
The only difference in the rest of your code is that you'll need to call Value (e.g. pixels.[0,10,10].Value to get the actual color component of the pixel.
You could define more complex data structures (such as your own type that supports indexing and is lazy), but I think that array of lazy values should be a good starting point.
As mentioned already by other comments that you can use the lazy pixel loading in the 3D array but that would just make the GetPixel operation lazy and not the memory allocation of the 3D array as the array is allocated already when you call create method of Array3D.
If you want to make the memory allocation as well as GetPixel lazy then you can use sequences as shown by below code:
let getPixels (bmp:Bitmap) =
seq {
for i = 0 to bmp.Height-1 do
yield seq {
for j = 0 to bmp.Width-1 do
let pixel = bmp.GetPixel(j,i)
yield (pixel.R,pixel.G,pixel.B)
}
}
Related
I'm using an external library written in C that applies filters to an image. It receives the original image pixels as an array of float values and writes the new image float values on another array. One specific filter creates a mask to be used by a sharpen filter, and I don't know why but it only works with smaller images, while bigger images (a million pixels more or less) cause the application to crash, giving an EXC_BAD_ACCESS error right after executing the wrapper that calls the external lib function. Is there anything wrong with my code, which creates the parameters that will be passed to the external lib, or is the problem likely in the external library?
func allocateMaskArgs() { //method to allocate mask parameters in memory, to be used by sharpen filter
let size = originalImageMatrix.params[0] * originalImageMatrix.params[1] //height of the image multiplied by width
if maskBuffer != nil {
self.maskBuffer.deallocate()
}
maskBuffer = UnsafeMutablePointer<UnsafeMutablePointer<Float>?>.allocate(capacity: 2)
let constantPointer: UnsafeMutablePointer<Float>?
constantPointer = UnsafeMutablePointer<Float>.allocate(capacity: 1)
constantPointer!.advanced(by: 0).pointee = 4.0 //this is the intensity value of the mask, it should always be 4
maskBuffer.advanced(by: 0).pointee = constantPointer
let maskArrayPointer: UnsafeMutablePointer<Float>? //this is where the mask created by createMask() should be stored by the external lib function
maskArrayPointer = UnsafeMutablePointer<Float>.allocate(capacity: size)
maskBuffer.advanced(by: 1).pointee = maskArrayPointer
}
func createMask() { //creates sharpen mask and stores in maskBuffer
var input_params: [Int] = [self.originalImageMatrix.params[0], self.originalImageMatrix.params[1]]
var output_params: [Int] = [self.newImageMatrix.params[0], self.newImageMatrix.params[1]]
self.imagingAPI.applyFilters(self.originalImageMatrix.v!, input_params: &input_params, output_image: self.newImageMatrix.v!, output_params: &output_params, filter_id: 11, args: self.maskBuffer)
}
The external library function is accessed through this wrapper function:
- (void) applyFilters: (float *) input_image input_params: (long *) input_params output_image : (float *) output_image output_params : (long *) output_params filter_id : (int) filter_id args : (float**) args;
I want to write an algorithm which allows me to rescale numbers to between 0 and 1. This means if I pass 25, 100, 500 then it should generate a new scale and represent those numbers on a scale of 0 to 1.
Here is what I have which is incorrect and does not make sense.
height: item.height/item.height * 20
Pass in the numbers in an array.
Loop through the numbers and find the max.
Map the array of integers to an array of Doubles, each one being the value from the source array, divided by the max.
Try to write that code. If you have trouble, update your question with your attempt and tell us what's going wrong.
EDIT:
Your answer shows how to print your resulting scaled values, but you implied that you actually want to create a new array containing the scaled values. For that you could use a function like this:
func scaleArray(_ sourceArray: [Int]) -> [Double] {
guard let max = sourceArray.max() else {
return [Double]()
}
return sourceArray.map {
return Double($0)/Double(max)
}
}
Edit #2:
Here is code that would let you test the above:
func scaleAndPrintArray(_ sourceArray: [Int]) {
let scaledArray = scaleArray(sourceArray)
for index in 0..<sourceArray.count {
print(String(format: "%3d", sourceArray[index]), String(format: "%0.5f",scaledArray[index]))
}
}
for arrayCount in 1...5 {
let arraySize = Int(arc4random_uniform(15)) + 5
var array = [Int]()
for _ in 1..<arraySize {
array.append(Int(arc4random_uniform(500)))
}
scaleAndPrintArray(array)
if arrayCount < 5 {
print("-----------")
}
}
(Sorry but I don't know swift)
If you're wanting to create a linear scale, a linear equation is y(x) = m*x + c. You wish the output to range from 0 to 1 when the input ranges from the minimum value to the maximum (your question is ambiguous, maybe you may wish to lock y(0) to 0).
y(0) = min
y(1) = max
therefore
c = min
m = max - min
and to find the value of any intervening value
y = m*x + c
This is my code. I'm trying to make a small tile game and this is the function to randomise the tiles location. The problem i'm having here is to assign the randLocation to the imageview.
var imageViewCentersCopy : NSMutableArray = imageViewsCenters.mutableCopy() as! NSMutableArray
var randLocationIndex : Int
var randLocation : CGPoint
for imageView in self.imageViews {
randLocationIndex = Int(arc4random_uniform(UInt32(imageViewCentersCopy.count)))
randLocation = imageViewCentersCopy.objectAtIndex(randLocationIndex).CGPointValue()
println("\(self.imageViews)")
imageView.center = randLocation
}
Use the typecast to avoid that problem
(imageView as! UIImageView).center = randLocation
I think it is not very expedient to use the coordinates in your randomizing logic. These are really two separate problems. Try to think of a simpler model, then develop the placement logic.
For example, you could just randomize the order of the tiles in a flat array.
Then you simply iterate through the array, calculate the correct coordinates with some simple row and column algorithm (based on the index) and place the tiles in their correct position.
This, too, is MVC.
E.g. assuming 9 tiles for a 3x3 grid:
for var x = 0; x < randomTiles.count; x++ {
let row = x / 3
let column = x % 3
let point = CGPointMake(10 + column * 20, 10 + row * 20)
// assign the tile's center
}
I am trying to implement RGB histogram computation for images in Swift (I am new to iOS).
However the computation time for 1500x1000 image is about 66 sec, which I consider to be too slow.
Are there any ways to speed up image traversal?
P.S. current code is the following:
func calcHistogram(image: UIImage) {
let bins: Int = 20;
let width = Int(image.size.width);
let height = Int(image.size.height);
let binStep: Double = Double(bins-1)/255.0
var hist = Array(count:bins, repeatedValue:Array(count:bins, repeatedValue:Array(count:bins, repeatedValue:Int())))
for i in 0..<bins {
for j in 0..<bins {
for k in 0..<bins {
hist[i][j][k] = 0;
}
}
}
var pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage))
var data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
for x in 0..<width {
for y in 0..<height {
var pixelInfo: Int = ((width * y) + x) * 4
var r = Double(data[pixelInfo])
var g = Double(data[pixelInfo+1])
var b = Double(data[pixelInfo+2])
let r_bin: Int = Int(floor(r*binStep));
let g_bin: Int = Int(floor(g*binStep));
let b_bin: Int = Int(floor(b*binStep));
hist[r_bin][g_bin][b_bin] += 1;
}
}
}
As noted in my comment on the question, there are some things you might rethink before you even try to optimize this code.
But even if you do move to a better overall solution like GPU-based histogramming, a library, or both... There are some Swift pitfalls you're falling into here that are good to talk about so you don't run into them elsewhere.
First, this code:
var hist = Array(count:bins, repeatedValue:Array(count:bins, repeatedValue:Array(count:bins, repeatedValue:Int())))
for i in 0..<bins {
for j in 0..<bins {
for k in 0..<bins {
hist[i][j][k] = 0;
}
}
}
... is initializing every member of your 3D array twice, with the same result. Int() produces a value of zero, so you could leave out the triple for loop. (And possibly change Int() to 0 in your innermost repeatedValue: parameter to make it more readable.)
Second, arrays in Swift are copy-on-write, but this optimization can break down in multidimensional arrays: changing an element of a nested array can cause the entire nested array to be rewritten instead of just the one element. Multiply that by the depth of nested arrays and number of element writes you have going on in a double for loop and... it's not pretty.
Unless there's a reason your bins need to be organized this way, I'd recommend finding a different data structure for them. Three separate arrays? One Int array where index i is red, i + 1 is green, and i + 2 is blue? One array of a custom struct you define that has separate r, g, and b members? See what conceptually fits with your tastes or the rest of your app, and profile to make sure it works well.
Finally, some Swift style points:
pixelInfo, r, g, and b in your second loop don't change. Use let, not var, and the optimizer will thank you.
Declaring and initializing something like let foo: Int = Int(whatever) is redundant. Some people like having all their variables/constants explicitly typed, but it does make your code a tad less readable and harder to refactor.
Int(floor(x)) is redundant — conversion to integer always takes the floor.
If you have some issues about performance in your code, first of all, use Time Profiler from Instruments. You can start it via Xcode menu Build->Profile, then, Instruments app opened, where you can choose Time Profiler.
Start recording and do all interactions in the your app.
Stop recording and analyse where is the "tightest" place of your code.
Also check options "Invert call tree", "Hide missing symbols" and "Hide system libraries" for better viewing profile results.
You can also double click at any listed function to view it in code and seeing percents of usage
So I'm writing a lowpass accelerometer function to moderate the jitters of the accelerometer. I have a CGFloat array to represent the data and i want to damp it with this function:
// Damps the gittery motion with a lowpass filter.
func lowPass(vector:[CGFloat]) -> [CGFloat]
{
let blend:CGFloat = 0.2
// Smoothens out the data input.
vector[0] = vector[0] * blend + lastVector[0] * (1 - blend)
vector[1] = vector[1] * blend + lastVector[1] * (1 - blend)
vector[2] = vector[2] * blend + lastVector[2] * (1 - blend)
// Sets the last vector to be the current one.
lastVector = vector
// Returns the lowpass vector.
return vector
}
In this case, lastVector is defined as follows up at the top of my program:
var lastVector:[CGFloat] = [0.0, 0.0, 0.0]
The three lines in the form vector[a] = ... give me the errors. Any ideas as to why i am getting this error?
That code seems to compile if you pass the array with the inout modifier:
func lowPass(inout vector:[CGFloat]) -> [CGFloat] {
...
}
I'm not sure whether that's a bug or not. Instinctively, if I pass an array to a function I expect to be able to modify it. If I pass with the inout modifier, I'd expect to be able to make the original variable to point to a new array - similar to what the & modifier does in C and C++.
Maybe the reason behind is that in Swift there are mutable and immutable arrays (and dictionaries). Without the inout it's considered immutable, hence the reason why it cannot be modified.
Addendum 1 - It's not a bug
#newacct says that's the intended behavior. After some research I agree with him. But even if not a bug I originally considered it wrong (read up to the end for conclusions).
If I have a class like this:
class WithProp {
var x : Int = 1
func SetX(newVal : Int) {
self.x = newVal
}
}
I can pass an instance of that class to a function, and the function can modify its internal state
var a = WithProp()
func Do1(p : WithProp) {
p.x = 5 // This works
p.SetX(10) // This works too
}
without having to pass the instance as inout.
I can use inout instead to make the a variable to point to another instance:
func Do2(inout p : WithProp) {
p = WithProp()
}
Do2(&a)
With that code, from within Do2 I make the p parameter (i.e. the a variable) point to a newly created instance of WithProp.
The same cannot be done with an array (and I presume a dictionary as well). To change its internal state (modify, add or remove an element) the inout modifier must be used. That was counterintuitive.
But everything gets clarified after reading this excerpt from the swift book:
Swift’s String, Array, and Dictionary types are implemented as structures. This means that strings, arrays, and dictionaries are copied when they are assigned to a new constant or variable, or when they are passed to a function or method.
So when passed to a func, it's not the original array, but a copy of it - Hence any change made to it (even if possible) wouldn't be done on the original array.
So, in the end, my original answer above is correct and the experienced behavior is not a bug
Many thanks to #newacct :)
Since Xcode 6 beta 3, modifying the contents of an Array is a mutating operation. You cannot modify a constant (i.e. let) Array; you can only modify a non-constant (i.e. var) Array.
Parameters to a function are constants by default. Therefore, you cannot modify the contents of vector since it is a constant. Like other parameters, there are two ways to be able to change a parameter:
Declare it var, in which case you can assign to it, but it is still passed by value, so any changes to the parameter has no effect on the calling scope.
Declare it inout, in which case the parameter is passed by reference, and any changes to the parameter is just like you made the changes on the variable in the calling scope.
You can see in the Swift standard library that all the functions that take an Array and mutate it, like sort(), take the Array as inout.
P.S. this is just like how arrays work in PHP by the way
Edit: The following worked for Xcode Beta 2. Apparently, the syntax and behavior of arrays has changed in Beta 3. You can no longer modify the contents of an array with subscripts if it is immutable (a parameter not declared inout or var):
Not valid with the most recent changes to the language
The only way I could get it to work in the play ground was change how you are declaring the arrays. I suggest trying this (works in playground):
import Cocoa
let lastVector: CGFloat[] = [0.0,0.0,0.0]
func lowPass(vector:CGFloat[]) -> CGFloat[] {
let blend: CGFloat = 0.2
vector[0] = vector[0] * blend + lastVector[0] * ( 1 - blend)
vector[1] = vector[1] * blend + lastVector[1] * ( 1 - blend)
vector[2] = vector[2] * blend + lastVector[2] * ( 1 - blend)
return vector
}
var test = lowPass([1.0,2.0,3.0]);
Mainly as a followup for future reference, #newacct's answer is the correct one. Since the original post showed a function that returns an array, the correct answer to this question is to tag the parameter with var:
func lowPass(var vector:[CGFloat]) -> [CGFloat] {
let blend:CGFloat = 0.2
// Smoothens out the data input.
vector[0] = vector[0] * blend + lastVector[0] * (1 - blend)
vector[1] = vector[1] * blend + lastVector[1] * (1 - blend)
vector[2] = vector[2] * blend + lastVector[2] * (1 - blend)
// Sets the last vector to be the current one.
lastVector = vector
// Returns the lowpass vector.
return vector
}