I'm trying to write a function in ClojureScript, which returns the average RGBA value of a given ImageData Object.
In JavaScript, implementations for this problem with a "for" or "while" loop are very fast. Within milliseconds they return the average of e.g. 4000 x 4000 sized ImageData objects.
In ClojureScript my solutions are not approximately as fast, sometimes the browser gives up yielding "stack trace errors".
However the fastest one I wrote until now is this one:
(extend-type js/Uint8ClampedArray
ISeqable
(-seq [array] (array-seq array 0)))
(defn average-color [img-data]
(let [data (.-data img-data)
nr (/ (count data) 4)]
(->> (reduce (fn [m v] (-> (update-in m [:color (rem (:pos m) 4)] (partial + v))
(update-in [:pos] inc)))
{:color [0 0 0 0] :pos 0}
data)
(:color)
(map #(/ % nr)))))
Well, unfortunately it works only upt to values around 500x500, which is not acceptable.
I'm asking myself what exactly is the problem here. What do I have to attend in order to write a properly fast average-color function in ClojureScript.
The problem is that the function you have defined is recursive. I am not strong in clojurescript so I will not tell you haw to fix the problem in code but in concept.
You need to break the problem into smaller recursive units. So reduce a pixel row to get a result for each row, then reduce the row results. This will prevent the recursion from overflowing the call stack in javascript.
As for the speed, That will depend on how accurate you want the result to be, I would use a random sample selecting 10% of pixels randomly and using the average of that result.
You could also just use the hardware and scale the image by half, render it with smoothing on and then halving again until you have one pixel and use the value of that pixel. That will give you a pixel value average and is very fast but only does a value mean, not a photon mean.
I will point out that the value of the RGB channels are logarithmic and represent the square root of the photon count captured (for photo) or emitted (by screen). Thus the mean of the pixel values is much lower than the mean of the photon count. To get the correct mean you must get the mean of the square of each channel and then get the square root of the mean to bring it back to the logarithmic scale that is used for the RGB values.
Related
I have an array of N HTMLCanvasElements that come from N frames of a video, and I want to compute the "median canvas" in the sense that every component (r, g, b, opacity) of every pixel is the median of the corresponding component in all the canvases.
The video frames are 1280x720, so that the pixels data for every canvas (obtained with canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height).data) is a Uint8ClampedArray of length 3.686.400.
The naive way to compute the median is to:
prepare a result Uint8ClampedArray of length 3.686.400
prepare a temporary Uint8ClampedArray of length N
loop from 0 to 3.686.399
a) loop over the N canvases to fill the array
b) compute the median of the array
c) store the median to the result array
But it's very slow, even for 4 canvases.
Is there an efficient way (or existing code) to do that? My question is very similar to Find median of list of images, but I need to to this in JavaScript, not Python.
Note: for b), I use d3.median() which doesn't work on typed arrays, as far as I understand, so that it implies converting to numbers, then converting back to Uint8Clamped.
Note 2: I don't know much of GLSL shaders, but maybe using the GPU would be a way to get faster results. It would require to pass data from the CPU to the GPU though, which takes time if done repeatedly.
Note 3: the naive solution is there: https://observablehq.com/#severo/compute-the-approximate-median-image-of-a-video
You wrote
I use d3.median() which doesn't work on typed arrays…
Although that is not exactly true it leads into the right direction. Internally d3.median() uses the d3.quantile() method which starts off like this:
export default function quantile(values, p, valueof) {
values = Float64Array.from(numbers(values, valueof));
As you can see, this in fact does make use of typed arrays, it is just not your Uint8ClampedArray but a Float64Array instead. Because floating-point arithmetic is much more computation-intensive than its integer counterpart (including the conversion itself) this has a dramatic effect on the performance of your code. Doing this some 3 million times in a tight loop kills the efficiency of your solution.
Since you are retrieving all your pixel values from a Uint8ClampedArray you can be sure that you are always dealing with integers, though. That said, it is fairly easy to build a custom function median(values) derived from d3.median() and d3.quantile():
function median(values) {
// No conversion to floating point values needed.
if (!(n = values.length)) return;
if (n < 2) return d3.min(values);
var n,
i = (n - 1) * 0.5,
i0 = Math.floor(i),
value0 = d3.max(d3.quickselect(values, i0).subarray(0, i0 + 1)),
value1 = d3.min(values.subarray(i0 + 1));
return value0 + (value1 - value0) * (i - i0);
}
On top of getting rid of the problematic conversion on the first line this implementation additionally applies some more micro-optimizations because in your case you are always looking for the 2-quantile (i.e. the median). That might not seem much at first, but doing this multiple million times in a loop it does make a difference.
With minimal changes to your own code you can call it like this:
// medianImageData.data[i] = d3.median(arr); Instead of this use line below.
medianImageData.data[i] = median(arr);
Have a look at my working fork of your Observable notebook.
I am trying to calculate with higher precision numbers in JavaScript to be able to zoom in more on the Mandlebrot set.
(after a certain amount of zooming the results get "pixelated", because of the low precision)
I have looked at this question, so I tried using a library such as BigNumber but it was unusably slow.
I have been trying to figure this out for a while and I think the only way is to use a slow library.
Is there a faster library?
Is there any other way to calculate with higher precision numbers?
Is there any other way to be able to zoom in more on the Mandlebrot set?
Probably unneceseary to add this code, but this is the function I use to check if a point is in the Mandlebrot set.
function mandelbrot(x, y, it) {
var z = [0, 0]
var c1 = [x, y]
for (var i = 0; i < it; i++) {
z = [z[0]*z[0] - z[1]*z[1] + c1[0], 2*z[0]*z[1] + c1[1]]
if (Math.abs(z[0]) > 2, Math.abs(z[1]) > 2) {
break
}
}
return i
}
The key is not so much the raw numeric precision of JavaScript numbers (though that of course has its effects), but the way the basic Mandelbrot "escape" test works, specifically the threshold iteration counts. To compute whether a point in the complex plane is in or out of the set, you iterate on the formula (which I don't exactly remember and don't feel like looking up) for the point over and over again until the point obviously diverges (the formula "escapes" from the origin of the complex plane by a lot) or doesn't before the iteration threshold is reached.
The iteration threshold when rendering a view of the set that covers most of it around the origin of the complex plane (about 2 units in all directions from the origin) can be as low as 500 to get a pretty good rendering of the whole set at a reasonable magnification on a modern computer. As you zoom in, however, the iteration threshold needs to increase in inverse proportion to the size of the "window" onto the complex plane. If it doesn't, then the "escape" test doesn't work with sufficient accuracy to delineate fine details at higher magnifications.
The formula I used in my JavaScript implementation is
maxIterations = 400 * Math.log(1/dz0)
where dz0 is (arbitrarily) the width of the window onto the plane. As one zooms into a view of the set (well, the "edge" of the set, where things are interesting), dz0 gets pretty small so the iteration threshold gets up into the thousands.
The iteration count, of course, for points that do "escape" (that is, points that are not part of the Mandelbrot set) can be used as a sort of "distance" measurement. A point that escapes within a few iterations is clearly not "close to" the set, while a point that escapes only after 2000 iterations is much closer. That distance quality can be used in various ways in visualizations, either to provide a color value (common) or possibly a z-axis value if the set is being rendered as a 3D view (with the set as a sort of "mesa" in three dimensions and the borders being a vertical "cliff" off the sides).
My problem is as follows:
I have a set of values V1, V2, ... Vn
I have a function f(V) = g * V, where g is a scaling factor, that maps these values to another set of values A1, A2, ... An. These values correspond to areas of squares.
I also have W (width) and H (height) variables. And finally, I have a box packing algorithm (This one to be specific), that takes the W and H variables, and the A1 ... An areas, and tries to find a way to pack the areas into a box of size W x H. If the areas A are not too big, and the box packing algorithm successfully manages to fit the areas into the box, it will return the positions of the squares (the left-top coordinates, but this is not relevant). If the areas are too big, it will return nothing.
Given the values V and the dimensions of the box W and H, what is the highest value of g (the scaling factor in f(V)) that still fits the box?
I have tried to create an algorithm that initally sets g to (W x H) / sum(V1, V2, ... Vn). If the values V are distributed in such a way that they fit exactly into the box without leaving any space in between, this would give me a solution instantly. In reality this never happens, but it seems like a good starting point. With this initial value of g I would calculate the values A which are then fed to the box packing algorithm. The box packing algorithm will fail (return nothing), after which I decrease g by 0.01 (a completely arbitrary value established by trial and error) and try again. This cycle repeats until the box packing algorithm succeeds.
While this solution works, I feel like there should be faster and more accurate ways to determine g. For example, depending on how big W and H compared to the sum of the values V, it seems that there should be a way to determine a better value than 0.01, because if the difference is extremely big the algorithm would take really long, while if the difference is extremely small it would be very fast but very crude. In addition, I feel like there should be a more efficient method than just brute-forcing it like this. Any ideas?
You're on a good trail with your method I think !
I think you shouldn't decrease your value by a fixed amount but rather try to approach the value by ever smaller steps.
It's good because you have a good starting value. First you could decrease g by something like 0.1 * g, check if your packing succeeds, if not, continue to decrease with same step, else if it packs correctly increase g with a smaller step (like step = step / 2)
At some point your steps will become very small and you can stop searching (defining "small" is up to you)
You can use binary search approach. If you have two values of g, so that for one (g1) packing exists and for second (g2) packing doesn't exist, try value on half way h=(g1+g2)/2. If packing exists for h you get new larger final g, and you can make same check with h and g2. If packing doesn't exist you can make same check with values g1 and h.
With each step, interval of possible result max value, is halfed. You can get final result as precise as you like, with more iterations.
I have an array of about 1000 floats, all between 0 and 1. I want to show them in a bar chart where the highest 10 values clearly stand out.
The problem is my data appears to be normally distributed, i.e., there are a lot of 0.999943 and 0.99902 values. So if I plot all the values, you won't clearly see the top 10.
What kind of function can I pass the array values through in order to exaggerate the variance at the higher end of the range?
Since 0 < Your data < 1, what about using Math.log10 to calculate the base 10 logarithm of your data?
For example, Math.log10(0.999943) = -0.000024755491006670377 while Math.log10(0.99902 ) = -0.00042581727682710913, then if you like, you can multiple the result with 100000 and get the result -2.4 and -42, the variance is exaggerated and their relationship is not changed, because log10 is an monotone increasing function.
For any continuous random variate X with cumulative distribution function F(x), transforming the observations by F(x) makes them uniformly distributed between zero and one. Expressing the same idea in a slightly conceptually different way, present the p-values rather than the raw observations to highlight the extreme values.
This has the benefit that if the transformed value is above 0.95 for instance, you can directly state that that observation is in the top 5% of the distribution.
I've created a simple algorithm for a game I'm working on that creates a cave like structure. The algorithm outputs a 2 dimensional array of bits that represent the open area's. Example:
000000000000000000000000
010010000000000111100000
011110000000011111111000
011111110000011111111100
011111111001111111111110
011000000000001111000000
000000000000000000000000
(0's represent wall, 1's represent open areas)
The problem is that the algorithm can sometimes create a cave that has 2 non connected sections (as in the above example). I've written a function that gives me an array of arrays that contain all the x, y positions of the open spots for each area
My question is, given a number of lists that contain all of the x,y coordinates for each open area what is the fastest way to "connect" these area's be a corridor that is a minimum of 2 thickness wide.
(I'm writing this in javascript but even just pseudo code will help me out)
I've tried comparing the distances from every point in one area to every other area in another area, finding the two points that have the closest distance then cutting out a path from those 2 two points but this approach is way to slow I'm hoping there is another way.
Given two caves A and B, choose a point x in A and y in B (at random will do, the two closest or locally closest is better). Drill a corridor of thickness 2 between A and B (use Bresenham's algorithm). If you have multiple disconnected caves, do the above for each edge (A,B) of the minimal spanning tree of the graph of all the caves (edge weight is the length of the corridor you'll drill if you choose this edge).
Edit for the edit: to approximate the distance between two caves, you can use hill climbing. It will return the global minimum for convex caves in O(n) rather than the naive O(n2). For non-convex caves, do multiple iterations of hill climbing with initial guess chosen in random.
If you need the exactly minimal solution, you can consider first building the frontiers of your caves and then applying O(nm) algorithm. This will eliminate the need to compare distances between interior points of your caves. Then as soon as you know the distances between each pair of caves, you build the minimal spanning tree, then you drill your tunnels.
Since I don't know too much from your description, here are some hints I would consider:
How do you look for the pair of nearest points? Do you use a naive brute-force approach and thus obtain a run time of O(n*n)? Or are you using a more efficient variant taking O(n log n) time?
If you have obtained the closest points, I'd use a simple line-drawing algorithm.
Another approach might be that you generate a structure that definitely has only one single connected area. Therefore you could do the following: First you take a random cell (x,y) and set it to 1. Then, you traverse all it's neighbours and for each of them you randomly set it to 1 or leave it at 0. For each cell set to 1, you do the same, i.e. you traverse it's neighbours and set them randomly to 1 or 0. This guarantees that you won't have two separate areas.
An algorithm to ensure this could be the following (in python):
def setCell(x,y,A):
if x>=len(A) or y>=len(A[0]) or x<0 or y<0:
return
A[x][y] = 1
def getCell(x,y,A):
if x>=len(A) or y>=len(A[0]) or x<0 or y<0:
return 1
return A[x][y]
def generate(height, width):
A = [[0 for _ in xrange(width)] for _ in xrange(height)]
from random import randint
import Queue
(x,y) = (randint(0, height-1), randint(0, width-1))
setCell (x,y,A)
q = Queue.Queue()
q.put((x,y))
while not q.empty():
(x,y) = q.get()
for (nx, ny) in [(x+1,y), (x-1,y), (x,y+1), (x,y-1)]:
if randint(0,8)<=6:
if getCell(nx,ny,A)==0:
setCell(nx,ny,A)
if randint(0,2)<=1:
q.put((nx,ny))
return A
def printField(A):
for l in A:
for c in l:
print (" " if c==1 else "X"),
print ""
Then printField(generate(20,30)) does the job. Probably you'll have to adjust the parameters for random stuff so it fits your needs.