mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-08-04 09:18:27 +02:00
Merge pull request #1403 from alicevision/dev/distortionCalibration
New lens distortion calibration node
This commit is contained in:
commit
d3cb164316
4 changed files with 85 additions and 10 deletions
|
@ -12,7 +12,7 @@ imageExtensions = (
|
|||
# cineon:
|
||||
'.cin',
|
||||
# dds
|
||||
'dds'
|
||||
'.dds',
|
||||
# dpx:
|
||||
'.dpx',
|
||||
# gif:
|
||||
|
@ -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
|
||||
|
||||
|
@ -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__})
|
||||
|
@ -509,6 +517,7 @@ def cameraTrackingPipeline(graph, sourceSfm=None):
|
|||
featureExtractionT,
|
||||
imageMatchingT,
|
||||
featureMatchingT,
|
||||
distortionCalibrationT,
|
||||
structureFromMotionT,
|
||||
exportAnimatedCameraT,
|
||||
]
|
||||
|
@ -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)
|
||||
|
|
|
@ -34,7 +34,10 @@ Intrinsic = [
|
|||
"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"
|
||||
|
@ -42,8 +45,11 @@ Intrinsic = [
|
|||
" * 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)),
|
||||
|
@ -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()
|
||||
|
@ -182,8 +194,8 @@ The metadata needed are:
|
|||
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=',',
|
||||
|
@ -298,6 +310,7 @@ The metadata needed are:
|
|||
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
|
||||
|
@ -306,7 +319,7 @@ The metadata needed are:
|
|||
view['metadata'] = json.loads(view['metadata'])
|
||||
|
||||
sfmData = {
|
||||
"version": [1, 0, 0],
|
||||
"version": [1, 2, 0],
|
||||
"views": views + newViews,
|
||||
"intrinsics": intrinsics,
|
||||
"featureFolder": "",
|
||||
|
|
53
meshroom/nodes/aliceVision/DistortionCalibration.py
Normal file
53
meshroom/nodes/aliceVision/DistortionCalibration.py
Normal file
|
@ -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=[],
|
||||
)
|
||||
]
|
|
@ -360,8 +360,8 @@ class ViewpointWrapper(QObject):
|
|||
""" 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):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue