Skip to content

Commit

Permalink
Merge pull request #1403 from alicevision/dev/distortionCalibration
Browse files Browse the repository at this point in the history
New lens distortion calibration node
  • Loading branch information
fabiencastan authored Jun 1, 2021
2 parents c8dde8e + 64d2715 commit d3cb164
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 10 deletions.
13 changes: 11 additions & 2 deletions meshroom/multiview.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# cineon:
'.cin',
# dds
'dds'
'.dds',
# dpx:
'.dpx',
# gif:
Expand Down Expand Up @@ -490,6 +490,12 @@ def cameraTrackingPipeline(graph, sourceSfm=None):
sfmNodes, _ = sfmAugmentation(graph, sourceSfm)
cameraInitT, featureExtractionT, imageMatchingT, featureMatchingT, structureFromMotionT = sfmNodes

distortionCalibrationT = graph.addNewNode('DistortionCalibration',
input=cameraInitT.output)

graph.removeEdge(featureMatchingT.input)
graph.addEdge(distortionCalibrationT.outSfMData, featureMatchingT.input)

imageMatchingT.attribute("nbMatches").value = 5 # voctree nb matches
imageMatchingT.attribute("nbNeighbors").value = 10

Expand All @@ -500,6 +506,8 @@ def cameraTrackingPipeline(graph, sourceSfm=None):
structureFromMotionT.attribute("minAngleForLandmark").value = 0.5

exportAnimatedCameraT = graph.addNewNode('ExportAnimatedCamera', input=structureFromMotionT.output)
if sourceSfm:
graph.addEdge(sourceSfm.output, exportAnimatedCameraT.sfmDataFilter)

# store current pipeline version in graph header
graph.header.update({'pipelineVersion': __version__})
Expand All @@ -509,6 +517,7 @@ def cameraTrackingPipeline(graph, sourceSfm=None):
featureExtractionT,
imageMatchingT,
featureMatchingT,
distortionCalibrationT,
structureFromMotionT,
exportAnimatedCameraT,
]
Expand Down Expand Up @@ -537,7 +546,7 @@ def photogrammetryAndCameraTracking(inputImages=list(), inputViewpoints=list(),
with GraphModification(graph):
cameraInit, featureExtraction, imageMatching, featureMatching, structureFromMotion = sfmPipeline(graph)

cameraInitT, featureExtractionT, imageMatchingMultiT, featureMatchingT, structureFromMotionT, exportAnimatedCameraT = cameraTrackingPipeline(graph, structureFromMotion)
cameraInitT, featureExtractionT, imageMatchingMultiT, featureMatchingT, distortionCalibrationT, structureFromMotionT, exportAnimatedCameraT = cameraTrackingPipeline(graph, structureFromMotion)

cameraInit.viewpoints.extend([{'path': image} for image in inputImages])
cameraInit.viewpoints.extend(inputViewpoints)
Expand Down
25 changes: 19 additions & 6 deletions meshroom/nodes/aliceVision/CameraInit.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,22 @@
"So this value is used to limit the range of possible values in the optimization. \n"
"If you put -1, this value will not be used and the focal length will not be bounded.",
value=-1.0, uid=[0], range=None),
desc.FloatParam(name="pxFocalLength", label="Focal Length", description="Known/Calibrated Focal Length (in pixels)", value=-1.0, uid=[0], range=None),
desc.GroupAttribute(name="pxFocalLength", label="Focal Length", description="Known/Calibrated Focal Length (in pixels)", groupDesc=[
desc.FloatParam(name="x", label="x", description="", value=-1, uid=[], range=(0, 10000, 1)),
desc.FloatParam(name="y", label="y", description="", value=-1, uid=[], range=(0, 10000, 1)),
]),
desc.ChoiceParam(name="type", label="Camera Type",
description="Mathematical Model used to represent a camera:\n"
" * pinhole: Simplest projective camera model without optical distortion (focal and optical center).\n"
" * radial1: Pinhole camera with one radial distortion parameter\n"
" * radial3: Pinhole camera with 3 radial distortion parameters\n"
" * brown: Pinhole camera with 3 radial and 2 tangential distortion parameters\n"
" * fisheye4: Pinhole camera with 4 distortion parameters suited for fisheye optics (like 120deg FoV)\n"
" * equidistant_r3: Non-projective camera model suited for full-fisheye optics (like 180deg FoV)\n",
value="", values=['', 'pinhole', 'radial1', 'radial3', 'brown', 'fisheye4', 'equidistant_r3'], exclusive=True, uid=[0]),
" * equidistant_r3: Non-projective camera model suited for full-fisheye optics (like 180deg FoV)\n"
" * 3deanamorphic4: Pinhole camera with a 4 anamorphic distortion coefficients.\n"
" * 3declassicld: Pinhole camera with a 10 anamorphic distortion coefficients\n"
" * 3deradial4: Pinhole camera with 3DE radial4 model\n",
value="", values=['', 'pinhole', 'radial1', 'radial3', 'brown', 'fisheye4', 'equidistant_r3', '3deanamorphic4', '3declassicld', '3deradial4'], exclusive=True, uid=[0]),
desc.IntParam(name="width", label="Width", description="Image Width", value=0, uid=[], range=(0, 10000, 1)),
desc.IntParam(name="height", label="Height", description="Image Height", value=0, uid=[], range=(0, 10000, 1)),
desc.FloatParam(name="sensorWidth", label="Sensor Width", description="Sensor Width (mm)", value=36, uid=[], range=(0, 1000, 1)),
Expand Down Expand Up @@ -100,6 +106,12 @@ def readSfMData(sfmFile):
intrinsic['principalPoint'] = {}
intrinsic['principalPoint']['x'] = pp[0]
intrinsic['principalPoint']['y'] = pp[1]

f = intrinsic['pxFocalLength']
intrinsic['pxFocalLength'] = {}
intrinsic['pxFocalLength']['x'] = f[0]
intrinsic['pxFocalLength']['y'] = f[1]

# convert empty string distortionParams (i.e: Pinhole model) to empty list
if intrinsic['distortionParams'] == '':
intrinsic['distortionParams'] = list()
Expand Down Expand Up @@ -182,8 +194,8 @@ class CameraInit(desc.CommandLineNode):
name='allowedCameraModels',
label='Allowed Camera Models',
description='the Camera Models that can be attributed.',
value=['pinhole', 'radial1', 'radial3', 'brown', 'fisheye4', 'fisheye1'],
values=['pinhole', 'radial1', 'radial3', 'brown', 'fisheye4', 'fisheye1'],
value=['pinhole', 'radial1', 'radial3', 'brown', 'fisheye4', 'fisheye1', '3deanamorphic4', '3deradial4', '3declassicld'],
values=['pinhole', 'radial1', 'radial3', 'brown', 'fisheye4', 'fisheye1', '3deanamorphic4', '3deradial4', '3declassicld'],
exclusive=False,
uid=[],
joinChar=',',
Expand Down Expand Up @@ -298,6 +310,7 @@ def createViewpointsFile(self, node, additionalViews=()):
intrinsics = node.intrinsics.getPrimitiveValue(exportDefault=True)
for intrinsic in intrinsics:
intrinsic['principalPoint'] = [intrinsic['principalPoint']['x'], intrinsic['principalPoint']['y']]
intrinsic['pxFocalLength'] = [intrinsic['pxFocalLength']['x'], intrinsic['pxFocalLength']['y']]
views = node.viewpoints.getPrimitiveValue(exportDefault=False)

# convert the metadata string into a map
Expand All @@ -306,7 +319,7 @@ def createViewpointsFile(self, node, additionalViews=()):
view['metadata'] = json.loads(view['metadata'])

sfmData = {
"version": [1, 0, 0],
"version": [1, 2, 0],
"views": views + newViews,
"intrinsics": intrinsics,
"featureFolder": "",
Expand Down
53 changes: 53 additions & 0 deletions meshroom/nodes/aliceVision/DistortionCalibration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
__version__ = '2.0'

from meshroom.core import desc


class DistortionCalibration(desc.CommandLineNode):
commandLine = 'aliceVision_distortionCalibration {allParams}'
size = desc.DynamicNodeSize('input')

documentation = '''
Calibration of a camera/lens couple distortion using a full screen checkerboard
'''

inputs = [
desc.File(
name='input',
label='SfmData',
description='SfmData File',
value='',
uid=[0],
),
desc.ListAttribute(
elementDesc=desc.File(
name='lensGridImage',
label='Lens Grid Image',
description='',
value='',
uid=[0],
),
name='lensGrid',
label='Lens Grid Images',
description='Lens grid images to estimate the optical distortions.',
),
desc.ChoiceParam(
name='verboseLevel',
label='Verbose Level',
description='Verbosity level (fatal, error, warning, info, debug, trace).',
value='info',
values=['fatal', 'error', 'warning', 'info', 'debug', 'trace'],
exclusive=True,
uid=[],
),
]

outputs = [
desc.File(
name='outSfMData',
label='Output SfmData File',
description='Path to the output sfmData file',
value=desc.Node.internalFolder + 'sfmData.sfm',
uid=[],
)
]
4 changes: 2 additions & 2 deletions meshroom/ui/reconstruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,8 @@ def fieldOfView(self):
""" Get camera vertical field of view in degrees. """
if not self.solvedIntrinsics:
return None
pxFocalLength = float(self.solvedIntrinsics["pxFocalLength"])
return 2.0 * math.atan(self.orientedImageSize.height() / (2.0 * pxFocalLength)) * 180 / math.pi
pxFocalLength = self.solvedIntrinsics["pxFocalLength"]
return 2.0 * math.atan(self.orientedImageSize.height() / (2.0 * float(pxFocalLength[0]))) * 180 / math.pi

@Property(type=QUrl, notify=denseSceneParamsChanged)
def undistortedImageSource(self):
Expand Down

0 comments on commit d3cb164

Please sign in to comment.