Sample program of photometric stereo. Camera and object are fixed, illuminate 1 infinite-far point light source in dark room, 3 grayscale images under different light orientation are input data. Known light directions should be written in the variable "matl" in the sourcecode. Open Google Colab, create new file, copy&paste the text below. Run, push upload button, select 3 image files. Output surface normal is downloaded. The output is the numpy file with 3D vector in each pixel.
input0.bmp
input1.bmp
input2.bmp
inside.bmp
from google.colab import files
import numpy as np
import cv2
files.upload()
input=[]
input.append(cv2.imread('input0.bmp',cv2.IMREAD_GRAYSCALE))
input.append(cv2.imread('input1.bmp',cv2.IMREAD_GRAYSCALE))
input.append(cv2.imread('input2.bmp',cv2.IMREAD_GRAYSCALE))
rows,cols=input[0].shape
matl=np.zeros((3,3),dtype=np.float64)
matl[0,0]=1.58546E-17
matl[0,1]=0.258819045
matl[0,2]=0.965925826
matl[1,0]=-0.353553391
matl[1,1]=-0.353553391
matl[1,2]=0.866025404
matl[2,0]=0.353553391
matl[2,1]=-0.353553391
matl[2,2]=0.866025404
invl=np.linalg.inv(matl)
output=np.zeros((rows,cols,3),dtype=np.float32)
for y in range(0,rows):
for x in range(0,cols):
veci=np.zeros((3,1),dtype=np.float64)
veci[0,0]=input[0][y,x]
veci[1,0]=input[1][y,x]
veci[2,0]=input[2][y,x]
vecn=invl@veci
r=np.linalg.norm(vecn)
if r>1e-15:
vecn/=r
output[y,x,0]=vecn[0,0]
output[y,x,1]=vecn[1,0]
output[y,x,2]=vecn[2,0]
np.save('normal.npy',output)
files.download('normal.npy')