¿Cuál es la mejor manera de calcular el tamaño necesario para ajustar una matriz de píxeles en una imagen lo más cerca posible de una forma de rectángulo/cuadrado sin perder o agregar píxeles innecesarios?
Usando una imagen de 100 píxeles como ejemplo, el mejor tamaño para ajustar todos los píxeles sería 10x10, porque 10*10 es 100, es decir, agrega la menor cantidad de píxeles adicionales (en este caso 0). 100x1 también se adapta a todos los píxeles, pero seguro que es mucho menos rectangular que 10x10.
Y para que quepan 101 píxeles el mejor tamaño es 8x13, aunque 8*13 es 104, es la única multiplicación que no pierde ningún píxel, agrega algunos píxeles extra (3) y tiene la forma más rectangular.
Hasta ahora he podido resolver este problema con las siguientes reglas:
Al aplicar estas reglas termino con una variedad de posibilidades, siendo la mejor la que, cuando se multiplica, se aproxima más al número de píxeles.
Así es como se ve mi código actualmente:
function loopPixels(pixels, callback) { for (let x = 2; x < pixels; x++) { for (let y = 2; y < pixels; y++) { callback(x, y); } } } function getRectangle(pixels) { let result = {extraPixels: pixels}; loopPixels(pixels, (left, right) => { let half = (left/right); let total = (left*right); if (Math.round(half) == 1 && total >= pixels) { if (total-pixels < result.extraPixels) { result = {size: [left, right], extraPixels: total-pixels}; } } }) return result; } getRectangle(101) // must return [[8, 13], 3] (width, height and additional pixels) Lo que hace es mantener una variable que contiene el resultado más pequeño de (width*height)-pixels , que es la diferencia entre los valores encontrados y la cantidad de píxeles.
Aunque funciona bien para pequeñas cantidades de píxeles, con valores enormes (que probablemente arrojarían tamaños de 1000x1000), es ultralento.
¿Hay alguna razón específica para tal lentitud? y ¿sería posible obtener el mismo resultado sin usar bucles for anidados?
El siguiente código se puede hacer más eficiente pero es muy descriptivo. Toma un conteo de píxeles ( n ) y un valor k que denota las mejores k coincidencias.
Probemos 68 millones de píxeles para obtener algunas relaciones de aspecto razonables.
function getReasonableDimensions(n,k){ var max = ~~Math.sqrt(n); return Array.from({length: max}, (_,i,a) => [n%(max-i),max-i]) .sort((a,b) => a[0] - b[0]) .slice(0,k) .map(t => [Math.floor(n/t[1]), t[1]]); } var res = getReasonableDimensions(68000000,10) console.log(JSON.stringify(res));