Last active
August 4, 2022 12:50
-
-
Save jokoon/ed40f69b4aaa821ab6c46c2c774b53af to your computer and use it in GitHub Desktop.
balancing images to fit inside a printed A3 or A2 paper sheet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# finding the best layout for several images, to reduce empty areas | |
# image are redimensionned to have either the same height or width | |
''' | |
for 1 2 3 | |
(1) (2 3) | |
(2) (1 3) | |
(3) (1 2) | |
for 1 2 3 4 | |
(1) (2 3 4) | |
(2) (1 3 4) | |
(3) (1 2 4) | |
(4) (1 2 3) | |
(1 2) (3 4) | |
(1 3) (2 4) | |
(1 4) (2 3) | |
for 1 2 3 4 5 | |
(1) (2 3 4 5) | |
(2) (1 3 4 5) | |
(3) (1 2 4 5) | |
(4) (1 2 3 5) | |
(5) (1 2 3 4) | |
(1 2) (3 4 5) | |
(1 3) (2 4 5) | |
(1 4) (2 3 5) | |
(1 5) (2 3 4) | |
(2 3) (1 4 5) | |
(2 4) (1 3 5) | |
(2 5) (1 3 4) | |
(3 4) (1 2 5) | |
(3 5) (1 2 4) | |
(4 5) (1 2 3) | |
I'm also curious to do the same for 3 sets, or maybe M sets, as long a M > N+1 | |
for 1 2 3 4 | |
(1) (2) (3 4) | |
(1) (2 3) (4) | |
(1) (2 4) (3) | |
(2) (1 3) (4) | |
(2) (1 4) (3) | |
(3) (1 4) (2) | |
''' | |
# import itertools | |
# cb = lambda n,m: [tup for tup in itertools.combinations(n,m)] | |
# def combs(n, m): | |
# print(f'n={n} m={m}') | |
# nums = [i for i in range(1,n+1)] | |
# for i in range(2, n+1): | |
# print(f' n={n} i={i} nums={nums}') | |
# print(' ', cb(nums, i)) | |
# combs(4,2) | |
from PIL import Image | |
from PIL import ImageDraw | |
from PIL import ImageFont, ImageOps | |
import sys, os, pprint | |
def write_raw_data(filename, raw_data): | |
f = open(filename,'w',encoding='utf8') | |
f.write(pprint.pformat(raw_data, width=200, indent = 4)) | |
def get_data(fname): | |
return eval(open(fname,encoding='utf8').read()) | |
def partition(collection): | |
if len(collection) == 1: | |
yield [ collection ] | |
return | |
first = collection[0] | |
for smaller in partition(collection[1:]): | |
# insert `first` in each of the subpartition's subsets | |
for n, subset in enumerate(smaller): | |
yield smaller[:n] + [[ first ] + subset] + smaller[n+1:] | |
# put `first` in its own subset | |
yield [ [ first ] ] + smaller | |
def generate_rows(collec, rows): | |
# something = list(range(1,img_count+1)) | |
# something = list(range(len(collec))) | |
something = collec | |
# for n, p in enumerate(partition(something), 1): | |
parts = [p for p in partition(something)] | |
parts_right_size = [p for p in parts if len(p) == rows] | |
parts_no_empties = [p for p in parts_right_size if all([len(l)>1 for l in p])] | |
print('generate_rows') | |
print(f' parts {len(parts)}') | |
print(f' parts_right_size {len(parts_right_size)}') | |
print(f' parts_no_empties {len(parts_no_empties)}') | |
# for p in parts: print(sorted(p)) | |
# print('parts', len(parts)) | |
return parts_no_empties | |
def make_html_report(html_content, filepath=None, title=None): | |
import sys | |
if filepath == None: | |
filepath = sys.argv[0].replace('.\\','').replace('.py','') | |
filepath +=".nogit.html" | |
print(filepath) | |
if title == None: | |
title = sys.argv[0].replace('.\\','').replace('.py','') | |
''' | |
body {font-family: monospace;} | |
''' | |
css = ''' | |
pre {white-space:pre-line;} /*works on desktop not on android*/ | |
pre {white-space:pre-wrap;word-wrap: break-word;} /*works on desktop, STILL not on android*/ | |
pre {white-space:pre-wrap;word-wrap: break-space;} /*works on desktop, STILL not on android*/ | |
div{margin:0; padding:0;font-size:0;} | |
p{ | |
margin-bottom:1px; | |
margin-top:1px; | |
} | |
.the_flex { | |
display: flex; | |
flex-flow: row wrap; | |
justify-content: center; | |
} | |
.flex_item { | |
background:#eee; | |
width:350px; | |
height:450px; | |
position: relative; | |
} | |
.container { | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
text-align: center; | |
height: 100%; | |
} | |
.no_icon { background:#ddd;} | |
.title { | |
position: absolute; | |
font-family:monospace; | |
/*font-size:0.75em;*/ | |
font-weight: bold; | |
word-break: break-word; | |
width:220px; | |
height:250px; | |
padding:0; | |
margin:0; | |
left:4px; | |
top: 4px; | |
text-shadow: | |
white 0px 0px 3px, | |
white 0px 0px 3px, | |
white 0px 0px 3px, | |
white 0px 0px 3px, | |
white 0px 0px 3px, | |
white 0px 0px 3px, | |
white 0px 0px 3px; | |
} | |
img.squary { | |
max-width:100%; | |
max-height:100%; | |
padding:0; | |
margin:0; | |
image-rendering: crisp-edges; | |
} | |
.this_one { | |
border: 2px solid red; | |
} | |
table { border-collapse:collapse;} | |
table tr td { | |
border-left: solid 2px #888; | |
border-right: solid 2px #888; | |
border-top: solid 1px #eee; | |
border-bottom: solid 1px #eee; | |
} | |
table tr:hover { background-color:#ddd; } | |
body{margin:1em; font-family:monospace;} | |
.title2 {font-size:150%; font-weight:bold;} | |
''' | |
html_template = ''' | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width"> | |
<title>---TITLE---</title> | |
<style> | |
---CSS--- | |
</style> | |
</head> | |
<body> | |
---BODY--- | |
<script type="text/javascript"> | |
window.onload = function(){ | |
makeRequest('/list', function(st, resp){ | |
// console.log(st, resp); | |
if (st == 200){ | |
selected = JSON.parse(resp); | |
console.log("got", selected.length, "selected stuffies"); | |
make_tooltips(); | |
} | |
}); | |
} | |
window.onload = function(){ | |
const getCellValue = (tr, idx) => tr.children[idx].innerText || tr.children[idx].textContent; | |
const comparer = (idx, asc) => (a, b) => ((v1, v2) => | |
v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2) ? v1 - v2 : v1.toString().localeCompare(v2) | |
)(getCellValue(asc ? a : b, idx), getCellValue(asc ? b : a, idx)); | |
// do the work... | |
document.querySelectorAll('th').forEach(th => th.addEventListener('click', (() => { | |
const table = th.closest('table'); | |
Array.from(table.querySelectorAll('tr:nth-child(n+2)')) | |
.sort(comparer(Array.from(th.parentNode.children).indexOf(th), this.asc = !this.asc)) | |
.forEach(tr => table.appendChild(tr) ); | |
}))); | |
}; | |
</script> | |
<!-- <div id="REPLACE_THIS"></div> --> | |
</body> | |
</html> | |
''' | |
html_template = html_template.replace("---TITLE---",title) | |
html_template = html_template.replace('---CSS---', css) | |
html_template = html_template.replace("\n"+" "*2,'\n') | |
html_template = html_template.replace('---BODY---', html_content) | |
open(filepath,'w', encoding = 'utf-8').write(html_template) | |
# calculate rations for 2 or 3 rows | |
def calc_ratios_old(rows, use_img, layout): | |
# rows is a list of 0 1 ratio | |
a4_ratio = 297/210 if layout == 'landscape' else 210/297 | |
size_mult = 250 | |
adjust = 1.005 | |
colors ="cyan red yellow blue green orange grey magenta".split() | |
if len(rows) == 2: | |
row1, row2 = rows | |
html = '' | |
# just the ratios | |
ratios1 = [ratio for ratio, width, height,img in row1] | |
ratios2 = [ratio for ratio, width, height,img in row2] | |
# the sums | |
ratio_sum1 = sum(ratios1) | |
ratio_sum2 = sum(ratios2) | |
# row2 must be scaled with scale2 to match width | |
# scaled2 = ratio_sum1/ratio_sum2 | |
scaled2 = ratio_sum2/ratio_sum1 | |
scaled_ratios2 = [(scaled2*ratio, width, height,img) for ratio, width, height,img in row2] | |
scaled_ratios2_sum = scaled2*ratio_sum2 | |
final_ratio = ratio_sum1/(1+scaled2) | |
# concatenating top rows with bottom scaled rows | |
all_rows = row1+scaled_ratios2 | |
all_ratios = [ratio for ratio, width, height,img in all_rows] | |
# deviations for sorting | |
ratio_dev = abs(final_ratio-a4_ratio) | |
scale_dev = abs(scaled2-1.) | |
# building a canvas rectangle to visualize empty space | |
canvas_w = ratio_sum1 | |
canvas_h = 1+scaled2 | |
canvas_ratio = canvas_w / canvas_h | |
canvas_pixels_w = canvas_w * size_mult * adjust | |
canvas_pixels_h = canvas_h * size_mult * adjust | |
canvas_pixels_a4_w = canvas_pixels_w | |
canvas_pixels_a4_h = canvas_pixels_h | |
if canvas_ratio < a4_ratio: | |
# it's wider | |
canvas_pixels_a4_w = canvas_pixels_h * a4_ratio | |
else: | |
# it's taller | |
canvas_pixels_a4_h = canvas_pixels_w / a4_ratio | |
# a4_rect = ((adjust*scaled2*a4_ratio*size_mult*rat_s1),(adjust*scaled2*size_mult*2)) | |
# root = ((adjust*scaled2*a4_ratio*size_mult*rat_s1),(adjust*scaled2*size_mult*2)) | |
# a4 = a4_rect[0]/a4_rect[1] | |
# a4_rect = ((a4_ratio*size_mult*rat_s1),(size_mult*2)) | |
html+=f'<div style="width: {canvas_pixels_a4_w}px; height:{canvas_pixels_a4_h}px; background:black;">' | |
html+=f'<div style="width: {canvas_pixels_w}px; height:{canvas_pixels_h}px; background:#ccc;">' | |
# row1 | |
# html+=f'<div style="width: {canvas_w}px; height:{size_mult}px;">' | |
html+=f'<div>' # style="width: {canvas_w}px; height:{size_mult}px;">' | |
i = 0 | |
surfaces = [] | |
for (ratio, width, height, fullpath) in (row1): | |
surfaces.append((ratio, width, height, fullpath,(size_mult * ratio * size_mult))) | |
html+=( | |
(f'<img src = "file:///{fullpath}" ' if use_img else '<div ') | |
+f'style="width: { int(size_mult * ratio)}px;' | |
f'height: {size_mult}px;' | |
f'background: {colors[i%len(colors)]};' | |
f'display:inline-block;' | |
+('"></img>' if use_img else '"></div>')) | |
i+=1 | |
html+="</div>" | |
# row2 | |
# html+=f'<div style="width: {a4_rect[0]}px; height:{size_mult}px;">' | |
html+=f'<div>' # style="width: {a4_rect[0]}px; height:{size_mult}px;">' | |
for (ratio, width, height, fullpath) in (row2): | |
surfaces.append((ratio, width, height, fullpath,(size_mult * scaled2 * ratio * scaled2 *size_mult))) | |
html+=( | |
(f'<img src = "file:///{fullpath}" ' if use_img else '<div ') | |
+f'style="width: { int(size_mult * scaled2 * ratio)}px;' | |
f'height: {int(scaled2 *size_mult)}px;' | |
f'background: {colors[i%len(colors)]};' | |
f'display:inline-block;' | |
+('"></img>' if use_img else '"></div>')) | |
i+=1 | |
html+="</div>" | |
html+="</div>" | |
html+="</div>" | |
only_surfs = [surf for (a,b,c,d, surf) in surfaces] | |
ratio_mima = max(only_surfs)/min(only_surfs) | |
html+=( | |
# f'<p>a4 {a4:.3f} (a4_ratio {a4_ratio:.3f})</p>' | |
# f'<p>a4 {a4:.3f} (a4_ratio {a4_ratio:.3f})</p>' | |
# f'<p>only_surfs {only_surfs}</p>' | |
f'<p>ratio_mima {ratio_mima}</p>' | |
# f'<p>max(only_surfs) {max(only_surfs)}</p>' | |
# f'<p>min(only_surfs) {min(only_surfs)}</p>' | |
f'<p>ratio_dev {ratio_dev:.3f} ({final_ratio:.3f} - {a4_ratio:.3f})</p>' | |
f'<p>scale_dev {scale_dev:.3f}</p>' | |
f'<p>ratio_sum1 {ratio_sum1:.3f}</p>' | |
f'<p>ratio_sum2 {ratio_sum2:.3f}</p>' | |
f'<p>scaled_ratios2_sum {scaled_ratios2_sum:.3f}</p>' | |
f'<p>scaled2 {scaled2:.3f}</p>' | |
f'<p>{len(row1)}x{len(row2)}</p>' | |
# f"<p>ratios1 {ratios1}</p>\n" | |
# f"<p>ratios2 {ratios2}</p>\n" | |
f"<p>all_ratios {all_ratios}</p>\n" | |
) | |
return ratio_dev, scale_dev, ratio_mima, html | |
elif len(rows) == 3: | |
row1, row2, row3 = rows | |
html = '' | |
# just the ratios | |
ratios1 = [ratio for ratio, width, height,img in row1] | |
# we scale all ratio so they can be relative to 1 | |
ratio_sum1 = sum(ratios1) | |
print('before', ratio_sum1, ratios1) | |
ratios1 = [ratio/ratio_sum1 for ratio in ratios1] | |
ratio_sum1 = sum(ratios1) | |
print('after ', ratio_sum1, ratios1) | |
ratios2 = [ratio for ratio, width, height,img in row2] | |
ratios3 = [ratio for ratio, width, height,img in row3] | |
# the sums | |
# ratio_sum1 = sum(ratios1) | |
ratio_sum2 = sum(ratios2) | |
ratio_sum3 = sum(ratios3) | |
# row2 must be scaled with scale2 to match width | |
scaled2 = ratio_sum1/ratio_sum2 | |
scaled3 = ratio_sum1/ratio_sum3 | |
# scaled2 = ratio_sum2/ratio_sum1 | |
# scaled2 = ratio_sum2/ratio_sum1 | |
# scaled3 = ratio_sum3/ratio_sum1 | |
scaled_ratios2 = [(scaled2*ratio, width, height,img) for ratio, width, height,img in row2] | |
scaled_ratios3 = [(scaled3*ratio, width, height,img) for ratio, width, height,img in row3] | |
# scaled_ratios2_sum = scaled2*ratio_sum2 | |
# scaled_ratios3_sum = scaled3*ratio_sum3 | |
final_ratio = ratio_sum1/(1+scaled2+scaled3) | |
# concatenating top rows with bottom scaled rows | |
all_rows = row1+scaled_ratios2+scaled_ratios3 | |
all_ratios = [ratio for ratio, width, height,img in all_rows] | |
# deviations for sorting | |
ratio_dev = abs(final_ratio-a4_ratio) | |
scale_dev = abs(scaled2-1.) | |
# building a canvas rectangle to visualize empty space | |
canvas_w = ratio_sum1 | |
canvas_h = 1+scaled2+scaled3 | |
canvas_ratio = canvas_w / canvas_h | |
canvas_pixels_w = canvas_w * size_mult * adjust | |
canvas_pixels_h = canvas_h * size_mult * adjust | |
canvas_pixels_a4_w = canvas_pixels_w | |
canvas_pixels_a4_h = canvas_pixels_h | |
if canvas_ratio < a4_ratio: | |
# it's wider | |
canvas_pixels_a4_w = canvas_pixels_h * a4_ratio | |
else: | |
# it's taller | |
canvas_pixels_a4_h = canvas_pixels_w / a4_ratio | |
# a4_rect = ((adjust*scaled2*a4_ratio*size_mult*rat_s1),(adjust*scaled2*size_mult*2)) | |
# root = ((adjust*scaled2*a4_ratio*size_mult*rat_s1),(adjust*scaled2*size_mult*2)) | |
# a4 = a4_rect[0]/a4_rect[1] | |
# a4_rect = ((a4_ratio*size_mult*rat_s1),(size_mult*2)) | |
html+=f'<div style="width: {canvas_pixels_a4_w}px; height:{canvas_pixels_a4_h}px; background:black;">' | |
html+=f'<div style="width: {canvas_pixels_w}px; height:{canvas_pixels_h}px; background:#ccc;">' | |
surfaces = [] | |
# row1 | |
# html+=f'<div style="width: {canvas_w}px; height:{size_mult}px;">' | |
html+=f'<div>' # style="width: {canvas_w}px; height:{size_mult}px;">' | |
i = 0 | |
for (ratio, width, height, fullpath) in (row1): | |
surfaces.append((ratio, width, height, fullpath, | |
(size_mult * ratio * size_mult))) | |
html+=( | |
(f'<img src = "file:///{fullpath}" ' if use_img else '<div ') | |
+f'style="width: { int(size_mult * ratio)}px;' | |
f'height: {size_mult}px;' | |
f'background: {colors[i%len(colors)]};' | |
f'display:inline-block;' | |
+('"></img>' if use_img else '"></div>')) | |
i+=1 | |
html+="</div>" | |
# row2 | |
# html+=f'<div style="width: {a4_rect[0]}px; height:{size_mult}px;">' | |
html+=f'<div>' # style="width: {a4_rect[0]}px; height:{size_mult}px;">' | |
for (ratio, width, height, fullpath) in (row2): | |
surfaces.append((ratio, width, height, fullpath, | |
(size_mult * scaled2 * ratio * scaled2 *size_mult))) | |
html+=( | |
(f'<img src = "file:///{fullpath}" ' if use_img else '<div ') | |
+f'style="width: { int(size_mult * scaled2 * ratio)}px;' | |
f'height: {int(scaled2 *size_mult)}px;' | |
f'background: {colors[i%len(colors)]};' | |
f'display:inline-block;' | |
+('"></img>' if use_img else '"></div>')) | |
i+=1 | |
html+="</div>" | |
# row3 | |
html+=f'<div>' # style="width: {a4_rect[0]}px; height:{size_mult}px;">' | |
for (ratio, width, height, fullpath) in (row3): | |
surfaces.append((ratio, width, height, fullpath, | |
(size_mult * scaled3 * ratio * scaled3 *size_mult))) | |
html+=( | |
(f'<img src = "file:///{fullpath}" ' if use_img else '<div ') | |
+f'style="width: { int(size_mult * scaled3 * ratio)}px;' | |
f'height: {int(scaled3 *size_mult)}px;' | |
f'background: {colors[i%len(colors)]};' | |
f'display:inline-block;' | |
+('"></img>' if use_img else '"></div>')) | |
i+=1 | |
html+="</div>" | |
only_surfs = [surf for (a,b,c,d, surf) in surfaces] | |
ratio_mima = max(only_surfs)/min(only_surfs) | |
html+="</div>" # canvas div | |
html+="</div>" # a4 div | |
html+=( | |
# f'<p>a4 {a4:.3f} (a4_ratio {a4_ratio:.3f})</p>' | |
# f'<p>only_surfs {only_surfs}</p>' | |
f'<p>ratio_mima {ratio_mima}</p>' | |
# f'<p>max(only_surfs) {max(only_surfs)}</p>' | |
# f'<p>min(only_surfs) {min(only_surfs)}</p>' | |
f'<p>ratio_dev {ratio_dev:.3f} ({final_ratio:.3f} - {a4_ratio:.3f})</p>' | |
f'<p>scale_dev {scale_dev:.3f}</p>' | |
f'<p>ratio_sum1 {ratio_sum1:.3f}</p>' | |
f'<p>ratio_sum2 {ratio_sum2:.3f}</p>' | |
# f'<p>scaled_ratios2_sum {scaled_ratios2_sum:.3f}</p>' | |
f'<p>scaled2 {scaled2:.3f}</p>' | |
f'<p>{len(row1)}x{len(row2)}</p>' | |
f"<p>ratios1 {ratios1}</p>\n" | |
f"<p>ratios2 {ratios2}</p>\n" | |
f"<p>all_ratios {all_ratios}</p>\n" | |
f"<p>scaled_ratios2 {[ratio for ratio, a,b,c in scaled_ratios2]}</p>\n") | |
return ratio_dev, scale_dev, ratio_mima, html | |
# calculate ratios for 2 or 3 rows | |
def calc_ratios(rows, use_img, layout): | |
# rows is a list of 0 1 ratio | |
a4_ratio = None | |
a4_ratio = 297/210 | |
if layout == 'landscape': | |
a4_ratio = 297/210 | |
elif layout == 'portrait': | |
a4_ratio = 210/297 | |
else: | |
exit('a4_ratio = None') | |
size_mult = 250 | |
adjust = 1.005 | |
colors ="cyan red yellow blue green orange grey magenta".split() | |
if len(rows) == 2: | |
row1, row2 = rows | |
html = '' | |
distrib = [[i for a,b,c,d,i in row] for row in rows] | |
# we scale all ratio so they can be relative to 1 | |
ratios1 = [ratio for ratio, width, height,img, i in row1] | |
ratio_sum1 = sum(ratios1) | |
normed_sizes1 = [(ratio/ratio_sum1, 1./ratio_sum1) for ratio in ratios1] | |
ratios2 = [ratio for ratio, width, height,img, i in row2] | |
ratio_sum2 = sum(ratios2) | |
normed_sizes2 = [(ratio/ratio_sum2, 1./ratio_sum2) for ratio in ratios2] | |
# print(f"normed_sizes1 [{', '.join([f'({w:.3f}, {h:.3f})' for w,h in normed_sizes1])}]") | |
# print(f"normed_sizes2 [{', '.join([f'({w:.3f}, {h:.3f})' for w,h in normed_sizes2])}]") | |
# print(f"normed_sizes3 [{', '.join([f'({w:.3f}, {h:.3f})' for w,h in normed_sizes3])}]") | |
# we use height later too | |
height = (normed_sizes1[0][1] + normed_sizes2[0][1]) | |
final_ratio = 1. / height | |
# print(final_ratio) | |
surfs_1 = [a*b for a,b in normed_sizes1] | |
surfs_2 = [a*b for a,b in normed_sizes2] | |
all_surfs = surfs_1+ surfs_2 | |
scale_ratio_minmax = max(all_surfs)/min(all_surfs) | |
# return None, None, None | |
# deviations for sorting | |
ratio_dev = abs(final_ratio-a4_ratio) | |
scale_dev = abs(scale_ratio_minmax-1.) | |
# building a canvas rectangle to visualize empty space | |
canvas_w = 1 | |
canvas_h = height | |
canvas_pixels_w = canvas_w * size_mult * adjust | |
canvas_pixels_h = canvas_h * size_mult * adjust | |
canvas_pixels_a4_w = canvas_pixels_w | |
canvas_pixels_a4_h = canvas_pixels_h | |
if final_ratio > a4_ratio: | |
# it's wider | |
canvas_pixels_a4_h = canvas_pixels_w / a4_ratio | |
else: | |
# it's taller | |
canvas_pixels_a4_w = canvas_pixels_h * a4_ratio | |
html+=f'<div style="width: {canvas_pixels_a4_w}px; height:{canvas_pixels_a4_h}px; background:black;">' | |
html+=f'<div style="width: {canvas_pixels_w}px; height:{canvas_pixels_h}px; background:#ccc;">' | |
surfaces = [] | |
# row1 | |
# html+=f'<div style="width: {canvas_w}px; height:{size_mult}px;">' | |
html+=f'<div>' # style="width: {canvas_w}px; height:{size_mult}px;">' | |
i = 0 | |
for (ratio, width, height, fullpath, i) in (row1): | |
html+=( | |
(f'<img src = "file:///{fullpath}" ' if use_img else '<div ') | |
+f'style="width: { int(size_mult * ratio/ratio_sum1)}px;' | |
f'height: {int(size_mult/ratio_sum1)}px;' | |
f'background: {colors[i%len(colors)]};' | |
f'display:inline-block;' | |
+('"></img>' if use_img else '"></div>')) | |
i+=1 | |
html+="</div>" | |
# row2 | |
# html+=f'<div style="width: {a4_rect[0]}px; height:{size_mult}px;">' | |
html+=f'<div>' # style="width: {a4_rect[0]}px; height:{size_mult}px;">' | |
for (ratio, width, height, fullpath, i) in (row2): | |
html+=( | |
(f'<img src = "file:///{fullpath}" ' if use_img else '<div ') | |
+f'style="width: { int(size_mult * ratio/ratio_sum2)}px;' | |
f'height: {int(size_mult/ratio_sum2)}px;' | |
f'background: {colors[i%len(colors)]};' | |
f'display:inline-block;' | |
+('"></img>' if use_img else '"></div>')) | |
i+=1 | |
html+="</div>" | |
html+="</div>" # canvas div | |
html+="</div>" # a4 div | |
html+=( | |
f'<p>scale_ratio_minmax {scale_ratio_minmax}</p>' | |
f'<p>ratio_dev {ratio_dev:.3f} ({final_ratio:.3f} - {a4_ratio:.3f})</p>' | |
f'<p>scale_dev {scale_dev:.3f}</p>' | |
# f'<p>ratio_sum1 {ratio_sum1:.3f}</p>' | |
# f'<p>ratio_sum2 {ratio_sum2:.3f}</p>' | |
# f'<p>scaled_ratios2_sum {scaled_ratios2_sum:.3f}</p>' | |
# f"<p>distrib {distrib}</p>\n" | |
# f"<p>ratios1 {ratios1}</p>\n" | |
# f"<p>ratios2 {ratios2}</p>\n" | |
) | |
return final_ratio, ratio_dev, scale_dev, html | |
elif len(rows) == 3: | |
row1, row2, row3 = rows | |
html = '' | |
distrib = [[i for a,b,c,d,i in row] for row in rows] | |
# we scale all ratio so they can be relative to 1 | |
ratios1 = [ratio for ratio, width, height,img, i in row1] | |
ratio_sum1 = sum(ratios1) | |
normed_sizes1 = [(ratio/ratio_sum1, 1./ratio_sum1) for ratio in ratios1] | |
ratios2 = [ratio for ratio, width, height,img, i in row2] | |
ratio_sum2 = sum(ratios2) | |
normed_sizes2 = [(ratio/ratio_sum2, 1./ratio_sum2) for ratio in ratios2] | |
ratios3 = [ratio for ratio, width, height,img, i in row3] | |
ratio_sum3 = sum(ratios3) | |
normed_sizes3 = [(ratio/ratio_sum3, 1./ratio_sum3) for ratio in ratios3] | |
# print(f"normed_sizes1 [{', '.join([f'({w:.3f}, {h:.3f})' for w,h in normed_sizes1])}]") | |
# print(f"normed_sizes2 [{', '.join([f'({w:.3f}, {h:.3f})' for w,h in normed_sizes2])}]") | |
# print(f"normed_sizes3 [{', '.join([f'({w:.3f}, {h:.3f})' for w,h in normed_sizes3])}]") | |
# we use height later too | |
height = (normed_sizes1[0][1] + normed_sizes2[0][1] + normed_sizes3[0][1]) | |
final_ratio = 1. / height | |
# print(final_ratio) | |
surfs_1 = [a*b for a,b in normed_sizes1] | |
surfs_2 = [a*b for a,b in normed_sizes2] | |
surfs_3 = [a*b for a,b in normed_sizes3] | |
all_surfs = surfs_1+ surfs_2+surfs_3 | |
surf_ratio_minmax = max(all_surfs)/min(all_surfs) | |
# return None, None, None | |
# deviations for sorting | |
ratio_dev = abs(final_ratio-a4_ratio) | |
surf_dev = abs(surf_ratio_minmax-1.) | |
# building a canvas rectangle to visualize empty space | |
canvas_w = 1 | |
canvas_h = height | |
canvas_pixels_w = canvas_w * size_mult * adjust | |
canvas_pixels_h = canvas_h * size_mult * adjust | |
canvas_pixels_a4_w = canvas_pixels_w | |
canvas_pixels_a4_h = canvas_pixels_h | |
if final_ratio > a4_ratio: | |
# it's wider | |
canvas_pixels_a4_h = canvas_pixels_w / a4_ratio | |
else: | |
# it's taller | |
canvas_pixels_a4_w = canvas_pixels_h * a4_ratio | |
html+=f'<div style="width: {int(canvas_pixels_a4_w)}px; height:{int(canvas_pixels_a4_h)}px; background:black;">' | |
html+=f'<div style="width: {int(canvas_pixels_w)}px; height:{int(canvas_pixels_h)}px; background:#ccc;">' | |
surfaces = [] | |
# row1 | |
# html+=f'<div style="width: {canvas_w}px; height:{size_mult}px;">' | |
html+=f'<div>' # style="width: {canvas_w}px; height:{size_mult}px;">' | |
i = 0 | |
for (ratio, width, height, fullpath, i) in (row1): | |
html+=( | |
(f'<img src="file:///{fullpath}" ' if use_img else '<div ') | |
+f'style="width: { int(size_mult * ratio/ratio_sum1)}px;' | |
f'height: {int(size_mult/ratio_sum1)}px;' | |
f'background: {colors[i%len(colors)]};' | |
f'display:inline-block;' | |
+('"></img>' if use_img else '"></div>')) | |
i+=1 | |
html+="</div>" | |
# row2 | |
# html+=f'<div style="width: {a4_rect[0]}px; height:{size_mult}px;">' | |
html+=f'<div>' # style="width: {a4_rect[0]}px; height:{size_mult}px;">' | |
for (ratio, width, height, fullpath, i) in (row2): | |
html+=( | |
(f'<img src="file:///{fullpath}" ' if use_img else '<div ') | |
+f'style="width: { int(size_mult * ratio/ratio_sum2)}px;' | |
f'height: {int(size_mult/ratio_sum2)}px;' | |
f'background: {colors[i%len(colors)]};' | |
f'display:inline-block;' | |
+('"></img>' if use_img else '"></div>')) | |
i+=1 | |
html+="</div>" | |
# row3 | |
html+=f'<div>' # style="width: {a4_rect[0]}px; height:{size_mult}px;">' | |
for (ratio, width, height, fullpath, i) in (row3): | |
html+=( | |
(f'<img src="file:///{fullpath}" ' if use_img else '<div ') | |
+f'style="width: { int(size_mult * ratio/ratio_sum3)}px;' | |
f'height: {int(size_mult/ratio_sum3)}px;' | |
f'background: {colors[i%len(colors)]};' | |
f'display:inline-block;' | |
+('"></img>' if use_img else '"></div>')) | |
i+=1 | |
html+="</div>" | |
html+="</div>" # canvas div | |
html+="</div>\n" # a4 div | |
html+=( | |
f'<p>surf_ratio_minmax {surf_ratio_minmax}</p>' | |
f'<p>ratio_dev {ratio_dev:.3f} ({final_ratio:.3f} - {a4_ratio:.3f})</p>' | |
f'<p>surf_dev {surf_dev:.3f}</p>' | |
# f'<p>ratio_sum1 {ratio_sum1:.3f}</p>' | |
# f'<p>ratio_sum2 {ratio_sum2:.3f}</p>' | |
# f'<p>ratio_sum2 {ratio_sum3:.3f}</p>' | |
# f'<p>scaled_ratios2_sum {scaled_ratios2_sum:.3f}</p>' | |
# f"<p>distrib {distrib}</p>\n" | |
# f"<p>ratios1 {ratios1}</p>\n" | |
# f"<p>ratios2 {ratios2}</p>\n" | |
# f"<p>ratios3 {ratios3}</p>\n" | |
) | |
return final_ratio, ratio_dev, surf_dev, html | |
# calculate ratios for n rows | |
def calc_ratios_any(rows, use_img, layout): | |
# rows is a list of 0 1 ratio | |
a4_ratio = None | |
if layout == 'landscape': | |
a4_ratio = 297/210 | |
elif layout == 'portrait': | |
a4_ratio = 210/297 | |
else: | |
exit('a4_ratio = None') | |
size_mult = 300 | |
adjust = 1.005 | |
colors ="cyan red yellow blue green orange grey magenta".split() | |
html = '' | |
distrib = [[i for a,b,c,d,i in row] for row in rows] | |
# ratios2 = [ratio for ratio, width, height,img, i in row2] | |
# ratio_sum2 = sum(ratios2) | |
# normed_sizes2 = [(ratio/ratio_sum2, 1./ratio_sum2) for ratio in ratios2] | |
rows_with_data = [] | |
for row in rows: | |
# aligned_row=[] | |
ratios = [ratio for ratio, width, height,img, i in row] | |
# ratio_sum = sum(ratios) | |
ratio_sum = sum([ratio for ratio, width, height,img, i in row]) | |
normed_sizes = [(ratio/ratio_sum, 1./ratio_sum) for ratio in ratios] | |
surfs_1 = [a*b for a,b in normed_sizes] | |
rows_with_data.append((row, ratios, ratio_sum, normed_sizes, surfs_1)) | |
# print(f"normed_sizes1 [{', '.join([f'({w:.3f}, {h:.3f})' for w,h in normed_sizes1])}]") | |
# print(f"normed_sizes2 [{', '.join([f'({w:.3f}, {h:.3f})' for w,h in normed_sizes2])}]") | |
# print(f"normed_sizes3 [{', '.join([f'({w:.3f}, {h:.3f})' for w,h in normed_sizes3])}]") | |
# we use height later too | |
# height = (normed_sizes1[0][1] + normed_sizes2[0][1] + normed_sizes3[0][1]) | |
height = sum([normed_sizes[0][1] for (row, ratios, ratio_sum, normed_sizes, surfs_1) in rows_with_data]) | |
final_ratio = 1. / height | |
# print(final_ratio) | |
# surfs_1 = [a*b for a,b in normed_sizes1] | |
# surfs_2 = [a*b for a,b in normed_sizes2] | |
# surfs_3 = [a*b for a,b in normed_sizes3] | |
all_surfs = sum([surfs_1 for (a,b,c,d,surfs_1) in rows_with_data], []) | |
surf_ratio_minmax = max(all_surfs)/min(all_surfs) | |
# return None, None, None | |
# deviations for sorting | |
ratio_dev = abs(final_ratio-a4_ratio) | |
surf_dev = abs(surf_ratio_minmax-1.) | |
# building a canvas rectangle to visualize empty space | |
canvas_w = 1 | |
canvas_h = height | |
canvas_pixels_w = canvas_w * size_mult * adjust | |
canvas_pixels_h = canvas_h * size_mult * adjust | |
canvas_pixels_a4_w = canvas_pixels_w | |
canvas_pixels_a4_h = canvas_pixels_h | |
if final_ratio > a4_ratio: | |
# it's wider | |
canvas_pixels_a4_h = canvas_pixels_w / a4_ratio | |
else: | |
# it's taller | |
canvas_pixels_a4_w = canvas_pixels_h * a4_ratio | |
html+=f'<div style="width: {int(canvas_pixels_a4_w)}px; height:{int(canvas_pixels_a4_h)}px; background:black;">' | |
html+=f'<div style="width: {int(canvas_pixels_w)}px; height:{int(canvas_pixels_h)}px; background:#ccc;">' | |
surfaces = [] | |
# row1 | |
# html+=f'<div style="width: {canvas_w}px; height:{size_mult}px;">' | |
html+=f'<div>' # style="width: {canvas_w}px; height:{size_mult}px;">' | |
for row, ratios, ratio_sum, normed_sizes, surfs_1 in rows_with_data: | |
# for (ratio, width, height, fullpath, i) in row: | |
for (ratio, width, height2, fullpath, i), (normed_w,normed_h) in zip(row, normed_sizes): | |
# for div | |
html+=( | |
(f'<img src="file:///{fullpath}" ' if use_img else '<div ') | |
+f'style="width: { int(size_mult * ratio/ratio_sum)}px;' | |
f'height: {int(size_mult/ratio_sum)}px;' | |
f'background: {colors[i%len(colors)]};' | |
f'display:inline-block;' | |
f'font-size:12px;' | |
+('"></img>' if use_img else f'">{normed_w:.2f} {normed_h:.2f}</div>')) | |
html+="</div>" | |
html+="</div>" # canvas div | |
html+="</div>\n" # a4 div | |
html+=( | |
f'<p>surf_dev {surf_dev:.4f}</p>' | |
f'<p>ratio_dev {ratio_dev:.4f}</p>' | |
f'<p>surf_ratio_minmax {surf_ratio_minmax:.4f}</p>' | |
f'<p>height {height}</p>' | |
f'<p>final_ratio {final_ratio:.4f}</p>' | |
# f'<p>ratio_dev {ratio_dev:.3f} ({final_ratio:.3f} - {a4_ratio:.3f})</p>' | |
) | |
return final_ratio, ratio_dev, surf_dev, html | |
# a media with a threshold | |
def partial_median(l, threshold = 0.5): | |
summed = sum(l) | |
use_this_list = sorted(l) | |
acc = 0 | |
this_one = 0 | |
median = 0 | |
for i, val in enumerate(use_this_list): | |
acc += val | |
if acc > threshold*summed: | |
median = val | |
this_one = i | |
return i, median | |
# allows to visualize a distribution | |
def mk_distr(l, parts = 10): | |
ma, mi = max(l), min(l) | |
parts_dic = {i:0 for i in range(parts+1)} | |
interval = (ma-mi)/parts | |
for i in l: | |
part_index = parts*(i-mi)/(ma-mi) | |
parts_dic[int(part_index)]+=1 | |
html='' | |
for i, n in parts_dic.items(): | |
# print(f'[{round(interval*i,1):6.2f}, {round(interval*(i+1),1):6.2f}] {n:3} {"#"*int(100*n/len(l))}') | |
html+=(f'[{round(interval*i,1):6.2f}, {round(interval*(i+1),1):6.2f}] {n:3} {"#"*int(100*n/len(l))}\n') | |
return html | |
# use everything | |
def get_best_layout(): | |
exts = [".jpg", ".png", ".gif", "jpeg"] | |
import sys, os | |
fold = sys.argv[1] | |
imgs = [] | |
use_img = 'img' in sys.argv | |
print('use_img', use_img) | |
# getting image path and shit | |
layout=None | |
if False: | |
print('parts test') | |
# for i in range(6,12): | |
for i in range(6,12): | |
elems = [j for j in range(i)] | |
print(' ',elems) | |
# for k in range(3,9): | |
for k in [4]: | |
parts = generate_rows(elems, k) | |
print(' ', k, len(parts)) | |
if 'ld' in sys.argv: | |
layout = 'landscape' | |
elif 'pt' in sys.argv: | |
layout = 'portrait' | |
else: | |
exit('layout=None') | |
print('layout', layout) | |
for a in os.listdir(fold): | |
if a[-4:] not in exts: | |
print('skipped:', a) | |
continue | |
fullpath = f'{fold}\\{a}' | |
width, height = Image.open(fullpath).size | |
# print(width/height, width, height, fullpath) | |
imgs.append((width/height, width, height, fullpath)) | |
imgs = [(ratio, w, h, path, i) for i, (ratio, w, h, path) in enumerate(imgs)] | |
rows_n = 3 | |
print('imgs', len(imgs)) | |
ints = set([str(i) for i in range(2,9)]) | |
if ints&set(sys.argv) != set(): | |
rows_n = int(list(ints&set(sys.argv))[0]) | |
print('rows_n', rows_n) | |
all_rows = generate_rows(imgs, rows_n) | |
# all_rows = generate_rows(imgs,3) | |
rows_with_diff = [] | |
# making everything with html and ratio | |
print('all_rows', len(all_rows)) | |
for i, rows in enumerate(all_rows): | |
# tup = calc_ratios(rows, use_img, layout=layout) | |
tup = calc_ratios_any(rows, use_img, layout=layout) | |
# print(repr(tup)) | |
rows_with_diff.append((tup, rows)) | |
if i %1000 == 0: | |
print(i) | |
# sorting per proximity with a4 ratio | |
# 1 is ratio_dev, 2 is surf_dev | |
only_surf_dev = [surf_dev for ((final_ratio, ratio_dev, surf_dev, html), rows) | |
in rows_with_diff] | |
only_ratio_dev = [ratio_dev for ((final_ratio, ratio_dev, surf_dev, html), rows) in rows_with_diff] | |
# filtering on scale | |
ind1, median_surf_dev = partial_median(only_surf_dev, 0.5) | |
# filtering on ratio | |
ind2, median_ratio_dev = partial_median(only_ratio_dev, 0.9) | |
def filter_rows(tup): | |
((final_ratio, ratio_dev, surf_dev, html), rows) = tup | |
# return ratio_dev < median_ratio_dev and surf_dev < median_scale_dev | |
# return True | |
# return surf_dev < 10 | |
return ratio_dev < 1 and surf_dev < 10 | |
# return ratio_dev < median_ratio_dev and surf_dev < 4 | |
def sort_this(tup): | |
((final_ratio, ratio_dev, surf_dev, html), rows) = tup | |
return ratio_dev | |
# return surf_dev | |
rows_with_diff = [a for a in filter(filter_rows,rows_with_diff)] | |
rows_with_diff.sort(key=sort_this, reverse=True) | |
# rows_with_diff.sort(key=sort_this) | |
html = "" | |
html += f"<p>rows_n {rows_n}</p>" | |
html += f"<p>all_rows {len(all_rows)}</p>" | |
html += f"<p>rows_with_diff {len(rows_with_diff)}</p>" | |
html += f"<p>median_surf_dev {median_surf_dev}</p>" | |
html += f"<p>median_ratio_dev {median_ratio_dev}</p>" | |
html += f"<pre>only_surf_dev \n{mk_distr(only_surf_dev)}</pre>" | |
html += f"<pre>only_ratio_dev \n{mk_distr(only_ratio_dev)}</pre>" | |
html+="<hr>" | |
# for (final_ratio, ratio_dev, surf_dev, html2), rows in rows_with_diff[:50]: | |
for (final_ratio, ratio_dev, surf_dev, html2), rows in rows_with_diff: | |
html+=html2 | |
html+="<hr>" | |
make_html_report(html) | |
def read_buffer_if_exists(path): | |
if os.path.exists(path): | |
return get_data(path) | |
else: | |
return False | |
def get_best_layout_for_params(layout, rows_n, imgs): | |
use_img = 'img' in sys.argv | |
fold = sys.argv[1] | |
filename = f'{fold}\\laid_out_{rows_n}_{layout}.txt' | |
print(filename) | |
# all_rows = read_buffer_if_exists(filename) | |
rows_with_diff = read_buffer_if_exists(filename) | |
# if rows_with_diff == False: | |
if True: | |
all_rows = generate_rows(imgs, rows_n) | |
rows_with_diff = [] | |
# all_rows = generate_rows(imgs,3) | |
if len(all_rows) == 0: | |
return '<p>fuck me, fuck you</p><hr size="10" color="black">' | |
# making everything with html and ratio | |
print('all_rows', len(all_rows)) | |
for i, rows in enumerate(all_rows): | |
# tup = calc_ratios(rows, use_img, layout=layout) | |
tup = calc_ratios_any(rows, use_img, layout=layout) | |
# print(repr(tup)) | |
rows_with_diff.append((tup, rows)) | |
# if i %1000 == 0: | |
# print(i) | |
# sorting per proximity with a4 ratio | |
# 1 is ratio_dev, 2 is surf_dev | |
def filter_rows(tup): | |
((final_ratio, ratio_dev, surf_dev, html), rows) = tup | |
# return ratio_dev < median_ratio_dev and surf_dev < median_scale_dev | |
# return True | |
# return surf_dev < 10 | |
return ratio_dev < 2 and surf_dev < 10 | |
# return ratio_dev < median_ratio_dev and surf_dev < 4 | |
def sort_this(tup): | |
((final_ratio, ratio_dev, surf_dev, html), rows) = tup | |
return surf_dev | |
return ratio_dev | |
rows_with_diff = [a for a in filter(filter_rows,rows_with_diff)] | |
rows_with_diff.sort(key=sort_this) | |
print('rows_with_diff', len(rows_with_diff)) | |
write_raw_data(filename, rows_with_diff) | |
if False: | |
only_surf_dev = [surf_dev for | |
((final_ratio, ratio_dev, surf_dev, html), rows) in rows_with_diff] | |
only_ratio_dev = [ratio_dev for | |
((final_ratio, ratio_dev, surf_dev, html), rows) in rows_with_diff] | |
# filtering on scale | |
ind1, median_surf_dev = partial_median(only_surf_dev, 0.5) | |
# filtering on ratio | |
ind2, median_ratio_dev = partial_median(only_ratio_dev, 0.9) | |
# rows_with_diff.sort(key=sort_this) | |
html = "" | |
html += f"<h3>rows_n {rows_n} orientation {layout}</h3>" | |
# html += f"<p>all_rows {len(all_rows)}</p>" | |
html += f"<p>rows_with_diff {len(rows_with_diff)}</p>" | |
# html += f"<p>median_surf_dev {median_surf_dev}</p>" | |
# html += f"<p>median_ratio_dev {median_ratio_dev}</p>" | |
# html += f"<pre>only_surf_dev \n{mk_distr(only_surf_dev)}</pre>" | |
# html += f"<pre>only_ratio_dev \n{mk_distr(only_ratio_dev)}</pre>" | |
html += '<hr size="3" color="black">'.join([html2 for (final_ratio, ratio_dev, surf_dev, html2), rows in rows_with_diff[:5]]) | |
html+='<hr size="10" color="black">' | |
return html | |
def get_best_layouts(): | |
# same, but for several orientation and row setups | |
exts = [".jpg", ".png", ".gif", "jpeg"] | |
import sys, os | |
fold = sys.argv[1] | |
imgs = [] | |
use_img = 'img' in sys.argv | |
print('use_img', use_img) | |
# getting image path and shit | |
layout=None | |
if False: | |
print('parts test') | |
# for i in range(6,12): | |
for i in range(6,12): | |
elems = [j for j in range(i)] | |
print(' ',elems) | |
# for k in range(3,9): | |
for k in [4]: | |
parts = generate_rows(elems, k) | |
print(' ', k, len(parts)) | |
for a in os.listdir(fold): | |
if a[-4:] not in exts: | |
print('skipped:', a) | |
continue | |
fullpath = f'{fold}\\{a}' | |
width, height = Image.open(fullpath).size | |
# print(width/height, width, height, fullpath) | |
imgs.append((width/height, width, height, fullpath)) | |
imgs = [(ratio, w, h, path, i) for i, (ratio, w, h, path) in enumerate(imgs)] | |
print('imgs', len(imgs)) | |
html = "" | |
html+=get_best_layout_for_params("portrait", 3, imgs) | |
html+=get_best_layout_for_params("portrait", 4, imgs) | |
html+=get_best_layout_for_params("landscape", 3, imgs) | |
html+=get_best_layout_for_params("landscape", 4, imgs) | |
make_html_report(html, filepath=f'{fold}\\best_layouts.html') | |
# get_best_layout() | |
get_best_layouts() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment