Function to scale linear range that accentuates peaks - linear-algebra

I am monitoring an audio source and visualizing the power of each channel. I get a number out of the api (averagePowerForChannel, but the language/platform shouldn't be important for this problem).
When I add both numbers together I have a scale from -240...0. This makes sense as this is the decibel range.
I transform this scale to a linear representation of the same numbers from 0...1. (I understand that decibels are logarithmic, I leave that alone and just map the scale linearly)
I then give the 0...1 value to an alpha channel that nicely represents the audio being played.
The problem is it's not showing enough change aesthetically. The value shifts slightly and usually hovers around 80:
alpha: 0.820713937282562
alpha: 0.816978693008423
alpha: 0.830410122871399
...
As you might imagine, this just creates a mild flicker.
Instead I'd like to accentuate the peaks of the audio. I have thrown some different methods at it:
// var alpha = 1 / (1 + exp(1-linear)) // never gets fully bright, sits at about .45
// var alpha = 1 - exp2(-linear) // stays around .45
// var alpha = linear / linear + 1
These do not get me a good result, but then again I don't have any idea what I was trying to do.
Goal:
low values on the range get pushed to zero or near zero (could even shift the range down 0.2 after the curve is calculated)
Mid values are pushed lower
High values have their differences accentuated (eg: .83 is shifted very close to 1, but .81 is shifted to .5)
I think I might want an exponential curve? I'm not sure. This is a very specific problem with known inputs so a magic number solution is acceptable.

I get a satisfactory visual by shifting the range to an interesting area, then using an exponential curve to emphasize changes from there on:
var alpha = volume / maxVolume
alpha = alpha - 0.5 // Shift the range over to the area with interesting differences in our source tracks
alpha = pow(alpha, 3) // Emphases the changes in this range
alpha *= 10 // Fix the decimal place
Will accept a better/more pure answer--for this I just wiggled numbers until they got me a good visual result. I'm sorry for grossing out the CS folks here :)
The best answer may be frequency isolation, but there is enough interesting difference to make a good visual without it.

Not sure why you don't invert the logarthmic scale:
decibels = 10 * log10( value );
the inverse is just algebra:
value = pow(10.0, decibels/10);
Observe that since your decibels are between -240 and 0, the value is between 0 (exclusive) and 1 (inclusive). That should ensure your values are more sparsely distributed. However if it still isn't, then your audio configuration might not be detecting significant changes in average amplitude - a not-so-unlikely possibility. In that case you might have to look at decomposing the audio into particular frequencies and looking at the amplitude of each frequency.

Related

Mathematically calculate "Vibrancy" of a color

Im writing a program that analyzes a picture and returns the most prominent color. Its simple to get the most frequently occurring color but I've found that very often this color is a dark black/gray/brown or a white and not the "color" you would associate with the image. So I'd like to get the top 5 colors and compare them based on some metric to determine which color is most "Vibrant/Colorful" and return that color.
Saturation wont work in this case because a saturated black will be ranked above a lighter pink and Brightness/Luminance wont work because a white will be ranked about a darker red. Im want to know what metric I can use to judge this. I recognize this is kind of an obtuse question but I know of other programs that do similar things so I assume there must be some way to calculate "Vibrancy/Colorfulness". It doesn't need to be perfect just work most of the time
For what its worth I'm working in javascript but the actual code is not the issue, I just need the equation I can use and then I can implement it
There is no common way to define "vibrancy" of a color. Thus, you can try combining multiple metrics such as "saturation", "brightness", and "luminance". The lower the overall metric is, the better. The following is an example in pseudocode.
// Compare metrics to "ideal"
var deltaSat = Saturation(thisColor) - idealSat;
var deltaBright = Brightness(thisColor) - idealBrightness;
var deltaLum = Luminance(thisColor) - idealLum;
// Calculate overall distance from ideal; the lower
// the better.
var dist = sqrt((deltaSat*deltaSat) +
(deltaBright*deltaBright) +
(deltaLum*deltaLum))
(If your issue is merely that you're having trouble calculating a metric for a given color, see my page on color topics for programmers.)
If your criteria for "vibrancy" are complex enough, you should consider using machine learning techniques such as classification algorithms. In machine learning in general:
You train a model to recognize different categories (such as "vibrant" and "non-vibrant" colors in this case).
You test the model to check how well it performs.
Once the model works well, you deploy the model and use it to predict whether a color is "vibrant" or "non-vibrant".
Machine learning is quite complex, however, so you should try the simpler method given earlier in this answer.
After trying several different formulas I had the most success with the following
let colorfulness = ((max+ min) * (max-min))/max
where max & min are the highest and lowest RGB values, respectively. This page has a more detailed explanation of the formula itself.
This will return a value between 0 and 255 with 0 being least colorful and 255 being most. From running this on a bunch of different colors, I found that for my application any value above 50 was colorful enough, thought you can adjust this.
My final code is as follows
function getColorFromImage(image) {
//gets the three most commonly occuring, distinct colors in an image as RGB values, in order of their frequency
let palette = getPaletteFromImage(image, 3)
for (let color of palette){
var colorfulness = 0
//(0,0,0) will return NAN if used in the formula, if (0,0,0) leave colorfulness as its default 0
if (color != [0,0,0]){
//get min & max values
let min = Math.min(color)
let max = Math.max(color)
//calculate colorfulness of color
colorfulness = ((max+ min) * (max-min))/max
}
//compare color's colorfulness against a threshold to determine if color is "colorful" enough
//ive found 50 is a good threshold but adjust as needed
if (colorfulness >= 50.0){
return color
}
}
//if none of the colors are deemed to be sufficiently colorful, just return the most common
return palette[0]
}

Gaussian Falloff Format for Mesh Manipulation

This return below is defined as a gaussian falloff. I am not seeing e or powers of 2, so I am not sure how this is related to the Gaussian falloff, or if it is the wrong kind of fallout for me to use to get a nice smooth deformation on my mesh:
Mathf.Clamp01 (Mathf.Pow (360.0, -Mathf.Pow (distance / inRadius, 2.5) - 0.01))
where Mathf.Clamp01 returns a value between 0 and 1.
inRadius is the size of the distortion and distance is determined by:
sqrMagnitude = (vertices[i] - position).sqrMagnitude;
// Early out if too far away
if (sqrMagnitude > sqrRadius)
continue;
distance = Mathf.Sqrt(sqrMagnitude);
vertices is a list of mesh vertices, and position is the point of mesh manipulation/deformation.
My question is two parts:
1) Is the above actually a Gaussian falloff? It is expontential, but there does not seem to be the crucial e or power of 2... (Updated - I see how the graph seems to decrease smoothly in a Gaussian-like way. Perhaps this function is not the cause for problem 2 below)
2) My mesh is not deforming smoothly enough - given the above parameters, would you recommend a different Gaussian falloff?
Don't know about meshes etc. but lets see that math:
f=360^(-0.1- ((d/r)^2.5) ) looks similar enough to gausian function to make a "fall off".
i'll take the exponent apart to show a point:
f= 360^( -(d/r)^2.5)*360^(-0.1)=(0.5551)*360^( -(d/r)^2.5)
if d-->+inf then f-->0
if d-->+0 then f-->(0.5551)
the exponent of 360 is always negative (assuming 'distance' and 'inRadius' are always positive) and getting bigger (more negative) almost cubicly ( power of 2.5) with distance thus the function is "falling off" and doing it pretty fast.
Conclusion: the function is not Gausian because it behaves badly for negative input and probably for other reasons. It does exibits the "fall off" behavior you are looking for.
Changing r will change the speed of the fall-off. When d==r the f=(1/360)*0.5551.
The function will never go over 0.5551 and below zero so the "clipping" in the code is meaningless.
I don't see any see any specific reason for the constant 360 - changing it changes the slope a bit.
cheers!

