cat > /workspace/scratch/fitfinal.py <<'PY'
import json,numpy as np
from scipy.optimize import least_squares
mods=json.load(open('/workspace/scratch/colourmodels.json'));ref=np.array(json.load(open('/workspace/input/chart_reference.json'))['srgb8'])/255
lin=lambda v:np.where(v<=.04045,v/12.92,((v+.055)/1.055)**2.4)
enc=lambda v:np.where(v<=.0031308,12.92*v,1.055*np.maximum(v,0)**(1/2.4)-.055)
out={}
for s,m in mods.items():
 obs=np.array(m['observed']);x=lin(obs);tar=lin(ref)
 p0=np.r_[np.ones(3),np.linalg.lstsq(x,tar,rcond=None)[0].ravel(),np.zeros(3)]
 def fun(p):return (enc(x**p[:3]@p[3:12].reshape(3,3)+p[12:15])-ref).ravel()
 fit=least_squares(fun,p0,max_nfev=300)
 corrected=x**fit.x[:3]@fit.x[3:12].reshape(3,3)+fit.x[12:15]
 anchor=float(x[21,1]/corrected[21,1])
 print(s,fit.x[:3].round(3),'offset',fit.x[12:].round(4),'mean/max',round(abs(fun(fit.x)).mean()*255,3),round(abs(fun(fit.x)).max()*255,3))
 out[s]={'gamma':fit.x[:3].tolist(),'matrix':fit.x[3:12].reshape(3,3).tolist(),'offset':fit.x[12:].tolist(),'anchor':anchor}
json.dump(out,open('/workspace/scratch/finalcolour.json','w'),indent=2)
PY
OPENBLAS_NUM_THREADS=1 python /workspace/scratch/fitfinal.py