I want to draw StackOverflow's logo with this Neural Network:
The NN should ideally become [r, g, b] = f([x, y]). In other words, it should return RGB colors for a given pair of coordinates. The FFNN works pretty well for simple shapes like a circle or a box. For example after several thousands epochs a circle looks like this:
Try it yourself: https://codepen.io/adelriosantiago/pen/PoNGeLw
However since StackOverflow's logo is far more complex even after several thousands of iterations the FFNN's results are somewhat poor:
From left to right:
StackOverflow's logo at 256 colors.
With 15 hidden neurons: The left handle never appears.
50 hidden neurons: Pretty poor result in general.
0.03 as learning rate: Shows blue in the results (blue is not in the orignal image)
A time-decreasing learning rate: The left handle appears but other details are now lost.
Try it yourself: https://codepen.io/adelriosantiago/pen/xxVEjeJ
Some parameters of interest are synaptic.Architect.Perceptron definition and learningRate value.
How can I improve the accuracy of this NN?
Could you improve the snippet? If so, please explain what you did. If there is a better NN architecture to tackle this type of job could you please provide an example?
Additional info:
Artificial Neural Network library used: Synaptic.js
To run this example in your localhost: See repository
By adding another layer, you get better results :
let perceptron = new synaptic.Architect.Perceptron(2, 15, 10, 3)
There are small improvements that you can do to improve efficiency (marginally):
Here is my optimized code:
const width = 125
const height = 125
const outputCtx = document.getElementById("output").getContext("2d")
const iterationLabel = document.getElementById("iteration")
const stopAtIteration = 3000
let perceptron = new synaptic.Architect.Perceptron(2, 15, 10, 3)
let iteration = 0
let inputData = (() => {
const tempCtx = document.createElement("canvas").getContext("2d")
tempCtx.drawImage(document.getElementById("input"), 0, 0)
return tempCtx.getImageData(0, 0, width, height)
})()
const getRGB = (img, x, y) => {
var k = (height * y + x) * 4;
return [
img.data[k] / 255, // R
img.data[k + 1] / 255, // G
img.data[k + 2] / 255, // B
//img.data[(height * y + x) * 4 + 3], // Alpha not used
]
}
const paint = () => {
var imageData = outputCtx.getImageData(0, 0, width, height)
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
var rgb = perceptron.activate([x / width, y / height])
var k = (height * y + x) * 4;
imageData.data[k] = rgb[0] * 255
imageData.data[k + 1] = rgb[1] * 255
imageData.data[k + 2] = rgb[2] * 255
imageData.data[k + 3] = 255 // Alpha not used
}
}
outputCtx.putImageData(imageData, 0, 0)
setTimeout(train, 0)
}
const train = () => {
iterationLabel.innerHTML = ++iteration
if (iteration > stopAtIteration) return
let learningRate = 0.01 / (1 + 0.0005 * iteration) // Attempt with dynamic learning rate
//let learningRate = 0.01 // Attempt with non-dynamic learning rate
for (let x = 0; x < width; x += 1) {
for (let y = 0; y < height; y += 1) {
perceptron.activate([x / width, y / height])
perceptron.propagate(learningRate, getRGB(inputData, x, y))
}
}
paint()
}
const startTraining = (btn) => {
btn.disabled = true
train()
}
EDIT : I made another CodePen with even better results:
https://codepen.io/xurei/pen/KKzWLxg
It is likely to be over-fitted BTW.
The perceptron definition:
let perceptron = new synaptic.Architect.Perceptron(2, 8, 15, 7, 3)
Taking some insights from the lecture/slides of Bhiksha Raj (from slides 62 onwards), and summarizing as below:
Each node can be assumed like a linear classifier, and combination of several nodes in a single layer of neural networks can approximate any basic shapes. For example, a rectangle can be formed by 4 nodes for each lines, assuming each nodes contributes to one line, and the shape can be approximated by the final output layer.
Falling back to the summary of complex shapes such as circle, it may require infinite nodes in a layer. Or this would likely hold true for a single layer with two disjoint shapes (A non-overlapping triangle and rectangle). However, this can still be learnt using more than 1 hidden layers. Where, the 1st layer learns the basic shapes, followed by 2nd layer approximating their disjoint combinations.
Thus, you can assume that this logo is combination of disjoint rectangles (5 rectangles for orange and 3 rectangles for grey). We can use atleast 32 nodes in 1st hidden layer and few nodes in the 2nd hidden layer. However, we don't have control over what each node learns. Hence, a few more number of neurons than required neurons should be helpful.
How does css filter contrast will work ? Is there a formula? I want to reproduce in javascript and I need a formula.
For example css filter brightness(2) take each pixel and multiply by 2, but for contrast I don't have any idea
Thanks
Multiply by 2 is a contrast filter. All multiplication and division of an images RGB values affects the contrast.
The function I like to use is a exponential ease function where the power controls the contrast.
function contrastPixel(r,g,b,power) {
r /= 255; // normalize channels
g /= 255;
b /= 255;
var rr = Math.pow(r,power); // raise each to the power
var gg = Math.pow(r,power);
var bb = Math.pow(r,power);
r = Math.floor((rr / (rr + Math.pow(1 - r, power)))*255);
g = Math.floor((gg / (gg + Math.pow(1 - g, power)))*255);
b = Math.floor((bb / (bb + Math.pow(1 - b, power)))*255);
return {r,g,b};
}
Using it
var dat = ctx.getPixelData(0,0,100,100);
var data = dat.data;
var i = 0;
while(i < data.length){
var res = contrastPixel(data[i],data[i+1],data[i+2],power);
data[i++] = res.r;
data[i++] = res.g;
data[i++] = res.b;
i++;
}
ctx.putImageData(dat,0,0);
The argument power controls the contrast.
power = 1; // no change to the image
0 < power < 1; // reduces contrast
1 < power; // increases contrast
Because the scaling of power is logarithmic it can be hard to control with a linear slider. To give the slider a linear feel use the following instructions to get a value from a slider
For a slider with a min -100 and max 100 and center 0 (0 being no contrast change) get the contrast power value using
power = Math.pow(((Number(slider.value)* 0.0498) + 5)/5,Math.log2(10));
It's not perfectly linear, and the range is limited but will cover most needs.
The test image shows the results. Center bottom is the original. Using the scale in the paragraph above from left to right slider values of -100, -50, 50, 100
BACKGROUND:
The app allows users to upload a photo of themselves and then place a pair of glasses over their face to see what it looks like. For the most part, it is working fine. After the user selects the location of the 2 pupils, I auto zoom the image based on the ratio between the distance of the pupils and then already known distance between the center points of the glasses. All is working fine there, but now I need to automatically place the glasses image over the eyes.
I am using KinectJS, but the problem is not with regards to that library or javascript.. it is more of an algorithm requirement
WHAT I HAVE TO WORK WITH:
Distance between pupils (eyes)
Distance between pupils (glasses)
Glasses width
Glasses height
Zoom ratio
SOME CODE:
//.. code before here just zooms the image, etc..
//problem is here (this is wrong, but I need to know what is the right way to calculate this)
var newLeftEyeX = self.leftEyePosition.x * ratio;
var newLeftEyeY = self.leftEyePosition.y * ratio;
//create a blue dot for testing (remove later)
var newEyePosition = new Kinetic.Circle({
radius: 3,
fill: "blue",
stroke: "blue",
strokeWidth: 0,
x: newLeftEyeX,
y: newLeftEyeY
});
self.pointsLayer.add(newEyePosition);
var glassesWidth = glassesImage.getWidth();
var glassesHeight = glassesImage.getHeight();
// this code below works perfect, as I can see the glasses center over the blue dot created above
newGlassesPosition.x = newLeftEyeX - (glassesWidth / 4);
newGlassesPosition.y = newLeftEyeY - (glassesHeight / 2);
NEEDED
A math genius to give me the algorithm to determine where the new left eye position should be AFTER the image has been resized
UPDATE
After researching this for the past 6 hours or so, I think I need to do some sort of "translate transform", but the examples I see only allow setting this by x and y amounts.. whereas I will only know the scale of the underlying image. Here's the example I found (which cannot help me):
http://tutorials.jenkov.com/html5-canvas/transformation.html
and here is something which looks interesting, but it is for Silverlight:
Get element position after transform
Is there perhaps some way to do the same in Html5 and/or KinectJS? Or perhaps I am going down the wrong road here... any ideas people?
UPDATE 2
I tried this:
// if zoomFactor > 1, then picture got bigger, so...
if (zoomFactor > 1) {
// if x = 10 (for example) and if zoomFactor = 2, that means new x should be 5
// current x / zoomFactor => 10 / 2 = 5
newLeftEyeX = self.leftEyePosition.x / zoomFactor;
// same for y
newLeftEyeY = self.leftEyePosition.y / zoomFactor;
}
else {
// else picture got smaller, so...
// if x = 10 (for example) and if zoomFactor = 0.5, that means new x should be 20
// current x * (1 / zoomFactor) => 10 * (1 / 0.5) = 10 * 2 = 20
newLeftEyeX = self.leftEyePosition.x * (1 / zoomFactor);
// same for y
newLeftEyeY = self.leftEyePosition.y * (1 / zoomFactor);
}
that didn't work, so then I tried an implementation of Rody Oldenhuis' suggestion (thanks Rody):
var xFromCenter = self.leftEyePosition.x - self.xCenter;
var yFromCenter = self.leftEyePosition.y - self.yCenter;
var angle = Math.atan2(yFromCenter, xFromCenter);
var length = Math.hypotenuse(xFromCenter, yFromCenter);
var xNew = zoomFactor * length * Math.cos(angle);
var yNew = zoomFactor * length * Math.sin(angle);
newLeftEyeX = xNew + self.xCenter;
newLeftEyeY = yNew + self.yCenter;
However, that is still not working as expected. So, I am not sure what the issue is currently. If anyone has worked with KinectJS before and has an idea of what the issue may be, please let me know.
UPDATE 3
I checked Rody's calculations on paper and they seem fine, so there is obviously something else here messing things up.. I got the coordinates of the left pupil at zoom factors 1 and 2. With those coordinates, maybe someone can figure out what the issue is:
Zoom Factor 1: x = 239, y = 209
Zoom Factor 2: x = 201, y = 133
OK, since it's an algorithmic question, I'm going to keep this generic and only write pseudo code.
I f I understand you correctly, What you want is the following:
Transform all coordinates such that the origin of your coordinate system is at the zoom center (usually, central pixel)
Compute the angle a line drawn from this new origin to a point of interest makes with the positive x-axis. Compute also the length of this line.
The new x and y coordinates after zooming are defined by elongating this line, such that the new line is the zoom factor times the length of the original line.
Transform the newly found x and y coordinates back to a coordinate system that makes sense to the computer (e.g., top left pixel = 0,0)
Repeat for all points of interest.
In pseudo-code (with formulas):
x_center = image_width/2
y_center = image_height/2
x_from_zoom_center = x_from_topleft - x_center
y_from_zoom_center = y_from_topleft - y_center
angle = atan2(y_from_zoom_center, x_from_zoom_center)
length = hypot(x_from_zoom_center, y_from_zoom_center)
x_new = zoom_factor * length * cos(angle)
y_new = zoom_factor * length * sin(angle)
x_new_topleft = x_new + x_center
y_new_topleft = y_new + y_center
Note that this assumes the number of pixels used for length and width stays the same after zooming. Note also that some rounding should take place (keep everything double precision, and only round to int after all calculations)
In the code above, atan2 is the four-quadrant arctangent, available in most programming languages, and hypot is simply sqrt(x*x + y*y), but then computed more carefully (e.g., to avoid overflow etc.), also available in most programing languages.
Is this indeed what you were after?
I'm currently working on making an interface where I have image links that lean towards the mouse cursor. This is more for fun than as a serious project, but nevertheless the information I'm learning from it will be useful in the future. Right now I have several variables setup...
diffx/y = the distance, in pixels, of the cursor from the link's original location. This value goes negative if the cursor goes to the left of or above the link's original location (already calculated).
spacex/y = the amount of distance that I want in between the cursor and the link
calcx/y = the calculated number will be added to the 'style.top' and 'style.left' of the link
calcx = diffx - spacex
calcy = diffy - spacey
link.style.top = calcx
link.style.top = calcy
If I set spacex/y = 0 the link is centered on the cursor
If I set spacex/y = diffx/y the link is set to its normal position
My goal is to have a link that leans slightly towards the cursor (maybe at max 40px from the original position) and
as the cursor gets closer to the link, the link will slowly return to its original position.
When the cursor gets within, let's say, 100px the link should (smoothly) jump towards the cursor as if to say "pick me!"
Here's what the equation would look like as a graph.
I need a way to write this as a javascript equation. I haven't taken algebra in awhile and I'm pretty sure we didn't go over anything that looked like this exactly. I'm guessing it has an exponent and a conditional in there somewhere, but I'm not quite sure. If your able to figure this out, I'd be really thankful (not to mention impressed).
Thank You for your help!
You'll definitely want a piecewise function here (the "conditional" you spoke of). The middle section appears to be an odd-powered polynomial of the form y = Ax^3 or y = Ax^5 for some small value of A (chosen to make y = 150 when x = 150). The curve appears to be essentially linear for |x| >= 200, that is y = x + B for x >= 200 and y = x - B for x <= -200. The transitions between 150 <= |x| <= 200 seem a little trickier, like a shifted exponential or quadratic. But you might start with this (pseudo code):
if (x < -150) {
y = x;
}
else if (x < 150) {
y = (1.0/22500.0) * pow(x, 3);
}
else { // x > 150
y = x;
}
Note that this ignores the transitions between x = 150 and 200 and correspondingly assumes the constants B I mentioned above are zero. But it might get you started.
Edit:
After looking at a plot of my function, I think a 5th order polynomial matches your desired shape more closely. In this case, the middle function will be y = (1.0/506250000.0) * pow(x,5). Results are below. By the way, the constant values are equivalent to 150^-2 for the cubic, and 150^-4 for the quintic function.
I agree it is probably easier to model your function if you split it in parts:
f(x) = x + 50 if x < -200
-150 if -200 <= x < -150
150*(x/150)^k if -150 <= x < 150:
150 if 150 <= x < 200
x - 50 if 200 <= x
for k some big odd number (I'd try 4-10 out...)
You can use one or combination of these:
http://drawlogic.com/2007/09/14/tweener-robert-penner-easing-equation-cheat-sheets/
Or http://www.robertpenner.com/easing/easing_demo.html
I plan to use it with JavaScript to crop an image to fit the entire window.
Edit: I'll be using a 3rd party component that only accepts the aspect ratio in the format like: 4:3, 16:9.
~12 year old edit: this kind of question is rather interesting! There is something here right? Absolutely!
I gather you're looking for an usable aspect ratio integer:integer solution like 16:9 rather than a float:1 solution like 1.77778:1.
If so, what you need to do is find the greatest common divisor (GCD) and divide both values by that. The GCD is the highest number that evenly divides both numbers. So the GCD for 6 and 10 is 2, the GCD for 44 and 99 is 11.
For example, a 1024x768 monitor has a GCD of 256. When you divide both values by that you get 4x3 or 4:3.
A (recursive) GCD algorithm:
function gcd (a,b):
if b == 0:
return a
return gcd (b, a mod b)
In C:
static int gcd (int a, int b) {
return (b == 0) ? a : gcd (b, a%b);
}
int main(void) {
printf ("gcd(1024,768) = %d\n",gcd(1024,768));
}
And here's some complete HTML/Javascript which shows one way to detect the screen size and calculate the aspect ratio from that. This works in FF3, I'm unsure what support other browsers have for screen.width and screen.height.
<html><body>
<script type="text/javascript">
function gcd (a, b) {
return (b == 0) ? a : gcd (b, a%b);
}
var w = screen.width;
var h = screen.height;
var r = gcd (w, h);
document.write ("<pre>");
document.write ("Dimensions = ", w, " x ", h, "<br>");
document.write ("Gcd = ", r, "<br>");
document.write ("Aspect = ", w/r, ":", h/r);
document.write ("</pre>");
</script>
</body></html>
It outputs (on my weird wide-screen monitor):
Dimensions = 1680 x 1050
Gcd = 210
Aspect = 8:5
Others that I tested this on:
Dimensions = 1280 x 1024
Gcd = 256
Aspect = 5:4
Dimensions = 1152 x 960
Gcd = 192
Aspect = 6:5
Dimensions = 1280 x 960
Gcd = 320
Aspect = 4:3
Dimensions = 1920 x 1080
Gcd = 120
Aspect = 16:9
I wish I had that last one at home but, no, it's a work machine unfortunately.
What you do if you find out the aspect ratio is not supported by your graphic resize tool is another matter. I suspect the best bet there would be to add letter-boxing lines (like the ones you get at the top and bottom of your old TV when you're watching a wide-screen movie on it). I'd add them at the top/bottom or the sides (whichever one results in the least number of letter-boxing lines) until the image meets the requirements.
One thing you may want to consider is the quality of a picture that's been changed from 16:9 to 5:4 - I still remember the incredibly tall, thin cowboys I used to watch in my youth on television before letter-boxing was introduced. You may be better off having one different image per aspect ratio and just resize the correct one for the actual screen dimensions before sending it down the wire.
aspectRatio = width / height
if that is what you're after. You can then multiply it by one of the dimensions of the target space to find out the other (that maintains the ratio)
e.g.
widthT = heightT * aspectRatio
heightT = widthT / aspectRatio
paxdiablo's answer is great, but there are a lot of common resolutions that have just a few more or less pixels in a given direction, and the greatest common divisor approach gives horrible results to them.
Take for example the well behaved resolution of 1360x765 which gives a nice 16:9 ratio using the gcd approach. According to Steam, this resolution is only used by 0.01% of it's users, while 1366x768 is used by a whoping 18.9%. Let's see what we get using the gcd approach:
1360x765 - 16:9 (0.01%)
1360x768 - 85:48 (2.41%)
1366x768 - 683:384 (18.9%)
We'd want to round up that 683:384 ratio to the closest, 16:9 ratio.
I wrote a python script that parses a text file with pasted numbers from the Steam Hardware survey page, and prints all resolutions and closest known ratios, as well as the prevalence of each ratio (which was my goal when I started this):
# Contents pasted from store.steampowered.com/hwsurvey, section 'Primary Display Resolution'
steam_file = './steam.txt'
# Taken from http://upload.wikimedia.org/wikipedia/commons/thumb/f/f0/Vector_Video_Standards4.svg/750px-Vector_Video_Standards4.svg.png
accepted_ratios = ['5:4', '4:3', '3:2', '8:5', '5:3', '16:9', '17:9']
#-------------------------------------------------------
def gcd(a, b):
if b == 0: return a
return gcd (b, a % b)
#-------------------------------------------------------
class ResData:
#-------------------------------------------------------
# Expected format: 1024 x 768 4.37% -0.21% (w x h prevalence% change%)
def __init__(self, steam_line):
tokens = steam_line.split(' ')
self.width = int(tokens[0])
self.height = int(tokens[2])
self.prevalence = float(tokens[3].replace('%', ''))
# This part based on pixdiablo's gcd answer - http://stackoverflow.com/a/1186465/828681
common = gcd(self.width, self.height)
self.ratio = str(self.width / common) + ':' + str(self.height / common)
self.ratio_error = 0
# Special case: ratio is not well behaved
if not self.ratio in accepted_ratios:
lesser_error = 999
lesser_index = -1
my_ratio_normalized = float(self.width) / float(self.height)
# Check how far from each known aspect this resolution is, and take one with the smaller error
for i in range(len(accepted_ratios)):
ratio = accepted_ratios[i].split(':')
w = float(ratio[0])
h = float(ratio[1])
known_ratio_normalized = w / h
distance = abs(my_ratio_normalized - known_ratio_normalized)
if (distance < lesser_error):
lesser_index = i
lesser_error = distance
self.ratio_error = distance
self.ratio = accepted_ratios[lesser_index]
#-------------------------------------------------------
def __str__(self):
descr = str(self.width) + 'x' + str(self.height) + ' - ' + self.ratio + ' - ' + str(self.prevalence) + '%'
if self.ratio_error > 0:
descr += ' error: %.2f' % (self.ratio_error * 100) + '%'
return descr
#-------------------------------------------------------
# Returns a list of ResData
def parse_steam_file(steam_file):
result = []
for line in file(steam_file):
result.append(ResData(line))
return result
#-------------------------------------------------------
ratios_prevalence = {}
data = parse_steam_file(steam_file)
print('Known Steam resolutions:')
for res in data:
print(res)
acc_prevalence = ratios_prevalence[res.ratio] if (res.ratio in ratios_prevalence) else 0
ratios_prevalence[res.ratio] = acc_prevalence + res.prevalence
# Hack to fix 8:5, more known as 16:10
ratios_prevalence['16:10'] = ratios_prevalence['8:5']
del ratios_prevalence['8:5']
print('\nSteam screen ratio prevalences:')
sorted_ratios = sorted(ratios_prevalence.items(), key=lambda x: x[1], reverse=True)
for value in sorted_ratios:
print(value[0] + ' -> ' + str(value[1]) + '%')
For the curious, these are the prevalence of screen ratios amongst Steam users (as of October 2012):
16:9 -> 58.9%
16:10 -> 24.0%
5:4 -> 9.57%
4:3 -> 6.38%
5:3 -> 0.84%
17:9 -> 0.11%
I guess you want to decide which of 4:3 and 16:9 is the best fit.
function getAspectRatio(width, height) {
var ratio = width / height;
return ( Math.abs( ratio - 4 / 3 ) < Math.abs( ratio - 16 / 9 ) ) ? '4:3' : '16:9';
}
James Farey's best rational approximation algorithm with adjustable level of fuzziness ported to Javascript from the aspect ratio calculation code originally written in python.
The method takes a float (width/height) and an upper limit for the fraction numerator/denominator.
In the example below I am setting an upper limit of 50 because I need 1035x582 (1.77835051546) to be treated as 16:9 (1.77777777778) rather than 345:194 which you get with the plain gcd algorithm listed in other answers.
function aspect_ratio(val, lim) {
var lower = [0, 1];
var upper = [1, 0];
while (true) {
var mediant = [lower[0] + upper[0], lower[1] + upper[1]];
if (val * mediant[1] > mediant[0]) {
if (lim < mediant[1]) {
return upper;
}
lower = mediant;
} else if (val * mediant[1] == mediant[0]) {
if (lim >= mediant[1]) {
return mediant;
}
if (lower[1] < upper[1]) {
return lower;
}
return upper;
} else {
if (lim < mediant[1]) {
return lower;
}
upper = mediant;
}
}
}
console.log(aspect_ratio(801/600, 50));
console.log(aspect_ratio(1035/582, 50));
console.log(aspect_ratio(2560/1441, 50));
Just in case you're a performance freak...
The Fastest way (in JavaScript) to compute a rectangle ratio it o use a true binary Great Common Divisor algorithm.
(All speed and timing tests have been done by others, you can check one benchmark here: https://lemire.me/blog/2013/12/26/fastest-way-to-compute-the-greatest-common-divisor/)
Here is it:
/* the binary Great Common Divisor calculator */
function gcd (u, v) {
if (u === v) return u;
if (u === 0) return v;
if (v === 0) return u;
if (~u & 1)
if (v & 1)
return gcd(u >> 1, v);
else
return gcd(u >> 1, v >> 1) << 1;
if (~v & 1) return gcd(u, v >> 1);
if (u > v) return gcd((u - v) >> 1, v);
return gcd((v - u) >> 1, u);
}
/* returns an array with the ratio */
function ratio (w, h) {
var d = gcd(w,h);
return [w/d, h/d];
}
/* example */
var r1 = ratio(1600, 900);
var r2 = ratio(1440, 900);
var r3 = ratio(1366, 768);
var r4 = ratio(1280, 1024);
var r5 = ratio(1280, 720);
var r6 = ratio(1024, 768);
/* will output this:
r1: [16, 9]
r2: [8, 5]
r3: [683, 384]
r4: [5, 4]
r5: [16, 9]
r6: [4, 3]
*/
Here is my solution it is pretty straight forward since all I care about is not necessarily GCD or even accurate ratios: because then you get weird things like 345/113 which are not human comprehensible.
I basically set acceptable landscape, or portrait ratios and their "value" as a float... I then compare my float version of the ratio to each and which ever has the lowest absolute value difference is the ratio closest to the item. That way when the user makes it 16:9 but then removes 10 pixels from the bottom it still counts as 16:9...
accepted_ratios = {
'landscape': (
(u'5:4', 1.25),
(u'4:3', 1.33333333333),
(u'3:2', 1.5),
(u'16:10', 1.6),
(u'5:3', 1.66666666667),
(u'16:9', 1.77777777778),
(u'17:9', 1.88888888889),
(u'21:9', 2.33333333333),
(u'1:1', 1.0)
),
'portrait': (
(u'4:5', 0.8),
(u'3:4', 0.75),
(u'2:3', 0.66666666667),
(u'10:16', 0.625),
(u'3:5', 0.6),
(u'9:16', 0.5625),
(u'9:17', 0.5294117647),
(u'9:21', 0.4285714286),
(u'1:1', 1.0)
),
}
def find_closest_ratio(ratio):
lowest_diff, best_std = 9999999999, '1:1'
layout = 'portrait' if ratio < 1.0 else 'landscape'
for pretty_str, std_ratio in accepted_ratios[layout]:
diff = abs(std_ratio - ratio)
if diff < lowest_diff:
lowest_diff = diff
best_std = pretty_str
return best_std
def extract_ratio(width, height):
try:
divided = float(width)/float(height)
if divided == 1.0: return '1:1'
return find_closest_ratio(divided)
except TypeError:
return None
You can always start by making a lookup table based on common aspect ratios. Check https://en.wikipedia.org/wiki/Display_aspect_ratio Then you can simply do the division
For real life problems, you can do something like below
let ERROR_ALLOWED = 0.05
let STANDARD_ASPECT_RATIOS = [
[1, '1:1'],
[4/3, '4:3'],
[5/4, '5:4'],
[3/2, '3:2'],
[16/10, '16:10'],
[16/9, '16:9'],
[21/9, '21:9'],
[32/9, '32:9'],
]
let RATIOS = STANDARD_ASPECT_RATIOS.map(function(tpl){return tpl[0]}).sort()
let LOOKUP = Object()
for (let i=0; i < STANDARD_ASPECT_RATIOS.length; i++){
LOOKUP[STANDARD_ASPECT_RATIOS[i][0]] = STANDARD_ASPECT_RATIOS[i][1]
}
/*
Find the closest value in a sorted array
*/
function findClosest(arrSorted, value){
closest = arrSorted[0]
closestDiff = Math.abs(arrSorted[0] - value)
for (let i=1; i<arrSorted.length; i++){
let diff = Math.abs(arrSorted[i] - value)
if (diff < closestDiff){
closestDiff = diff
closest = arrSorted[i]
} else {
return closest
}
}
return arrSorted[arrSorted.length-1]
}
/*
Estimate the aspect ratio based on width x height (order doesn't matter)
*/
function estimateAspectRatio(dim1, dim2){
let ratio = Math.max(dim1, dim2) / Math.min(dim1, dim2)
if (ratio in LOOKUP){
return LOOKUP[ratio]
}
// Look by approximation
closest = findClosest(RATIOS, ratio)
if (Math.abs(closest - ratio) <= ERROR_ALLOWED){
return '~' + LOOKUP[closest]
}
return 'non standard ratio: ' + Math.round(ratio * 100) / 100 + ':1'
}
Then you simply give the dimensions in any order
estimateAspectRatio(1920, 1080) // 16:9
estimateAspectRatio(1920, 1085) // ~16:9
estimateAspectRatio(1920, 1150) // non standard ratio: 1.65:1
estimateAspectRatio(1920, 1200) // 16:10
estimateAspectRatio(1920, 1220) // ~16:10
As an alternative solution to the GCD searching, I suggest you to check against a set of standard values. You can find a list on Wikipedia.
Im assuming your talking about video here, in which case you may also need to worry about pixel aspect ratio of the source video. For example.
PAL DV comes in a resolution of 720x576. Which would look like its 4:3. Now depending on the Pixel aspect ratio (PAR) the screen ratio can be either 4:3 or 16:9.
For more info have a look here http://en.wikipedia.org/wiki/Pixel_aspect_ratio
You can get Square pixel Aspect Ratio, and a lot of web video is that, but you may want to watch out of the other cases.
Hope this helps
Mark
Based on the other answers, here is how I got the numbers I needed in Python;
from decimal import Decimal
def gcd(a,b):
if b == 0:
return a
return gcd(b, a%b)
def closest_aspect_ratio(width, height):
g = gcd(width, height)
x = Decimal(str(float(width)/float(g)))
y = Decimal(str(float(height)/float(g)))
dec = Decimal(str(x/y))
return dict(x=x, y=y, dec=dec)
>>> closest_aspect_ratio(1024, 768)
{'y': Decimal('3.0'),
'x': Decimal('4.0'),
'dec': Decimal('1.333333333333333333333333333')}
function ratio(w, h) {
function mdc(w, h) {
var resto;
do {
resto = w % h;
w = h;
h = resto;
} while (resto != 0);
return w;
}
var mdc = mdc(w, h);
var width = w/mdc;
var height = h/mdc;
console.log(width + ':' + height);
}
ratio(1920, 1080);
I think this does what you are asking for:
webdeveloper.com - decimal to fraction
Width/height gets you a decimal, converted to a fraction with ":" in place of '/' gives you a "ratio".
This algorithm in Python gets you part of the way there.
Tell me what happens if the windows is a funny size.
Maybe what you should have is a list of all acceptable ratios (to the 3rd party component). Then, find the closest match to your window and return that ratio from the list.
bit of a strange way to do this but use the resolution as the aspect.
E.G.
1024:768
or you can try
var w = screen.width;
var h = screen.height;
for(var i=1,asp=w/h;i<5000;i++){
if(asp*i % 1==0){
i=9999;
document.write(asp*i,":",1*i);
}
}
in my case i want something like
[10,5,15,20,25] -> [ 2, 1, 3, 4, 5 ]
function ratio(array){
let min = Math.min(...array);
let ratio = array.map((element)=>{
return element/min;
});
return ratio;
}
document.write(ratio([10,5,15,20,25])); // [ 2, 1, 3, 4, 5 ]
I believe that aspect ratio is width divided by height.
r = w/h
Width / Height
?