Volume rendering: confusion with front-to-back compositing

In, for example, GPU Gems the front-to-back compositing equation (for colour) is
C'i = (1 - A'i-1)Ci + C'i-1
where C'i is the output accumulated colour value; A'i-1 is the accumulated alpha (opacity) value up to the previous voxel; Ci is the colour value of the current voxel; and C'i-1 is the accumulated colour value up to the previous voxel.
This formulation raises two questions to me:
Termination of front-to-back occurs once the accumulated opacity reaches approximately 1. What, then, to do about the colour channels (RGB) that go past the maximum before the opacity limit is reached? Do you just clamp the values between 0..255 (e.g. 500,1000,2000 would become 255,255,255), or look to the ratio between the channels (e.g. 500,1000,2000 would become 64,128,255).
The answer to the previous question possibly feeds into this. The colour output of the current voxel depends on one minus the accumulated opacity. What if the accumulated opacity is zero and the current voxel's opacity is zero? - the output is a completely opaque voxel, since (1 - A'i-1) = 1, even though it is supposedly a transparent voxel!?
Any hints much appreciated.
A and C should be in the range 0-1. (If you're using unsigned bytes as the representation, divide by 255.0, but note that for some volume rendering application areas this will give you insufficient control over small alpha/low opacity regions to really be satisfactory. These days it's generally just easier to compute using floats from the outset). It turns out that the alpha and color values can never escape outside this range using the your formulas.
The sequence for the ray alpha A' is A'(i) = (1-A'(i-1)).A(i) + A'(i-1) (where A(i) is the voxel alpha), so if your accumulated ray starts with A' zero, and passes through a transparent (zero A) voxel, the ray now has A' = (1-0)*0+0, which is still zero as expected.
A and C should be between 0 and 1. Use pre-multiplied alpha; you will have no overflow issues.
Hm, let's belive that C and A are between 0 and 1. as one can see sequense C'(i) = [1 - A'_(i+1)]C(i) + C'(i-1) is grows with grow of i. I think C is not color (of RGB or whatever model). Maybe it's 'greyness' of some voxel. I.e. if voxel has many voxels in front of it it should be more grey then top voxels.
So my assumption is that C_i does not describe color dirrectly. It tells us how grey we should make color of some voxel.
forgive me for my poor English and fill free to re-ask if something is not clear.
BTW: if you belive me C_0 (greyness of top voxel) should be 1, and A_0 should be 0.

2D Spaceship movement math

I'm trying to make a top-down spaceship game and I want the movement to somewhat realistic. 360 degrees with inertia, gravity, etc.
My problem is I can make the ship move 360° with inertia with no problem, but what I need to do is impose a limit for how fast the engines can go while not limiting other forces pushing/pulling the ship.
So, if the engines speed is a maximum of 500 and the ship is going 1000 from a gravity well, the ship is not going to go 1500 when it's engines are on, but if is pointing away from the angle is going then it could slow down.
For what it's worth, I'm using Construct, and all I need is the math of it.
Thanks for any help, I'm going bald from trying to figure this out.
Take a page from relative physics, where objects cannot exceed the speed of light:
(See below for my working C++ code snippet and running demo [Windows only].)
Set the constant c to the maximum speed an object can reach (the "speed of light" in your game).
If applying a force will increase the speed of the object, divide the acceleration (change in velocity) by the Lorentz factor. The if condition is not realistic in terms of special relativity, but it keeps the ship more "controllable" at high speeds.
Update: Normally, the ship will be hard to maneuver when going at speeds near c because changing direction requires an acceleration that pushes velocity past c (The Lorentz factor will end up scaling acceleration in the new direction to nearly nothing.) To regain maneuverability, use the direction that the velocity vector would have been without Lorentz scaling with the magnitude of the scaled velocity vector.
Explanation:
Definition of Lorentz factor, where v is velocity and c is the speed of light:
This works because the Lorentz factor approaches infinity as velocity increases. Objects would need an infinite amount of force applied to cross the speed of light. At lower velocities, the Lorentz factor is very close to 1, approximating classical Newtonian physics.
Graph of Lorentz factor as velocity increases:
Note: I previously tried to solve a similar problem in my asteroids game by playing with friction settings. I just came up with this solution as I read your question^^
Update: I tried implementing this and found one potential flaw: acceleration in all directions is limited as the speed of light c is approached, including deceleration! (Counter-intuitive, but does this happen with special relativity in the real world?) I guess this algorithm could be modified to account for the directions of the velocity and force vectors... The algorithm has been modified to account for directions of vectors so the ship does not "lose controllability" at high speeds.
Update: Here is a code snippet from my asteroids game, which uses the Lorentz factor to limit the speed of game objects. It works pretty well!
update:* added downloadable demo (Windows only; build from source code for other platforms) of this algorithm in action. I'm not sure if all the dependencies were included in the zip; please let me know if something's missing. And have fun^^
void CObject::applyForces()
{
// acceleration: change in velocity due to force f on object with mass m
vector2f dv = f/m;
// new velocity if acceleration dv applied
vector2f new_v = v + dv;
// only apply Lorentz factor if acceleration increases speed
if (new_v.length() > v.length())
{
// maximum speed objects may reach (the "speed of light")
const float c = 4;
float b = 1 - v.length_squared()/(c*c);
if (b <= 0) b = DBL_MIN;
double lorentz_factor = 1/sqrt(b);
dv /= lorentz_factor;
}
// apply acceleration to object's velocity
v += dv;
// Update:
// Allow acceleration in the forward direction to change the direction
// of v by using the direction of new_v (without the Lorentz factor)
// with the magnitude of v (that applies the Lorentz factor).
if (v.length() > 0)
{
v = new_v.normalized() * v.length();
}
}
Well, lets consider the realistic problem first and see why this doesn't work and how we have to differ from it. In space as long as your engines are firing, you will be accelerating. Your speed is only limited by your fuel (and in fact you can accelerate faster once you've spent some fuel because your moving less mass).
To give this model an effective maximum speed, you can consider particles in space slowing you down and causing friction. The faster you go, the more particles you're hitting and the faster you're hitting them, so eventually at some fast enough speed, you will be hitting enough particles the amount of decelerating they do exactly cancels out the amount of accelerating your engine is doing.
This realistic model does NOT sound like what you want. The reason being: You have to introduce friction. This means if you cut your engines, you will automatically start to slow down. You can probably count this as one of the unintended forces you do not want.
This leaves us with reducing the effective force of your engine to 0 upon reaching a certain speed. Now keep in mind if your going max speed in the north direction, you still want force to be able to push you in the east direction, so your engines shouldn't be cut out by raw velocity alone, but instead based on the velocity your going in the direction your engines are pointing.
So, for the math:
You want to do a cross dot product between your engine pointing vector and your velocity vector to get the effective velocity in the direction your engines are pointing. Once you have this velocity, say, 125 mph (with a max speed of 150) you can then scale back the force of your engines is exerting to (150-125)/150*(Force of Engines).
This will drastically change the velocity graph of how long it will take you to accelerate to full speed. As you approach the full speed your engines become less and less powerful. Test this out and see if it is what you want. Another approach is to just say Force of Engines = 0 if the dot product is >=150, otherwise it is full force. This will allow you to accelerate linearly to your max speed, but no further.
Now that I think about it, this model isn't perfect, because you could accelerate to 150 mph in the north direction, and then turn east and accelerate to 150 mph going in that direction for a total of 212 mph in the north east direction, so not a perfect solution.
I really do like Wongsungi's answer (with the Lorentz factor), but I wanted to note that the code can be simplified to have fewer floating-point operations.
Instead of calculating the Lorentz factor (which itself is a reciprocal) and then dividing by it, like this:
double lorentz_factor = 1/sqrt(b);
dv /= lorentz_factor;
simply multiply by the reciprocal of the Lorentz factor, like this:
double reciprocal_lorentz_factor = sqrt(b);
dv *= reciprocal_lorentz_factor;
This eliminates one floating-point operation from the code, and also eliminates the need to clamp b to DBL_MIN (it can now be clamped to 0 because we're not dividing anymore). Why divide by the reciprocal of x when you can just multiply by x?
Additionally, if you can guarantee that the magnitude of v will never exceed c, then you can eliminate the testing of b being less than zero.
Finally, you can eliminate two additional sqrt() operations by using length_squared() instead of length() in the outer if statement:
if (new_v.length_squared() > v.length_squared())
{
const float c = 4;
float b = 1 - v.length_squared()/(c*c);
if (b < 0) b = 0;
double reciprocal_lorentz_factor = sqrt(b);
dv *= reciprocal_lorentz_factor;
}
This may only make a 0.1% difference in speed, but I think the code is simpler this way.
You need to have three variables for your ship, which you update at each physics time step based on the forces that are acting on it. These will be mass, position, and velocity. (note that position and velocity are single numbers but vectors). At each physics time step you update the position based on the velocity, and the velocity based on the acceleration. you calculate the acceleration based on the forces acting on the ship (gravity, friction, engines)
Newton's equation for force is F = M*A We can rearrange that to A = F/M to get Acceleration. Basically you need to figure out how much the ship should accelerate, and in which direction (vector), then add that acceleration to the ship's velocity, and add the ship's velocity to its position.
Here is the code you should execute each physics time step (I hope you can fill in the blanks) please ask if this is not enough detail
gravity = //calculate force of gravity acting on ship from Newton's law of universal gravitation
friction = //ten percent of the ship's velocity vector, in the opposite direction
engines = 0
if (engines_are_firing)
engines = 500
forces = gravity + friction + engines
acceleration = forces / ship.mass
ship.velocity += acceleration
ship.position += velocity
redraw()
Your question is difficult for me to understand but it seems like you're not using real physics for this game. Have you considered using real physics equations such as velocity, acceleration, force, etc?
Edit:
After your edits, I think I have a better understanding. You are simply keeping track of the current velocity (or something similar) but you don't keep track of the force where that velocity comes from. The ship should not be storing any of that information (other than engine thrust) -- it should come from the environment the ship is in.
For instance, the environment has a gravity vector (directional force) so you would need to take that into account when calculating the directional force provided by the engine.
Your ship should be storing its own engine force, acceleration, and velocity.

Smooth local celing function

I have a gray-scale image and I want to make a function that
closely follows the image
is always grater than it the image
smooth at some given scale.
In other words I want a smooth function that approximates the maximum of another function in the local region while over estimating the that function at all points.
Any ideas?
My first pass at this amounted to picking the "high spots" (by comparing the image to a least-squares fit of a high order 2-D polynomial) and matching a 2-D polynomial to them and their slopes. As the first fit required more working space than I had address space, I think it's not going to work and I'm going to have to come up with something else...
What I did
My end target was to do a smooth adjustment on an image so that each local region uses the full range of values. The key realization was that an "almost perfect" function would do just fine for me.
The following procedure (that never has the max function explicitly) is what I ended up with:
Find the local mean and standard deviation at each point using a "blur" like function.
offset the image to get a zero mean. (image -= mean;)
divide each pixel by its stdev. (image /= stdev;)
the most image should now be in [-1,1] (oddly enough most of my test images have better than 99% in that range rather than the 67% that would be expected)
find the standard deviation of the whole image.
map some span +/- n*sigma to your output range.
With a little manipulation, that can be converted to find the Max function I was asking about.
Here's something that's easy; I don't know how good it is.
To get smooth, use your favorite blurring algorithm. E.g., average points within radius 5. Space cost is order the size of the image and time is the product of the image size with the square of the blurring radius.
Take the difference of each individual pixel with the original image, find the maximum value of (original[i][j] - blurred[i][j]), and add that value to every pixel in the blurred image. The sum is guaranteed to overapproximate the original image. Time cost is proportional to the size of the image, with constant additional space (if you overwrite the blurred image after computing the max.
To do better (e.g., to minimize the square error under some set of constraints), you'll have to pick some class of smooth curves and do some substantial calculations. You could try quadratic or cubic splines, but in two dimensions splines are not much fun.
My quick and dirty answer would be to start with the original image, and repeat the following process for each pixel until no changes are made:
If an overlarge delta in value between this pixel and its neighbours can be resolved by increasing the value of the pixel, do so.
If an overlarge slope change around this pixel and its neighbours can be resolved by increasing the value of the pixel, do so.
The 2D version would look something like this:
for all x:
d = img[x-1] - img[x]
if d > DMAX:
img[x] += d - DMAX
d = img[x+1] - img[x]
if d > DMAX:
img[x] += d - DMAX
dleft = img[x-1] - img[x]
dright = img[x] - img[x+1]
d = dright - dleft
if d > SLOPEMAX:
img[x] += d - SLOPEMAX
Maximum filter the image with an RxR filter, then use an order R-1 B-spline smoothing on the maximum-filtered image. The convex hull properties of the B-spline guarantee that it will be above the original image.
Can you clarify what you mean by your desire that it be "smooth" at some scale? Also, over how large of a "local region" do you want it to approximate the maximum?
Quick and dirty answer: weighted average of the source image and a windowed maximum.

Resources