法線から高さを計算するサンプルソースコード

Tweet


法線から高さを計算するPoisson表面復元のサンプルプログラムです.法線のy軸は上向きにしてください.法線は,各画素に3次元ベクトルを格納したnumpyファイルにしてください.また,物体領域を表す画像ファイルも用意してください.画像中で,物体が存在している領域を白,背景を黒で指定した画像を物体領域ファイルとします.黒の値は0とし,白の値は255とし,画像中に1~254の値が存在ないようにしてください.Google Colabを開いて新規作成し,以下のソースコードのテキスト情報をコピペしてください.実行したら,ファイルアップロードのボタンが出てくるので,ボタンを押して"normal.npy"と"inside.bmp"を指定してください.計算結果のobjファイルがダウンロードされます.

normal.npy
inside.bmp

from google.colab import files
import numpy as np
from scipy.sparse import lil_matrix
from scipy.sparse.linalg import spsolve
import cv2

files.upload()
normal=np.load('normal.npy')
inside=cv2.imread('inside.bmp',cv2.IMREAD_GRAYSCALE)
inside=inside.astype(dtype=bool)
rows,cols,_=normal.shape

print('Gradient...',end='',flush=True)
GRADIENTMAX=40
gradp=np.zeros((rows,cols),dtype=np.float64)
gradq=np.zeros((rows,cols),dtype=np.float64)
for y in range(0,rows):
  for x in range(0,cols):
    nx=normal[y,x,0]
    ny=normal[y,x,1]
    nz=normal[y,x,2]
    if nz<1e-15:
      nz=1e-15
    gradp[y,x]=np.clip(-nx/nz,-GRADIENTMAX,GRADIENTMAX)
    gradq[y,x]=np.clip(-ny/nz,-GRADIENTMAX,GRADIENTMAX)
    gradq[y,x]=-gradq[y,x]
print('done')

print('Matrix...',end='',flush=True)
size=rows*cols
left=lil_matrix((size,size),dtype=np.float64)
right=lil_matrix((size,1),dtype=np.float64)
for y in range(0,rows):
  for x in range(0,cols):
    i=y*cols+x
    iy0=(y-1)*cols+x
    iy1=(y+1)*cols+x
    ix0=y*cols+(x-1)
    ix1=y*cols+(x+1)

    if inside[y,x]:
      w=0
      g=0
      if y-1>=0:
        if inside[y-1,x]:
          left[i,iy0]=-1
          w+=1
          g+=(gradq[y-1,x]+gradq[y,x])/2
      if x-1>=0:
        if inside[y,x-1]:
          left[i,ix0]=-1
          w+=1
          g+=(gradp[y,x-1]+gradp[y,x])/2
      if y+1<rows:
        if inside[y+1,x]:
          left[i,iy1]=-1
          w+=1
          g+=-(gradq[y+1,x]+gradq[y,x])/2
      if x+1<cols:
        if inside[y,x+1]:
          left[i,ix1]=-1
          w+=1
          g+=-(gradp[y,x+1]+gradp[y,x])/2
      left[i,i]=w
      right[i,0]=g
      left[i,i]+=1e-15*1
      right[i,0]+=1e-15*0
    else:
      left[i,i]=1
      right[i,0]=0
print('done')

print('Integrate...',end='',flush=True)
height=spsolve(left.tocsr(),right.tocsr())
height=height.reshape((rows,cols))
height-=np.min(height)
print('done')

print('Save...',end='',flush=True)
OBJSCALE=0.01
vertex=np.zeros((rows,cols),dtype=np.int32)
with open('shape.obj',mode='w') as f:
  f.write('o shape\n')
  num = 1
  for y in range(rows-1,-1,-1):
    for x in range(0,cols):
      if inside[y,x]:
        dx=x-cols/2
        dy=y-rows/2
        dy=-dy
        dz=height[y,x]
        dx*=OBJSCALE
        dy*=OBJSCALE
        dz*=OBJSCALE
        f.write('v %1.7f %1.7f %1.7f\n'%(dx,dy,dz))
        vertex[y,x]=num
        num+=1
      else:
        vertex[y,x]=0
  for y in range(rows-1,0,-1):
    for x in range(0,cols-1):
      i00=vertex[y,x]
      i10=vertex[y,x+1]
      i11=vertex[y-1,x+1]
      i01=vertex[y-1,x]
      if i00>0 and i11>0 and i01>0:
        f.write('f %d %d %d\n'%(i00,i11,i01))
      if i11>0 and i00>0 and i10>0:
        f.write('f %d %d %d\n'%(i11,i00,i10))
print('done')

files.download('shape.obj')


もどる