animation/fourcell/calibrate.py

199 lines
5.0 KiB
Python
Raw Normal View History

2023-02-22 03:18:14 +00:00
import argparse
import sys
import cv2
import numpy as np
from os.path import exists, basename
from json import dumps
2023-02-22 20:17:03 +00:00
from common import display, draw_text, read_json
2023-02-22 03:18:14 +00:00
IN=25.4
DPI=600
W=215.9
H=279.4
ACTIONS=['generate', 'calibrate', 'scale']
MEASUREMENTS={
'A' : { 'x' : 10.0 }, # X 10mm
'B' : { 'x' : 50.0 }, # X 50mm
'C' : { 'x' : 100.0 }, # X 100mm
'D' : { 'y' : 10.0 }, # Y 10mm
'E' : { 'y' : 50.0 }, # Y 50mm
'F' : { 'y' : 100.0 } # Y 100mm
}
def create_blank(width, height):
rgb_color=(255,255,255)
image = np.zeros((height, width, 3), np.uint8)
color = tuple(reversed(rgb_color))
image[:] = color
return image
def generate (args) :
2023-02-22 20:17:03 +00:00
if args.output is None:
print(f'Please include an -o/--output image')
exit(1)
2023-02-22 03:18:14 +00:00
print('generate')
print('--------')
2023-02-22 20:17:03 +00:00
2023-02-22 03:18:14 +00:00
dpi = DPI
if args.dpi is not None :
dpi = args.dpi
dpmm = dpi / IN
2023-02-22 20:17:03 +00:00
2023-02-22 03:18:14 +00:00
print(f'DPI:{dpi}')
print(f'DPMM:{dpmm}')
2023-02-22 20:17:03 +00:00
2023-02-22 03:18:14 +00:00
w = int(W * dpmm)
h = int(H * dpmm)
blank = create_blank(w, h)
2023-02-22 20:17:03 +00:00
2023-02-22 03:18:14 +00:00
topLeftX = int(round(w * 0.2))
topLeftY = int(round(h * 0.2))
bottomRightX = int(round(w * 0.8))
bottomRightY = int(round(h * 0.8))
hundred = int(round(100.0 * dpmm))
five = int(round(2.5 * dpmm))
2023-02-22 20:17:03 +00:00
2023-02-22 03:18:14 +00:00
cv2.line(blank, (topLeftX, topLeftY,), (topLeftX + hundred, topLeftY,), [0, 0, 0], 1)
cv2.line(blank, (topLeftX, topLeftY,), (topLeftX, topLeftY + hundred,), [0, 0, 0], 1)
for i in MEASUREMENTS.keys() :
if 'x' in MEASUREMENTS[i] is not None:
x = int(round(MEASUREMENTS[i]['x'] * dpmm))
cv2.line(blank, (topLeftX + x, topLeftY + five,), (topLeftX + x, topLeftY - five,), [0, 0, 0], 1)
draw_text(blank, (topLeftX + x, topLeftY - five - int(2 * dpmm),), f'{i}', dpi)
elif 'y' in MEASUREMENTS[i] is not None:
y = int(round(MEASUREMENTS[i]['y'] * dpmm))
cv2.line(blank, (topLeftX + five, topLeftY + y,), (topLeftX - five, topLeftY + y,), [0, 0, 0], 1)
draw_text(blank, (topLeftX + five + int(2 * dpmm), topLeftY + y,), f'{i}', dpi)
2023-02-22 20:17:03 +00:00
cv2.imwrite(args.output, blank)
def calculate_scale (obj) :
xReal = 0
xMeasured = 0
yReal = 0
yMeasured = 0
for i in MEASUREMENTS.keys() :
if 'x' in MEASUREMENTS[i] is not None:
xReal += MEASUREMENTS[i]['x']
xMeasured += obj[i]
elif 'y' in MEASUREMENTS[i] is not None:
yReal += MEASUREMENTS[i]['y']
yMeasured += obj[i]
return xReal / xMeasured, yReal / yMeasured
def merge (original, newImg) :
oH, oW = original.shape[:2]
nH, nW = newImg.shape[:2]
if nH == oH and nW == oW :
return newImg
if nH > oH and nW > oW :
# total crop
offsetX = int((nW - oW) / 2)
offsetY = int((nH - oH) / 2)
return newImg[offsetY:offsetY+oH, offsetX:offsetX+oW]
if nH < oH and nW < oW :
# total placement
offsetX = int((oH - nH) / 2)
offsetY = int((oW - nW) / 2)
original[offsetY:offsetY+nH, offsetX:offsetX+nW] = newImg
return original
if nH > oH and nW <= oW :
offsetX = int((oW - nW) / 2)
offsetY = int((nH - oH) / 2)
crop = newImg[offsetY:offsetY+oH,0:nW]
original[0:oH, offsetX:offsetX+nW] = crop
return original
if nH <= oH and nW > oW :
offsetX = int((nW - oW) / 2)
offsetY = int((oH - nH) / 2)
crop = newImg[0:nH,offsetX:offsetX+oW]
original[offsetY:offsetY+nH, 0:oW] = crop
return original
2023-02-22 03:18:14 +00:00
def calibrate (args) :
print('calibrate')
print('---------')
json = {}
name = input('Printer name: ')
json['name'] = name
for m in MEASUREMENTS.keys():
val = input(f'{m}: ')
json[m] = float(val)
2023-02-22 20:17:03 +00:00
x, y = calculate_scale(json)
json['x'] = x
json['y'] = y
2023-02-22 03:18:14 +00:00
txt=dumps(json, sort_keys = True, indent = 4)
if args.output is None :
print(f'{txt}')
else :
print(f'Wrote file to {args.output}')
2023-02-22 20:17:03 +00:00
with open(args.output, 'w') as output:
output.write(txt)
2023-02-22 03:18:14 +00:00
def scale (args) :
print('scale')
print('-----')
2023-02-22 20:17:03 +00:00
if args.calibration is None :
print(f'Please provide a calibration file (-c/--calibration)')
exit(1)
if args.input is None :
print(f'Please provide an input file (-i/--input)')
exit(2)
if args.output is None:
print(f'Please provide an output file (-o/--output)')
exit(3)
obj = read_json(args.calibration)
img = cv2.imread(args.input)
height, width = img.shape[:2]
output = create_blank(width, height)
newWidth = int(round(width * obj['x']))
newHeight = int(round(height * obj['y']))
dim = (newWidth, newHeight)
resized = cv2.resize(img, dim, interpolation = cv2.INTER_AREA)
output = merge(output, resized)
cv2.imwrite(args.output, output)
2023-02-22 03:18:14 +00:00
def main () :
parser = argparse.ArgumentParser(description='Create a calibration file, apply to a scanned page, or scale image to desired DPI.')
2023-02-22 03:18:14 +00:00
parser.add_argument('action', help='The action to perform', choices=ACTIONS)
parser.add_argument('--dpi', '-d', help='DPI to generate', required=False, type=int)
parser.add_argument('--input', '-i', help='Input file', required=False)
parser.add_argument('--output', '-o', help='Output file', required=False)
2023-02-22 20:17:03 +00:00
parser.add_argument('--calibration', '-c', help='Calibration file', required=False)
2023-02-22 03:18:14 +00:00
args = parser.parse_args()
if args.action == 'generate' :
generate(args)
elif args.action == 'calibrate' :
calibrate(args)
elif args.action == 'scale' :
scale(args)
main()