Skip to content

Instantly share code, notes, and snippets.

@jokoon
Last active August 4, 2022 12:50
Show Gist options
  • Save jokoon/ed40f69b4aaa821ab6c46c2c774b53af to your computer and use it in GitHub Desktop.
Save jokoon/ed40f69b4aaa821ab6c46c2c774b53af to your computer and use it in GitHub Desktop.
balancing images to fit inside a printed A3 or A2 paper sheet
# 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