diff --git a/.dockerignore b/.dockerignore index 83f5d8131..ee425a432 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,8 +11,9 @@ !doc !LICENSE !opensfm +!pyproject.toml +!conda.yml !README.md -!requirements.txt !setup.cfg !setup.py !viewer diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..d103a476a --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,52 @@ +# OpenSfM AI Coding Instructions + +## 1. Architecture & "Big Picture" +OpenSfM is a Structure-from-Motion library with a hybrid architecture: +- **Core Logic (C++)**: Heavy computational tasks (geometry, bundle adjustment, features) are implemented in C++ under `opensfm/src/`. These are exposed to Python as compiled modules (e.g., `pygeometry`, `pymap`, `pfm`) using **PyBind11**. +- **Pipeline Layout (Python)**: The application logic, pipeline orchestration, and user interface are in Python (`opensfm/` package). +- **Data Abstraction**: The `DataSet` class (`opensfm/dataset.py`) is the central definition for filesystem interactions. All pipeline stages read/write to a hardcoded directory structure (`images/`, `config.yaml`, `reconstruction.json`) managed by this class. +- **State Management**: The `Reconstruction` class (`opensfm/types.py`) wraps the C++ `pymap.Map` object. This pattern (Python class holding a C++ handle) is pervasive. + +## 2. Key Developer Workflows + +### Building +The project uses `scikit-build-core` to compile C++ extensions. +- **Full Build**: `pip install -e .[test]` (Builds C++ extensions and tests, and installs the package in editable mode). +- **Rebuild C++**: Re-running `pip install -e .` is usually required after changing `.cc` files. +- **Dependencies**: Managed via `pyproject.toml` for managing Python dependencies. Outer envinvironment (C++ toolchain, libraries) is set up through `conda` and the `conda.yml` file. Any change to `conda.yml` requires recreating the conda environment with `conda env create --file conda.yml --yes`, then activating it with `conda activate opensfm`. Note that before running the build commands, the conda environment must be activated once. + +### Testing +- **Framework**: `pytest` is used for all tests. +- **Location**: `opensfm/test/` for Python tests. C++ tests are in `./cmake_build`. +- **Synthetic Data**: Python tests heavily rely on synthetic scenes generated in `opensfm/test/conftest.py`. Look for fixtures like `scene_synthetic` or `scene_synthetic_cube`. +- **Run Tests**: `pytest opensfm/test/` for Python tests. C++ tests can be run via `ctest` with `cd cmake_build && ctest --output-on-failure && cd ..`. + +### Running the Pipeline +- **Entry Point**: `bin/opensfm` (shell script) -> `bin/opensfm_main.py`. +- **Commands**: Each CLI command (e.g., `detect_features`, `reconstruct`) is implemented as a module in `opensfm/commands/`. +- **Example**: `bin/opensfm reconstruct path/to/dataset` invokes the `run()` method in `opensfm/commands/reconstruct.py`. + +## 3. Coding Conventions & Patterns + +### Python/C++ Interop +- **Wrappers**: Do not use C++ objects directly if a Python wrapper exists (e.g., use `opensfm.types.Reconstruction` instead of `opensfm.pymap.Map` where possible). +- **Extensions**: C++ extensions are imported from the root package (e.g., `from opensfm import pygeometry`). + +### Type Hinting +- **Strictness**: The codebase uses `pyre-strict`. Ensure all new code has complete type annotations. A comment `# pyre-strict` is often present at the top of files. + +### Configuration +- **Definition**: Default parameters are in `opensfm/config.py` (dataclass `OpenSfMConfig`). +- **Overrides**: Parameters are overridden by `config.yaml` in the dataset directory. +- **Access**: Access config values via `data.config['param_name']` (where `data` is a `DataSet` instance). + +## 4. Essential Files +- `opensfm/dataset.py`: **READ THIS FIRST** when dealing with file I/O. Defines where every file lives. +- `opensfm/types.py`: Defines key data structures (`Reconstruction`, `Shot`, `Camera`). +- `opensfm/config.py`: Documentation for all tunable parameters. +- `opensfm/src/map/map.cc`: The backing C++ implementation for `Reconstruction`. +- `opensfm/commands/`: Implementation of individual pipeline steps. + +## 5. Common Pitfalls +- **Direct File Access**: Avoid manual `open()` calls. Use `dataset.load_*` and `dataset.save_*` methods to ensure consistency with the expected directory structure. +- **Geometry Types**: Be careful with rotation representations (angle-axis vs matrices). `pygeometry` exposes helper functions; check `opensfm/src/geometry/` for implementation details if behavior is unclear. diff --git a/.github/workflows/conda.yml b/.github/workflows/conda.yml new file mode 100644 index 000000000..b65d62498 --- /dev/null +++ b/.github/workflows/conda.yml @@ -0,0 +1,37 @@ +name: Conda + +on: [push, pull_request] + +jobs: + + build-test: + name: Build conda environment and run tests + + strategy: + matrix: + os: [ubuntu-24.04, ubuntu-22.04, macos-latest] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Setup Miniconda + uses: conda-incubator/setup-miniconda@v3 + with: + environment-file: conda.yml + activate-environment: opensfm + + - name: Build OpenSfM + shell: bash -l {0} + run: pip install -e .[test] + + - name: Run C++ tests + shell: bash -l {0} + run: cd cmake_build && ctest + + - name: Run Python tests + shell: bash -l {0} + run: export LD_PRELOAD=$CONDA_PREFIX/lib/libtcmalloc.so && python -m pytest diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index 4a35eab12..000000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Docker CI - -on: [push, pull_request] - -jobs: - - build-test: - name: Build docker and run tests - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - - name: Build the Docker image - run: docker build . --file Dockerfile --tag mapillary/opensfm:$GITHUB_SHA - - - name: Run C++ tests - run: docker run mapillary/opensfm:$GITHUB_SHA /bin/sh -c "cd cmake_build && ctest" - - - name: Run Python tests - run: docker run mapillary/opensfm:$GITHUB_SHA python3 -m pytest diff --git a/.github/workflows/docker_ceres2.yml b/.github/workflows/docker_ceres2.yml deleted file mode 100644 index 54908c9df..000000000 --- a/.github/workflows/docker_ceres2.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Docker CI - -on: [push, pull_request] - -jobs: - - build-test: - name: Build docker installing CeresSolver 2 and run tests - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - - name: Build the Docker image - run: docker build . --file Dockerfile.ceres2 --tag mapillary/opensfm.ceres2:$GITHUB_SHA - - - name: Run C++ tests - run: docker run mapillary/opensfm.ceres2:$GITHUB_SHA /bin/sh -c "cd cmake_build && ctest" - - - name: Run Python tests - run: docker run mapillary/opensfm.ceres2:$GITHUB_SHA python3 -m pytest diff --git a/.github/workflows/docker_ubuntu20.yml b/.github/workflows/docker_ubuntu20.yml new file mode 100644 index 000000000..d89e3adf8 --- /dev/null +++ b/.github/workflows/docker_ubuntu20.yml @@ -0,0 +1,23 @@ +name: Docker Ubuntu 20.04 + +on: [push, pull_request] + +jobs: + + build-test: + name: Build docker and run tests on Ubuntu 20.04 + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + + - name: Build the Docker image + run: docker build . --file Dockerfile.ubuntu20 --tag mapillary/opensfm.ubuntu20:$GITHUB_SHA + + - name: Run C++ tests + run: docker run mapillary/opensfm.ubuntu20:$GITHUB_SHA /bin/bash -c 'source ~/.bashrc && conda activate opensfm && cd cmake_build && ctest' + + - name: Run Python tests + run: docker run mapillary/opensfm.ubuntu20:$GITHUB_SHA /bin/bash -c 'source ~/.bashrc && conda activate opensfm && export LD_PRELOAD=$CONDA_PREFIX/lib/libtcmalloc.so && python3 -m pytest' \ No newline at end of file diff --git a/.github/workflows/docker_ubuntu24.yml b/.github/workflows/docker_ubuntu24.yml new file mode 100644 index 000000000..8a9f25eed --- /dev/null +++ b/.github/workflows/docker_ubuntu24.yml @@ -0,0 +1,23 @@ +name: Docker Ubuntu 24.04 + +on: [push, pull_request] + +jobs: + + build-test: + name: Build docker and run tests on Ubuntu 24.04 + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + + - name: Build the Docker image + run: docker build . --file Dockerfile.ubuntu24 --tag mapillary/opensfm.ubuntu24:$GITHUB_SHA + + - name: Run C++ tests + run: docker run mapillary/opensfm.ubuntu24:$GITHUB_SHA /bin/bash -c 'source ~/.bashrc && conda activate opensfm && cd cmake_build && ctest --output-on-failure' + + - name: Run Python tests + run: docker run mapillary/opensfm.ubuntu24:$GITHUB_SHA /bin/bash -c 'source ~/.bashrc && conda activate opensfm && export LD_PRELOAD=$CONDA_PREFIX/lib/libtcmalloc.so && python -m pytest -v' \ No newline at end of file diff --git a/.gitignore b/.gitignore index a739a68fd..ea19cfda8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ xcode launch.json .vscode .idea +.claude +uv.lock # Ignore generated files /build @@ -29,3 +31,5 @@ data/berlin/* # Ignore virtualenv files env/* + +PACKAGE diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 75de8b3cf..000000000 --- a/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM ubuntu:20.04 - -ARG DEBIAN_FRONTEND=noninteractive - -# Install apt-getable dependencies -RUN apt-get update \ - && apt-get install -y \ - build-essential \ - cmake \ - git \ - libeigen3-dev \ - libopencv-dev \ - libceres-dev \ - python3-dev \ - python3-numpy \ - python3-opencv \ - python3-pip \ - python3-pyproj \ - python3-scipy \ - python3-yaml \ - curl \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - - -COPY . /source/OpenSfM - -WORKDIR /source/OpenSfM - -RUN pip3 install -r requirements.txt && \ - python3 setup.py build diff --git a/Dockerfile.ceres2 b/Dockerfile.ceres2 deleted file mode 100644 index f839a7cc5..000000000 --- a/Dockerfile.ceres2 +++ /dev/null @@ -1,43 +0,0 @@ -FROM ubuntu:20.04 - -ARG DEBIAN_FRONTEND=noninteractive - -# Install apt-getable dependencies -RUN apt-get update \ - && apt-get install -y \ - build-essential \ - cmake \ - git \ - libeigen3-dev \ - libgoogle-glog-dev \ - libopencv-dev \ - libsuitesparse-dev \ - python3-dev \ - python3-numpy \ - python3-opencv \ - python3-pip \ - python3-pyproj \ - python3-scipy \ - python3-yaml \ - curl \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - - -# Install Ceres 2 -RUN \ - mkdir -p /source && cd /source && \ - curl -L http://ceres-solver.org/ceres-solver-2.0.0.tar.gz | tar xz && \ - cd /source/ceres-solver-2.0.0 && \ - mkdir -p build && cd build && \ - cmake .. -DCMAKE_C_FLAGS=-fPIC -DCMAKE_CXX_FLAGS=-fPIC -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF && \ - make -j4 install && \ - cd / && rm -rf /source/ceres-solver-2.0.0 - - -COPY . /source/OpenSfM - -WORKDIR /source/OpenSfM - -RUN pip3 install -r requirements.txt && \ - python3 setup.py build diff --git a/Dockerfile.ubuntu20 b/Dockerfile.ubuntu20 new file mode 100644 index 000000000..e330eaff2 --- /dev/null +++ b/Dockerfile.ubuntu20 @@ -0,0 +1,36 @@ +FROM ubuntu:20.04 + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y \ + build-essential \ + cmake \ + git \ + wget \ + python3-dev \ + python3-pip \ + python3-venv \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +# Install Miniconda +FROM continuumio/miniconda3 + +# Setup source +COPY . /source/OpenSfM +WORKDIR /source/OpenSfM + +# Create conda environment from conda.yml +RUN conda env create --file conda.yml --yes + +# Override default shell and use bash in the conda environment +SHELL ["conda", "run", "-n", "opensfm", "/bin/bash", "-c"] + +# Build and install OpenSfM using pip with pyproject.toml +# C++ tests are built automatically (OPENSFM_BUILD_TESTS=ON in pyproject.toml) +# and will be available in cmake_build/ directory for running with ctest +RUN pip install --no-cache-dir -e .[test] + +# Ensure we always activate the conda environment when starting a bash shell +RUN echo "conda activate opensfm" >> ~/.bashrc \ No newline at end of file diff --git a/Dockerfile.ubuntu24 b/Dockerfile.ubuntu24 new file mode 100644 index 000000000..b59c649cd --- /dev/null +++ b/Dockerfile.ubuntu24 @@ -0,0 +1,36 @@ +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y \ + build-essential \ + cmake \ + git \ + wget \ + python3-dev \ + python3-pip \ + python3-venv \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +# Install Miniconda +FROM continuumio/miniconda3 + +# Setup source +COPY . /source/OpenSfM +WORKDIR /source/OpenSfM + +# Create conda environment from conda.yml +RUN conda env create --file conda.yml --yes + +# Override default shell and use bash in the conda environment +SHELL ["conda", "run", "-n", "opensfm", "/bin/bash", "-c"] + +# Build and install OpenSfM using pip with pyproject.toml +# C++ tests are built automatically (OPENSFM_BUILD_TESTS=ON in pyproject.toml) +# and will be available in cmake_build/ directory for running with ctest +RUN pip install --no-cache-dir -e .[test] + +# Ensure we always activate the conda environment when starting a bash shell +RUN echo "conda activate opensfm" >> ~/.bashrc diff --git a/README.md b/README.md index 5e4e64420..737f04405 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Please note that all such changes are released under the AGPLv3 license, not BSD OpenSfM ![Docker workflow](https://github.com/mapillary/opensfm/workflows/Docker%20CI/badge.svg) ======= +[![Conda](https://github.com/mapillary/OpenSfM/actions/workflows/conda.yml/badge.svg)](https://github.com/mapillary/OpenSfM/actions/workflows/conda.yml) [![Docker Ubuntu 20.04](https://github.com/mapillary/OpenSfM/actions/workflows/docker_ubuntu20.yml/badge.svg)](https://github.com/mapillary/OpenSfM/actions/workflows/docker_ubuntu20.yml) [![Docker Ubuntu 24.04](https://github.com/mapillary/OpenSfM/actions/workflows/docker_ubuntu24.yml/badge.svg)](https://github.com/mapillary/OpenSfM/actions/workflows/docker_ubuntu24.yml) ## Overview OpenSfM is a Structure from Motion library written in Python. The library serves as a processing pipeline for reconstructing camera poses and 3D scenes from multiple images. It consists of basic modules for Structure from Motion (feature detection/matching, minimal solvers) with a focus on building a robust and scalable reconstruction pipeline. It also integrates external sensor (e.g. GPS, accelerometer) measurements for geographical alignment and robustness. A JavaScript viewer is provided to preview the models and debug the pipeline. diff --git a/annotation_gui_gcp/lib/GUI.py b/annotation_gui_gcp/lib/GUI.py index f9953f4d8..1676201a1 100644 --- a/annotation_gui_gcp/lib/GUI.py +++ b/annotation_gui_gcp/lib/GUI.py @@ -1,3 +1,4 @@ +# pyre-unsafe import os import random import subprocess @@ -6,12 +7,13 @@ from collections import defaultdict import flask -from annotation_gui_gcp.lib.views.cad_view import CADView -from annotation_gui_gcp.lib.views.cp_finder_view import ControlPointFinderView -from annotation_gui_gcp.lib.views.image_view import ImageView -from annotation_gui_gcp.lib.views.tools_view import ToolsView from opensfm import dataset +from .views.cad_view import CADView +from .views.cp_finder_view import ControlPointFinderView +from .views.image_view import ImageView +from .views.tools_view import ToolsView + class Gui: def __init__( diff --git a/annotation_gui_gcp/lib/gcp_manager.py b/annotation_gui_gcp/lib/gcp_manager.py index 1a22e1ae7..3472bf21c 100644 --- a/annotation_gui_gcp/lib/gcp_manager.py +++ b/annotation_gui_gcp/lib/gcp_manager.py @@ -1,3 +1,4 @@ +# pyre-unsafe import json import os import typing as t @@ -70,7 +71,7 @@ def __repr__(self): def observation_to_json( - obs: t.Union[PointMeasurement3D, PointMeasurement] + obs: t.Union[PointMeasurement3D, PointMeasurement], ) -> t.Dict[str, t.Any]: if isinstance(obs, PointMeasurement): return { @@ -89,7 +90,7 @@ def observation_to_json( def observation_from_json( - obs: t.Dict[str, t.Any] + obs: t.Dict[str, t.Any], ) -> t.Union[PointMeasurement3D, PointMeasurement]: if "projection" in obs: return PointMeasurement( diff --git a/annotation_gui_gcp/lib/geometry.py b/annotation_gui_gcp/lib/geometry.py index c10b2daa5..edb9b2ddf 100644 --- a/annotation_gui_gcp/lib/geometry.py +++ b/annotation_gui_gcp/lib/geometry.py @@ -1,7 +1,9 @@ -from opensfm import dataset -from numpy import ndarray +# pyre-unsafe from typing import Dict, Tuple +from numpy import ndarray +from opensfm import dataset + def get_all_track_observations(gcp_database, track_id: str) -> Dict[str, ndarray]: print(f"Getting all observations of track {track_id}") @@ -11,7 +13,9 @@ def get_all_track_observations(gcp_database, track_id: str) -> Dict[str, ndarray return {shot_id: obs.point for shot_id, obs in track_obs.items()} -def get_tracks_visible_in_image(gcp_database, image_key, min_len: int=5) -> Dict[str, Tuple[ndarray, int]]: +def get_tracks_visible_in_image( + gcp_database, image_key, min_len: int = 5 +) -> Dict[str, Tuple[ndarray, int]]: print(f"Getting track observations visible in {image_key}") data = dataset.DataSet(gcp_database.path) tracks_manager = data.load_tracks_manager() diff --git a/annotation_gui_gcp/lib/image_manager.py b/annotation_gui_gcp/lib/image_manager.py index bd4d63922..494eeda7a 100644 --- a/annotation_gui_gcp/lib/image_manager.py +++ b/annotation_gui_gcp/lib/image_manager.py @@ -1,3 +1,4 @@ +# pyre-unsafe import typing as t from io import BytesIO diff --git a/annotation_gui_gcp/lib/views/cad_view.py b/annotation_gui_gcp/lib/views/cad_view.py index fb3e46c79..23a6c118c 100644 --- a/annotation_gui_gcp/lib/views/cad_view.py +++ b/annotation_gui_gcp/lib/views/cad_view.py @@ -1,13 +1,15 @@ +# pyre-unsafe import json import logging from pathlib import Path from typing import Any, Dict, Tuple import rasterio -from annotation_gui_gcp.lib.views.web_view import WebView, distinct_colors from flask import send_file from PIL import ImageColor +from ..views.web_view import distinct_colors, WebView + logger: logging.Logger = logging.getLogger(__name__) @@ -30,7 +32,7 @@ def __init__( route_prefix, path_cad_file, is_geo_reference=False, - )-> None: + ) -> None: super().__init__(main_ui, web_app, route_prefix) self.main_ui = main_ui @@ -59,7 +61,7 @@ def process_client_message(self, data: Dict[str, Any]) -> None: else: raise ValueError(f"Unknown event {event}") - def add_remove_update_point_observation(self, point_coordinates=None)->None: + def add_remove_update_point_observation(self, point_coordinates=None) -> None: gcp_manager = self.main_ui.gcp_manager active_gcp = self.main_ui.curr_point if active_gcp is None: @@ -97,17 +99,17 @@ def add_remove_update_point_observation(self, point_coordinates=None)->None: def display_points(self) -> None: pass - def refocus(self, lat, lon)->None: + def refocus(self, lat, lon) -> None: x, y, z = self.latlon_to_xyz(lat, lon) self.send_sse_message( {"x": x, "y": y, "z": z}, event_type="move_camera", ) - def highlight_gcp_reprojection(self, *args, **kwargs)->None: + def highlight_gcp_reprojection(self, *args, **kwargs) -> None: pass - def populate_image_list(self, *args, **kwargs)->None: + def populate_image_list(self, *args, **kwargs) -> None: pass def latlon_to_xyz(self, lat, lon) -> Tuple[float, float, float]: @@ -128,13 +130,13 @@ def xyz_to_latlon(self, x, y, z) -> Tuple[float, float, float]: lons, lats, alts = rasterio.warp.transform(self.crs, "EPSG:4326", [x], [y], [z]) return lats[0], lons[0], alts[0] - def load_georeference_metadata(self, path_cad_model)->None: + def load_georeference_metadata(self, path_cad_model) -> None: metadata = _load_georeference_metadata(path_cad_model) self.scale = metadata["scale"] self.crs = metadata["crs"] self.offset = metadata["offset"] - def sync_to_client(self)->None: + def sync_to_client(self) -> None: """ Sends all the data required to initialize or sync the CAD view """ @@ -151,7 +153,11 @@ def sync_to_client(self)->None: for point_id, coords in visible_points_coords.items(): hex_color = distinct_colors[divmod(hash(point_id), 19)[1]] color = ImageColor.getrgb(hex_color) - data["annotations"][point_id] = {"coordinates": coords[:-1], "precision": coords[-1], "color": color} + data["annotations"][point_id] = { + "coordinates": coords[:-1], + "precision": coords[-1], + "color": color, + } # Add the 3D reprojections of the points fn_reprojections = Path( diff --git a/annotation_gui_gcp/lib/views/cp_finder_view.py b/annotation_gui_gcp/lib/views/cp_finder_view.py index af6f3b0a6..42705e7e6 100644 --- a/annotation_gui_gcp/lib/views/cp_finder_view.py +++ b/annotation_gui_gcp/lib/views/cp_finder_view.py @@ -1,6 +1,7 @@ +# pyre-unsafe import typing as t -from annotation_gui_gcp.lib.views.image_view import ImageView +from .image_view import ImageView class ControlPointFinderView(ImageView): diff --git a/annotation_gui_gcp/lib/views/image_view.py b/annotation_gui_gcp/lib/views/image_view.py index 16dddbed8..0465eaaea 100644 --- a/annotation_gui_gcp/lib/views/image_view.py +++ b/annotation_gui_gcp/lib/views/image_view.py @@ -1,6 +1,7 @@ -from typing import Dict, Any +# pyre-unsafe +from typing import Any, Dict -from annotation_gui_gcp.lib.views.web_view import WebView, distinct_colors +from .web_view import distinct_colors, WebView def point_color(point_id: str) -> str: diff --git a/annotation_gui_gcp/lib/views/tools_view.py b/annotation_gui_gcp/lib/views/tools_view.py index 9a70b0482..263a2fff1 100644 --- a/annotation_gui_gcp/lib/views/tools_view.py +++ b/annotation_gui_gcp/lib/views/tools_view.py @@ -1,6 +1,7 @@ -from typing import Dict, Any +# pyre-unsafe +from typing import Any, Dict -from annotation_gui_gcp.lib.views.web_view import WebView +from .web_view import WebView class ToolsView(WebView): diff --git a/annotation_gui_gcp/lib/views/web_view.py b/annotation_gui_gcp/lib/views/web_view.py index a2f93107c..0505ff62f 100644 --- a/annotation_gui_gcp/lib/views/web_view.py +++ b/annotation_gui_gcp/lib/views/web_view.py @@ -1,9 +1,10 @@ +# pyre-unsafe import abc import json import time from queue import Queue -from flask import Response, jsonify, render_template, request +from flask import jsonify, render_template, request, Response distinct_colors = [ "#46f0f0", diff --git a/annotation_gui_gcp/main.py b/annotation_gui_gcp/main.py index aad365118..949a99e74 100644 --- a/annotation_gui_gcp/main.py +++ b/annotation_gui_gcp/main.py @@ -1,16 +1,19 @@ +# pyre-unsafe import argparse import json import typing as t -from collections import OrderedDict, defaultdict +from collections import defaultdict, OrderedDict from os import PathLike from pathlib import Path from typing import Union import numpy as np -from annotation_gui_gcp.lib import GUI -from annotation_gui_gcp.lib.gcp_manager import GroundControlPointManager -from annotation_gui_gcp.lib.image_manager import ImageManager from flask import Flask +from mapillary.opensfm.annotation_gui_gcp.lib import GUI +from mapillary.opensfm.annotation_gui_gcp.lib.gcp_manager import ( + GroundControlPointManager, +) +from mapillary.opensfm.annotation_gui_gcp.lib.image_manager import ImageManager from opensfm import dataset, io diff --git a/annotation_gui_gcp/run_ba.py b/annotation_gui_gcp/run_ba.py index 5e4ad9f09..2e5db05ee 100644 --- a/annotation_gui_gcp/run_ba.py +++ b/annotation_gui_gcp/run_ba.py @@ -1,3 +1,4 @@ +# pyre-unsafe import argparse import json import logging @@ -8,10 +9,16 @@ import numpy as np import opensfm.reconstruction as orec -from opensfm import dataset, log, multiview, pygeometry, pymap -from opensfm import reconstruction_helpers as helpers -from opensfm import transformations as tf -from opensfm import types +from opensfm import ( + dataset, + log, + multiview, + pygeometry, + pymap, + reconstruction_helpers as helpers, + transformations as tf, + types, +) from opensfm.align import apply_similarity logger = logging.getLogger(__name__) @@ -36,7 +43,8 @@ def merge_reconstructions(reconstructions, tracks_manager): merged.add_camera(camera) for point in reconstruction.points.values(): - new_point = merged.create_point(f"R{ix_r}_{point.id}", point.coordinates) + new_point = merged.create_point( + f"R{ix_r}_{point.id}", point.coordinates) new_point.color = point.color for shot in reconstruction.shots.values(): @@ -44,7 +52,8 @@ def merge_reconstructions(reconstructions, tracks_manager): try: obsdict = tracks_manager.get_shot_observations(shot.id) except RuntimeError: - logger.warning(f"Shot id {shot.id} missing from tracks_manager!") + logger.warning( + f"Shot id {shot.id} missing from tracks_manager!") continue for track_id, obs in obsdict.items(): merged_track_id = f"R{ix_r}_{track_id}" @@ -81,11 +90,14 @@ def resplit_reconstruction(merged, original_reconstructions): return split -def gcp_geopositional_error(gcps: List[pymap.GroundControlPoint], reconstruction: types.Reconstruction): +def gcp_geopositional_error( + gcps: List[pymap.GroundControlPoint], reconstruction: types.Reconstruction +): coords_reconstruction = triangulate_gcps(gcps, reconstruction) out = {} for ix, gcp in enumerate(gcps): - expected = reconstruction.reference.to_lla(*gcp.lla_vec) if gcp.lla else None + expected = reconstruction.reference.to_lla( + *gcp.lla_vec) if gcp.lla else None triangulated = ( coords_reconstruction[ix] if coords_reconstruction[ix] is not None else None ) @@ -104,7 +116,8 @@ def gcp_geopositional_error(gcps: List[pymap.GroundControlPoint], reconstruction lat, lon, _alt = out[gcp.id]["expected_lla"] expected_xy = reconstruction.reference.to_topocentric(lat, lon, 0) lat, lon, _alt = out[gcp.id]["triangulated_lla"] - triangulated_xy = reconstruction.reference.to_topocentric(lat, lon, 0) + triangulated_xy = reconstruction.reference.to_topocentric( + lat, lon, 0) out[gcp.id]["error_planar"] = np.linalg.norm( np.array(expected_xy) - np.array(triangulated_xy) ) @@ -114,7 +127,9 @@ def gcp_geopositional_error(gcps: List[pymap.GroundControlPoint], reconstruction return out -def triangulate_gcps(gcps: List[pymap.GroundControlPoint], reconstruction: types.Reconstruction): +def triangulate_gcps( + gcps: List[pymap.GroundControlPoint], reconstruction: types.Reconstruction +): coords = [] for gcp in gcps: res = multiview.triangulate_gcp( @@ -127,7 +142,11 @@ def triangulate_gcps(gcps: List[pymap.GroundControlPoint], reconstruction: types return coords -def reproject_gcps(gcps: List[pymap.GroundControlPoint], reconstruction: types.Reconstruction, reproj_threshold): +def reproject_gcps( + gcps: List[pymap.GroundControlPoint], + reconstruction: types.Reconstruction, + reproj_threshold, +): output = {} for gcp in gcps: point = multiview.triangulate_gcp( @@ -139,11 +158,13 @@ def reproject_gcps(gcps: List[pymap.GroundControlPoint], reconstruction: types.R output[gcp.id] = {} n_obs = len(gcp.observations) if point is None: - logger.info(f"Could not triangulate {gcp.id} with {n_obs} annotations") + logger.info( + f"Could not triangulate {gcp.id} with {n_obs} annotations") continue for observation in gcp.observations: lat, lon, alt = reconstruction.reference.to_lla(*point) - output[gcp.id][observation.shot_id] = {"lla": [lat, lon, alt], "error": 0} + output[gcp.id][observation.shot_id] = { + "lla": [lat, lon, alt], "error": 0} if observation.shot_id not in reconstruction.shots: continue shot = reconstruction.shots[observation.shot_id] @@ -180,7 +201,8 @@ def compute_gcp_std(gcp_errors): all_errors = [] for gcp_id in gcp_errors: errors = [e["error"] for e in gcp_errors[gcp_id].values()] - logger.info(f"gcp {gcp_id} mean reprojection error = {np.mean(errors)}") + logger.info( + f"gcp {gcp_id} mean reprojection error = {np.mean(errors)}") all_errors.extend(errors) all_errors = [e for e in all_errors if not np.isnan(e)] @@ -230,10 +252,11 @@ def add_gcp_to_bundle( f"Could not triangulate GCP '{point.id}'." f"Using {enu} (derived from lat,lon)" ) - coordinates = enu + coordinates = np.array(enu) else: logger.warning( - "Cannot initialize GCP '{}'." " Ignoring it".format(point.id) + "Cannot initialize GCP '{}'." " Ignoring it".format( + point.id) ) continue @@ -269,21 +292,26 @@ def bundle_with_fixed_images( for point in reconstruction.points.values(): ba.add_point(point.id, point.coordinates, False) - ba.add_point_prior(point.id, point.coordinates, np.array([100.0, 100.0, 100.0]), False) + ba.add_point_prior( + point.id, point.coordinates, np.array([100.0, 100.0, 100.0]), False + ) for shot_id in reconstruction.shots: shot = reconstruction.get_shot(shot_id) for point in shot.get_valid_landmarks(): obs = shot.get_landmark_observation(point) - ba.add_point_projection_observation(shot.id, point.id, obs.point, obs.scale) + ba.add_point_projection_observation( + shot.id, point.id, obs.point, obs.scale) - add_gcp_to_bundle(ba, reconstruction.reference, gcp, gcp_std, reconstruction.shots) + add_gcp_to_bundle(ba, reconstruction.reference, gcp, + gcp_std, reconstruction.shots) ba.set_point_projection_loss_function( config["loss_function"], config["loss_function_threshold"] ) ba.set_internal_parameters_prior_sd( config["exif_focal_sd"], + config["aspect_ratio_sd"], config["principal_point_sd"], config["radial_distortion_k1_sd"], config["radial_distortion_k2_sd"], @@ -420,7 +448,8 @@ def resect_image(im, camera, gcps, reconstruction, data, dst_reconstruction=None R = T[:, :3].T t = -R.dot(T[:, 3]) dst_reconstruction.add_camera(camera) - shot = dst_reconstruction.create_shot(im, camera.id, pygeometry.Pose(R, t)) + shot = dst_reconstruction.create_shot( + im, camera.id, pygeometry.Pose(R, t)) shot.metadata = helpers.get_image_metadata(data, im) return shot else: @@ -487,7 +516,8 @@ def align_3d_annotations_to_reconstruction( model_id, reconstruction, ): - coords_triangulated_gcps = triangulate_gcps(gcps_this_model, reconstruction) + coords_triangulated_gcps = triangulate_gcps( + gcps_this_model, reconstruction) n_triangulated = sum(x is not None for x in coords_triangulated_gcps) if n_triangulated < 3: logger.info(f"{model_id} has {n_triangulated} gcps, not aligning") @@ -504,7 +534,8 @@ def align_3d_annotations_to_reconstruction( # Move / "reproject" the triangulated GCPs to the reference frame of the CAD model for display/interaction gcp_reprojections = {} try: - s, A, b = find_alignment(coords_triangulated_gcps, coords_annotated_gcps) + s, A, b = find_alignment( + coords_triangulated_gcps, coords_annotated_gcps) for gcp, coords in zip(gcps_this_model, coords_triangulated_gcps): gcp_reprojections[gcp.id] = ( (s * A.dot(coords) + b).tolist() if coords is not None else None @@ -616,24 +647,29 @@ def align( reconstructions = data.load_reconstruction(fn_resplit) else: reconstructions = data.load_reconstruction() - reconstructions = [reconstructions[rec_a], reconstructions[rec_b]] + reconstructions = [ + reconstructions[rec_a], reconstructions[rec_b]] coords0 = triangulate_gcps(gcps, reconstructions[0]) coords1 = triangulate_gcps(gcps, reconstructions[1]) n_valid_0 = sum(c is not None for c in coords0) - logger.debug(f"Triangulated {n_valid_0}/{len(gcps)} gcps for rec #{rec_a}") + logger.debug( + f"Triangulated {n_valid_0}/{len(gcps)} gcps for rec #{rec_a}") n_valid_1 = sum(c is not None for c in coords1) - logger.debug(f"Triangulated {n_valid_1}/{len(gcps)} gcps for rec #{rec_b}") + logger.debug( + f"Triangulated {n_valid_1}/{len(gcps)} gcps for rec #{rec_b}") try: s, A, b = find_alignment(coords1, coords0) apply_similarity(reconstructions[1], s, A, b) except ValueError: - logger.warning(f"Could not rigidly align rec #{rec_b} to rec #{rec_a}") + logger.warning( + f"Could not rigidly align rec #{rec_b} to rec #{rec_a}") return logger.info(f"Rigidly aligned rec #{rec_b} to rec #{rec_a}") else: # Image - to - reconstruction annotation reconstructions = data.load_reconstruction() base = reconstructions[rec_a] - resected = resect_annotated_single_images(base, gcps, camera_models, data) + resected = resect_annotated_single_images( + base, gcps, camera_models, data) reconstructions = [base, resected] else: logger.debug( @@ -645,7 +681,8 @@ def align( return logger.debug(f"Aligning annotations, if any, to rec #{rec_a}") - align_external_3d_models_to_reconstruction(data, gcps, reconstructions[0], rec_a) + align_external_3d_models_to_reconstruction( + data, gcps, reconstructions[0], rec_a) # Set the GPS constraint of the moved/resected shots to the manually-aligned position for shot in reconstructions[1].shots.values(): @@ -680,7 +717,8 @@ def align( logger.info("Running BA on merged reconstructions") # orec.align_reconstruction(merged, None, data.config) - orec.bundle(merged, camera_models, {}, gcp=gcps, config=data.config) + orec.bundle(merged, camera_models, {}, gcp=gcps, + grid_size=0, config=data.config) data.save_reconstruction( [merged], f"reconstruction_gcp_ba_{rec_a}x{rec_b}.json" ) @@ -709,7 +747,8 @@ def align( with open(f"{data.data_path}/gcp_reprojections_{rec_a}x{rec_b}.json", "w") as f: json.dump(gcp_reprojections, f, indent=4, sort_keys=True) - n_bad_gcp_annotations = int(sum(t[2] > px_threshold for t in reprojection_errors)) + n_bad_gcp_annotations = int( + sum(t[2] > px_threshold for t in reprojection_errors)) if n_bad_gcp_annotations > 0: logger.info(f"{n_bad_gcp_annotations} large reprojection errors:") for t in reprojection_errors: @@ -722,7 +761,6 @@ def align( resplit = resplit_reconstruction(merged, reconstructions) data.save_reconstruction(resplit, fn_resplit) if covariance: - # Re-triangulate to remove badly conditioned points n_points = len(merged.points) @@ -734,7 +772,7 @@ def align( data.config["triangulation_min_ray_angle"] = backup logger.info( f"Re-triangulated. Removed {n_points - len(merged.points)}." - f" Kept {int(100*len(merged.points)/n_points)}%" + f" Kept {int(100 * len(merged.points) / n_points)}%" ) data.save_reconstruction( [merged], @@ -746,7 +784,8 @@ def align( # If we have two reconstructions, we do this twice, fixing each one. _rec_ixs = [(0, 1), (1, 0)] if rec_b is not None else [(0, 1)] for rec_ixs in _rec_ixs: - logger.info(f"Running BA with fixed images. Fixing rec #{rec_ixs[0]}") + logger.info( + f"Running BA with fixed images. Fixing rec #{rec_ixs[0]}") fixed_images = set(reconstructions[rec_ixs[0]].shots.keys()) covariance_estimation_valid = bundle_with_fixed_images( merged, @@ -770,7 +809,8 @@ def align( shots_std_this_pair = [] for shot in merged.shots.values(): if shot.id in reconstructions[rec_ixs[1]].shots: - u, std_v = decompose_covariance(shot.covariance[3:, 3:]) + u, std_v = decompose_covariance( + shot.covariance[3:, 3:]) std = np.linalg.norm(std_v) shots_std_this_pair.append((shot.id, std)) logger.debug(f"{shot.id} std: {std}") @@ -834,7 +874,8 @@ def align( ) if n_bad_std != 0 or n_bad_gcp_annotations != 0: if rigid: - logger.info("Positional uncertainty was not calculated. (--rigid was set).") + logger.info( + "Positional uncertainty was not calculated. (--rigid was set).") elif not covariance: logger.info( "Positional uncertainty was not calculated (--covariance not set)." @@ -856,17 +897,18 @@ def align( gcp_reprojections, px_threshold ) gcps_sorted = sorted( - stats_bad_reprojections, key=lambda k: -stats_bad_reprojections[k] + stats_bad_reprojections, key=lambda k: - + stats_bad_reprojections[k] ) for ix, gcp_id in enumerate(gcps_sorted[:5]): n = stats_bad_reprojections[gcp_id] if n > 0: - logger.info(f"#{ix+1} - {gcp_id}: {n} bad annotations") + logger.info(f"#{ix + 1} - {gcp_id}: {n} bad annotations") else: logger.info("No annotations with large reprojection errors") -if __name__ == "__main__": +def main() -> None: log.setup() args = parse_args() sys.exit( @@ -880,3 +922,7 @@ def align( args.std_threshold, ) ) + + +if __name__ == "__main__": + main() # pragma: no cover diff --git a/bin/import_colmap.py b/bin/import_colmap.py index 9388883f4..209596049 100755 --- a/bin/import_colmap.py +++ b/bin/import_colmap.py @@ -1,5 +1,7 @@ #!/usr/bin/env python3 +# pyre-unsafe + # Snippets to read from the colmap database taken from: # https://github.com/colmap/colmap/blob/ad7bd93f1a27af7533121aa043a167fe1490688c / # scripts/python/export_to_bundler.py @@ -21,7 +23,22 @@ import opensfm.actions.undistort as osfm_u from matplotlib import cm from mpl_toolkits.axes_grid1 import make_axes_locatable -from opensfm import dataset, features, pygeometry, pymap, types + +try: + from opensfm import dataset, features, pygeometry, pymap, types +except ImportError: + # If ImportError, try importing without 'types' + try: + from opensfm import dataset, features, pygeometry, pymap + + types = None + except ImportError as e: + print(f"Fallback import failed: {e}") + dataset = features = pygeometry = pymap = types = None +except Exception as e: + print(f"Unexpected error importing OpenSfM: {e}") + dataset = features = pygeometry = pymap = types = None + EXPORT_DIR_NAME = "opensfm_export" logger = logging.getLogger(__name__) diff --git a/bin/migrate_undistort.sh b/bin/migrate_undistort.sh index 170d602a4..c17b4bf97 100755 --- a/bin/migrate_undistort.sh +++ b/bin/migrate_undistort.sh @@ -2,16 +2,16 @@ # Migrate dataset to the new undistort folder structure. -if [ $# -le 0 ] +if [ $# -le 0 ] then - echo "Migrate dataset to the new undistort folder structure." + echo "Migrate dataset to the new undistort folder structure." echo - echo "Usage:" + echo "Usage:" echo echo " $0 dataset" echo - exit 1 -fi + exit 1 +fi cd $1 diff --git a/bin/opensfm b/bin/opensfm index 8e2aec956..f438811b4 100755 --- a/bin/opensfm +++ b/bin/opensfm @@ -9,4 +9,8 @@ else PYTHON=python fi +if [ "$(uname)" == "Linux" ]; then + export LD_PRELOAD=$CONDA_PREFIX/lib/libtcmalloc.so +fi + "$PYTHON" "$DIR"/opensfm_main.py "$@" diff --git a/bin/opensfm_main.py b/bin/opensfm_main.py index 31249e129..b91abcf9c 100755 --- a/bin/opensfm_main.py +++ b/bin/opensfm_main.py @@ -1,5 +1,6 @@ +# pyre-strict import sys -from os.path import abspath, join, dirname +from os.path import abspath, dirname, join sys.path.insert(0, abspath(join(dirname(__file__), ".."))) @@ -21,7 +22,13 @@ def create_default_dataset_context( dataset.clean_up() -if __name__ == "__main__": +def main() -> None: commands.command_runner( - commands.opensfm_commands, create_default_dataset_context, dataset_choices=["opensfm"] + commands.opensfm_commands, + create_default_dataset_context, + dataset_choices=["opensfm"], ) + + +if __name__ == "__main__": + main() # pragma: no cover diff --git a/bin/plot_gcp.py b/bin/plot_gcp.py index dbb278550..77a7f86ca 100644 --- a/bin/plot_gcp.py +++ b/bin/plot_gcp.py @@ -1,39 +1,37 @@ -"""Plot image crops around GCPs. -""" +"""Plot image crops around GCPs.""" import argparse import logging from typing import List -import numpy as np import matplotlib.pyplot as plt +import numpy as np + import opensfm.reconstruction as orec -from opensfm import features -from opensfm import io -from opensfm import dataset -from opensfm import pymap -from opensfm import types +from opensfm import dataset, features, io, pymap, types logger = logging.getLogger(__name__) def parse_args(): - parser = argparse.ArgumentParser( - description=__doc__) + parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( - 'dataset', - help='dataset', + "dataset", + help="dataset", ) return parser.parse_args() def pix_coords(x, image): return features.denormalized_image_coordinates( - np.array([[x[0], x[1]]]), image.shape[1], image.shape[0])[0] + np.array([[x[0], x[1]]]), image.shape[1], image.shape[0] + )[0] -def gcp_to_ply(gcps: List[pymap.GroundControlPoint], reconstruction: types.Reconstruction): +def gcp_to_ply( + gcps: List[pymap.GroundControlPoint], reconstruction: types.Reconstruction +): """Export GCP position as a PLY string.""" vertices = [] @@ -44,13 +42,15 @@ def gcp_to_ply(gcps: List[pymap.GroundControlPoint], reconstruction: types.Recon p = orec.triangulate_gcp(gcp, reconstruction.shots) if p is None: - logger.warning("Could not compute the 3D position of GCP '{}'" - .format(gcp.id)) + logger.warning( + "Could not compute the 3D position of GCP '{}'".format(gcp.id) + ) continue c = 255, 0, 0 s = "{} {} {} {} {} {}".format( - p.value[0], p.value[1], p.value[2], int(c[0]), int(c[1]), int(c[2])) + p.value[0], p.value[1], p.value[2], int(c[0]), int(c[1]), int(c[2]) + ) vertices.append(s) header = [ @@ -66,20 +66,20 @@ def gcp_to_ply(gcps: List[pymap.GroundControlPoint], reconstruction: types.Recon "end_header", ] - return '\n'.join(header + vertices + ['']) + return "\n".join(header + vertices + [""]) def main(): args = parse_args() logging.basicConfig( - format="%(asctime)s %(levelname)s %(name)s: %(message)s", - level=logging.DEBUG) + format="%(asctime)s %(levelname)s %(name)s: %(message)s", level=logging.DEBUG + ) data = dataset.DataSet(args.dataset) reconstruction = data.load_reconstruction()[0] gcps = data.load_ground_control_points() - with io.open_wt(data.data_path + '/gcp.ply') as fout: + with io.open_wt(data.data_path + "/gcp.ply") as fout: fout.write(gcp_to_ply(gcps, reconstruction)) for gcp in gcps: @@ -91,8 +91,9 @@ def main(): coordinates = orec.triangulate_gcp(gcp, reconstruction.shots) if coordinates is None: - logger.warning("Could not compute the 3D position of GCP '{}'" - .format(gcp.id)) + logger.warning( + "Could not compute the 3D position of GCP '{}'".format(gcp.id) + ) continue for i, observation in enumerate(gcp.observations): @@ -113,5 +114,5 @@ def main(): plt.show() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/bin/plot_inliers b/bin/plot_inliers index b970318c7..19a808c9e 100755 --- a/bin/plot_inliers +++ b/bin/plot_inliers @@ -128,7 +128,7 @@ def create_subplot(figure, rows, columns, index, title, x_lim, y_lim, font_size= subplot.set_aspect(aspect) if grid: - pl.grid(b=True, which='major', color='0.3') + pl.grid(visible=True, which='major', color='0.3') return subplot @@ -231,21 +231,21 @@ def reprojection_errors(reprojections, observations, scale): return errors, mean, std, min_norm, max_norm, corr -def triangulate_tracks(tracks, reconstruction, graph, min_ray_angle): +def triangulate_tracks(tracks, reconstruction, tracks_manager, min_ray_angle, min_depth): """ Triangulates a list of tracks. :param tracks: The array of tracks. :param reconstruction: The reconstruction. - :param: graph, The tracks graph. + :param: tracks_manager, The tracks manager. :param: min_ray_angle: The minimum ray angle difference for a triangulation to be considered valid. :return: An array of booleans determining if each track was successfully triangulated or not. """ succeeded = [] - triangulator = reconstruct.TrackTriangulator(reconstruction, reconstruct.TrackHandlerTrackManager(graph, reconstruction) + triangulator = reconstruct.TrackTriangulator(reconstruction, reconstruct.TrackHandlerTrackManager(tracks_manager, reconstruction)) for track in tracks: # Triangulate with 1 as reprojection threshold to avoid excluding tracks because of error. - triangulator.triangulate(str(track), 1, min_ray_angle) + triangulator.triangulate(str(track), 1, min_ray_angle, min_depth, iterations=10) succeeded.append(True) if str(track) in reconstruction.points else succeeded.append(False) return np.array(succeeded) if len(succeeded) else np.empty((0,), bool) @@ -319,8 +319,8 @@ def load_common_tracks(im1, im2, tracks_manager): :param tracks_manager: The track manager. :return: An array with rows containing a track id and the corresponding feature id for the first and second image. """ - t1 = tracks_manager.get_shot_observations(im1).items() - t2 = tracks_manager.get_shot_observations(im2).items() + t1 = tracks_manager.get_shot_observations(im1) + t2 = tracks_manager.get_shot_observations(im2) tc, p1, p2 = tracking.common_tracks(tracks_manager, im1, im2) common_tracks = [] @@ -417,7 +417,7 @@ def create_matches_figure(im1, im2, data, save_figs=False, single_column=False): p1 = features_data1.points p2 = features_data2.points - symmetric_matches = matching.match_brute_force_symmetric(f1, f2, data.config) + symmetric_matches = matching.match_brute_force_symmetric(p1, p2, data.config) symmetric_matches = np.array(symmetric_matches) if symmetric_matches.shape[0] < 8: @@ -519,7 +519,7 @@ def plot_opencv_find_homography(fw, ip, index, tracks, track_points1, track_poin threshold, pixels1, pixels2 = thresholds(ip.im1_array, ip.im2_array, 'homography_threshold', 0.004, data) H, inliers = cv2.findHomography(track_points1, track_points2, cv2.RANSAC, threshold) - inliers = np.array(np.squeeze(inliers), np.bool) + inliers = np.array(np.squeeze(inliers), bool) inliers1 = track_points1[inliers, :] inliers2 = track_points2[inliers, :] @@ -554,7 +554,7 @@ def plot_two_view_reconstruction(fw, ip, index, track_points1, track_points2, da threshold, pixels1, pixels2 = thresholds(ip.im1_array, ip.im2_array, 'five_point_algo_threshold', 0.006, data) iterations = data.config['five_point_refine_rec_iterations'] - R, t, inliers = reconstruct.two_view_reconstruction(track_points1, track_points2, camera1, camera2, threshold, iterations) + R, t, inliers, _ = reconstruct.two_view_reconstruction_general(track_points1, track_points2, camera1, camera2, threshold, iterations) inliers1 = track_points1[inliers, :] inliers2 = track_points2[inliers, :] @@ -573,7 +573,7 @@ def plot_two_view_reconstruction(fw, ip, index, track_points1, track_points2, da plot_matches(subplot, ip.im1_array, ip.im2_array, outliers1, outliers2, 'r', 'om') -def plot_bootstrapped_reconstruction(fw, ip, index, p1, p2, robust_tracks, linked_tracks, graph, data): +def plot_bootstrapped_reconstruction(fw, ip, index, p1, p2, robust_tracks, linked_tracks, tracks_manager, data): """ Plot successfully reconstructed and failed 3D points by bootstrapping a reconstruction. :param fw: Figure wrapper. @@ -583,13 +583,13 @@ def plot_bootstrapped_reconstruction(fw, ip, index, p1, p2, robust_tracks, linke :param p2: Feature points for the second image. :param robust_tracks: Tracks corresponding to robust matches. :param linked_tracks: Common tracks linked by other image correspondences. - :param graph: Tracks graph. + :param tracks_manager: The tracks manager. :param data: Data set. """ print_reset = redirect_print() - _, pm1, pm2 = tracking.common_tracks(graph, ip.im1, ip.im2) - reconstruction, _, _ = reconstruct.bootstrap_reconstruction(data, graph, ip.im1, ip.im2, pm1, pm2) + _, pm1, pm2 = tracking.common_tracks(tracks_manager, ip.im1, ip.im2) + reconstruction, _ = reconstruct.bootstrap_reconstruction(data, tracks_manager, ip.im1, ip.im2, pm1, pm2) reset_print(print_reset) threshold, pixels1, pixels2 = thresholds(ip.im1_array, ip.im2_array, 'triangulation_threshold', 0.004, data) @@ -667,7 +667,7 @@ def create_tracks_figure(im1, im2, data, save_figs=False, single_column=False): plot_common_tracks(fw, ip, 1, p1, p2, tracks, robust_tracks, linked_tracks, robust_matches) plot_opencv_find_homography(fw, ip, 2, tracks, track_points1, track_points2, data) plot_two_view_reconstruction(fw, ip, 3, track_points1, track_points2, data) - plot_bootstrapped_reconstruction(fw, ip, 4, p1, p2, robust_tracks, linked_tracks, graph, data) + plot_bootstrapped_reconstruction(fw, ip, 4, p1, p2, robust_tracks, linked_tracks, tracks_manager, data) display_figure(fig, save_figs, data, '{0}_{1}_{2}_tracks.jpg'.format(im1, im2, data.feature_type())) @@ -704,7 +704,7 @@ def plot_reconstructed_tracks(fw, ip, index, p1, p2, tracks, rec_tracks, non_rec plot_matches(subplot, ip.im1_array, ip.im2_array, non_rec_track_points1, non_rec_track_points2, 'r', 'om') -def plot_reprojected_tracks(fw, ip, index, p1, p2, tracks, rec_tracks, non_rec_tracks, reconstruction, graph, data): +def plot_reprojected_tracks(fw, ip, index, p1, p2, tracks, rec_tracks, non_rec_tracks, reconstruction, tracks_manager, data): """ Reprojects tracks included in and excluded from reconstruction and plots reprojections on top of observations. :param fw: Figure wrapper. @@ -716,7 +716,7 @@ def plot_reprojected_tracks(fw, ip, index, p1, p2, tracks, rec_tracks, non_rec_t :param rec_tracks: Common tracks included in the reconstruction. :param non_rec_tracks: Common tracks not included in the reconstruction. :param reconstruction: Reconstruction. - :param graph: Tracks graph. + :param tracks_manager: The tracks manager. :param data: Data set """ @@ -727,7 +727,8 @@ def plot_reprojected_tracks(fw, ip, index, p1, p2, tracks, rec_tracks, non_rec_t rp2 = reproject_tracks(ip.im2, rec_tracks[:, 0], reconstruction) min_ray_angle = data.config['triangulation_min_ray_angle'] - succeeded = triangulate_tracks(non_rec_tracks[:, 0], reconstruction, graph, min_ray_angle) + min_depth = data.config['triangulation_min_depth'] + succeeded = triangulate_tracks(non_rec_tracks[:, 0], reconstruction, tracks_manager, min_ray_angle, min_depth) reprojected1 = reproject_tracks(ip.im1, non_rec_tracks[succeeded, 0], reconstruction) reprojected2 = reproject_tracks(ip.im2, non_rec_tracks[succeeded, 0], reconstruction) @@ -786,7 +787,7 @@ def create_reconstruction_matches_figure(im1, im2, data, save_figs=False): rec_tracks, non_rec_tracks = reconstruction_tracks(tracks, reconstruction) plot_reconstructed_tracks(fw, ip, 1, p1, p2, tracks, rec_tracks, non_rec_tracks) - plot_reprojected_tracks(fw, ip, 2, p1, p2, tracks, rec_tracks, non_rec_tracks, reconstruction, graph, data) + plot_reprojected_tracks(fw, ip, 2, p1, p2, tracks, rec_tracks, non_rec_tracks, reconstruction, tracks_manager, data) display_figure(fig, save_figs, data, '{0}_{1}_{2}_reconstruction.jpg'.format(im1, im2, data.feature_type())) @@ -833,7 +834,7 @@ def create_complete_reconstruction_figure(im, data, save_figs=False, single_colu plot_points(rec_plot, im_array, rp, '+w') min_ray_angle = data.config['triangulation_min_ray_angle'] - succeeded = triangulate_tracks(non_rec_tracks[:, 0], reconstruction, graph, min_ray_angle) + succeeded = triangulate_tracks(non_rec_tracks[:, 0], reconstruction, tracks_manager, min_ray_angle) reprojected = reproject_tracks(im, non_rec_tracks[succeeded, 0], reconstruction) triangulated_points = p[non_rec_tracks[succeeded, 1]] @@ -887,7 +888,7 @@ def create_reprojection_error_figure(im, data, save_figs=False, single_column=Fa reproj_rec = reproject_tracks(im, rec_tracks[:, 0], reconstruction) min_ray_angle = data.config['triangulation_min_ray_angle'] - succeeded = triangulate_tracks(non_rec_tracks[:, 0], reconstruction, graph, min_ray_angle) + succeeded = triangulate_tracks(non_rec_tracks[:, 0], reconstruction, tracks_manager, min_ray_angle) reproj_non = reproject_tracks(im, non_rec_tracks[succeeded, 0], reconstruction) triang_non = p[non_rec_tracks[succeeded, 1]] @@ -897,7 +898,7 @@ def create_reprojection_error_figure(im, data, save_figs=False, single_column=Fa non_errors, non_mean, non_std, non_min, non_max, non_corr = reprojection_errors(reproj_non, triang_non, scale) triang_thld = data.config['triangulation_threshold'] - bundle_thld = data.config['bundle_outlier_threshold'] + bundle_thld = data.config['bundle_outlier_fixed_threshold'] axis_max = np.max(np.abs(np.vstack((rec_errors, non_errors)))) axis_max = 1.05 * np.max([axis_max, scale * triang_thld, scale * bundle_thld]) diff --git a/bin/plot_matches.py b/bin/plot_matches.py index 94f9d7fbf..ab94b2e94 100755 --- a/bin/plot_matches.py +++ b/bin/plot_matches.py @@ -1,17 +1,17 @@ #!/usr/bin/env python3 +# pyre-unsafe + import argparse import os.path from itertools import combinations +from typing import List import matplotlib.cm as cm import matplotlib.pyplot as pl import numpy as np -from opensfm import dataset -from opensfm import features -from opensfm import io from numpy import ndarray -from typing import List +from opensfm import dataset, features, io def plot_matches(im1, im2, p1: ndarray, p2: ndarray) -> None: @@ -113,7 +113,8 @@ def plot_matches_for_images(data, image, images) -> None: pl.show() -if __name__ == "__main__": +def main() -> None: + global args, images parser = argparse.ArgumentParser(description="Plot matches between images") parser.add_argument("dataset", help="path to the dataset to be processed") parser.add_argument("--image", help="show tracks for a specific") @@ -124,12 +125,20 @@ def plot_matches_for_images(data, image, images) -> None: parser.add_argument( "--save_figs", help="save figures instead of showing them", action="store_true" ) - args: argparse.Namespace = parser.parse_args() + args = parser.parse_args() data = dataset.DataSet(args.dataset) - images: List[str] = data.images() + images = data.images() if args.graph: plot_graph(data) else: plot_matches_for_images(data, args.image, args.images) + + +args: argparse.Namespace +images: List[str] + + +if __name__ == "__main__": + main() # pragma: no cover diff --git a/conda.yml b/conda.yml new file mode 100644 index 000000000..72f9a22ea --- /dev/null +++ b/conda.yml @@ -0,0 +1,15 @@ +name: opensfm +dependencies: + - python=3.10 + - cmake=3.31 + - make + - libxcrypt + - libopencv *=headless* + - py-opencv + - anaconda::metis + - conda-forge::suitesparse=7.10.* + - conda-forge::llvm-openmp + - conda-forge::cxx-compiler + - conda-forge::openblas + - conda-forge::gperftools + - conda-forge::ceres-solver=2.2 \ No newline at end of file diff --git a/data/berlin/config.yaml b/data/berlin/config.yaml index d4689cc5e..c9249183b 100644 --- a/data/berlin/config.yaml +++ b/data/berlin/config.yaml @@ -1,4 +1,3 @@ - # OpenSfM will use the default parameters from opensfm/config.py # Set here any parameter that you want to override for this dataset # For example: diff --git a/data/lund/config.yaml b/data/lund/config.yaml index 5d2e0359f..6cfded297 100644 --- a/data/lund/config.yaml +++ b/data/lund/config.yaml @@ -1,4 +1,3 @@ - # OpenSfM will use the default parameters from opensfm/config.py # Set here any parameter that you want to override for this dataset # For example: diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 000000000..69ba4bb7d --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,33 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = ../build/doc +PORT ?= 8000 + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile serve clean + +# Serve the documentation locally +serve: + @echo "Serving documentation at http://localhost:$(PORT)/" + @echo "Press Ctrl+C to stop the server" + @python -m http.server --directory $(BUILDDIR)/html $(PORT) + +# Clean build artifacts +clean: + @echo "Cleaning build directory: $(BUILDDIR)" + @rm -rf $(BUILDDIR) + @echo "Done" + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/source/annotation_tool.rst b/doc/source/annotation_tool.rst index 1813f6186..b86d9471c 100644 --- a/doc/source/annotation_tool.rst +++ b/doc/source/annotation_tool.rst @@ -49,9 +49,9 @@ Main toolbox ~~~~~~~~~~~~ The main toolbox contains the list of existing control points as well as several controls. -The basic controls are explained here. Scroll to :ref:`additional-controls` for information on the rest. +The basic controls are explained here. Scroll to :ref:`advanced-features` for information on the rest. -- The 'Load', 'Save' buttons save and load the ground control points into a ``ground_control_points.json`` file with :ref:`json-gcps`. +- The 'Load', 'Save' buttons save and load the ground control points into a ``ground_control_points.json`` file (see the JSON file format section in the Ground Control Points documentation). - If there is a ``ground_control_points.json`` file in the dataset directory, it will be loaded upon launch. - Control points can be added or removed with the 'Add GCP' and 'Remove GCP' buttons. The active point can be selected from the dropdown. - By selecting a point in the list it becomes active and can be annotated on all images. @@ -84,6 +84,7 @@ Assuming that you have a set of ground control points whose geodetic coordinates You can use ``data/berlin`` for this example. 2. Generate a ``ground_control_points.json`` file with all your measured ground control points and place it in the root of the dataset See the example below. Note how the 'observations' is empty as we will generate those using the annotation tool. + :: "points": [ @@ -93,6 +94,7 @@ Assuming that you have a set of ground control points whose geodetic coordinates "observations": [] } ] + 3. Launch the annotation tool, note how the control points dropdown contains your ground control points. 4. Scroll through all the images, annotating each GCP on all the locations where it is visible. 5. Click on 'save' to overwrite the ``ground_control_points.json`` file with your annotations. @@ -122,6 +124,8 @@ The 'Flex' and 'Full' buttons produce additional analysis results and are explained in :ref:`two-reconstruction-annotation` +.. _advanced-features: + Advanced features ----------------- diff --git a/doc/source/building.rst b/doc/source/building.rst index 591444af4..061625a8b 100644 --- a/doc/source/building.rst +++ b/doc/source/building.rst @@ -4,6 +4,25 @@ Building ======== +Quick start +----------- + +To build OpenSfM, follow these steps: + +1. Download the OpenSfM code from Github:: + + git clone --recursive https://github.com/mapillary/OpenSfM + +2. Install the dependencies (we recommend using conda):: + + conda env create --file conda.yml --yes + conda activate opensfm + +3. Build OpenSfM:: + + pip install -e . + + Download -------- @@ -20,62 +39,39 @@ If you already have the code or you downloaded a release_, make sure to update t Install dependencies -------------------- -OpenSfM depends on the following libraries that need to be installed before building it. +OpenSfM depends on multiple libraries (OpenCV_, `Ceres Solver`_, ...) that need to be installed before building it. -* OpenCV_ -* `Ceres Solver`_ +The way to install these dependencies depends on your system. We recommend using a virtual environment manager such as anaconda or miniconda, not to mess up with your current setup. Anaconda will take care of installing both systems and python dependencies. -Python dependencies can be installed with:: +Installing dependencies using Conda (recommended) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - pip install -r requirements.txt +Creating a conda environment will take care of installing all dependencies. Make sure you have conda or miniconda installed. From the project root directory, run:: + conda env create --file conda.yml --yes -Installing dependencies on Ubuntu -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -See this `Dockerfile `_ for the commands to install all dependencies on Ubuntu 20.04. - -Installing dependencies on Fedora -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +You can then activate the anaconda environment with:: -Tested on Fedora 33 & 34 + conda activate opensfm - sudo dnf install zlib-devel libjpeg-devel python3-devel g++ ceres-solver-devel opencv-devel python3-opencv eigen3-devel libomp cmake glog-devel +and you are ready to build OpenSfM. -There's an `issue `_ with the gflags-config.cmake distributed with Fedora. This quick workaround works:: +(Anaconda dependencies installation has been tested under MacOS (Sequoia), Ubuntu 24.04 and Fedora 42.) - sudo sed -i "s^set (GFLAGS_INCLUDE_DIR.*^set (GFLAGS_INCLUDE_DIR "/usr/include")^" /usr/lib64/cmake/gflags/gflags-config.cmake +Installing dependencies on Ubuntu +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Install python dependencies before building:: +If you are not using conda, see this `Dockerfile.ubuntu24 `_ for the commands to install all dependencies on Ubuntu 24.04. - cd ~/src/OpenSfM && pip install -r requirements.txt Installing dependencies on MacOSX -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Install OpenCV and the Ceres solver using:: - - brew install opencv - brew install ceres-solver - brew install libomp - sudo pip install -r requirements.txt - -Make sure you update your ``PYTHONPATH`` to include ``/usr/local/lib/python3.7/site-packages`` where OpenCV have been installed. For example with:: - - export PYTHONPATH=/usr/local/lib/python3.7/site-packages:$PYTHONPATH - -Also, in order for Cmake to recognize the libraries installed by Brew, make sure that C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, DYLD_LIBRARY_PATH environment variables are set correctly. For example, you can run:: - - export C_INCLUDE_PATH=/usr/local/include - export CPLUS_INCLUDE_PATH=/usr/local/include - export DYLD_LIBRARY_PATH=$HOME/local/lib64 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. note:: Note on OpenCV 3 - When running OpenSfM on top of OpenCV version 3.0 the `OpenCV Contrib`_ modules are required for extracting SIFT or SURF features. +While it is possible to install all dependencies using brew, we recommend using the conda instructions above instead. Installing dependencies on Windows -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Install git_. @@ -96,29 +92,47 @@ Then install OpenCV, Ceres, SuiteSparse and LAPACK (this will take a while):: vcpkg install opencv4 ceres ceres[suitesparse] lapack suitesparse --triplet x64-windows -Finally install the PIP requirements:: - - pip install -r requirements.txt - Building the library -------------------- Once the dependencies have been installed, you can build OpenSfM by running the following command from the main folder:: - python3 setup.py build + pip install -e . + +This will first install python dependencies on your current python environment, and then build OpenSfM and install it in editable mode. + + +Building Docker images +---------------------- + +You can also use OpenSfM inside docker. We provide example Dockerfiles for Ubuntu 20.04 and 24.04. Build it by running the following command from the main folder:: + + docker build -t opensfm.ubuntu24 -f Dockerfile.ubuntu24 . Building the documentation -------------------------- -To build the documentation and browse it locally use:: - pip install sphinx_rtd_theme - python3 setup.py build_doc - python3 -m http.server --directory build/doc/html/ +To build the documentation and browse it locally, first install Sphinx:: + + pip install -e .[docs] + +Then build the documentation using make:: + + cd doc + make html + +To browse the documentation locally:: + + make serve and browse `http://localhost:8000/ `_ +To clean the build artifacts:: + + make clean + .. _Github: https://github.com/mapillary/OpenSfM .. _release: https://github.com/mapillary/OpenSfM/releases @@ -131,4 +145,4 @@ and browse `http://localhost:8000/ `_ .. _git: https://git-scm.com/ .. _cmake: https://cmake.org/ .. _Visual Studio 2019: https://visualstudio.microsoft.com/downloads/ -.. _Python 3.8: https://www.microsoft.com/en-us/p/python-38/9mssztt1n39l \ No newline at end of file +.. _Python 3.8: https://www.microsoft.com/en-us/p/python-38/9mssztt1n39l diff --git a/doc/source/conf.py b/doc/source/conf.py index ce672b700..7b716341b 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -50,7 +50,7 @@ # General information about the project. project = "OpenSfM" -copyright = "2021, Mapillary" +copyright = "2025, Mapillary" author = "Mapillary" # The version info for the project you're documenting, acts as replacement for @@ -67,7 +67,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: diff --git a/doc/source/gcp.rst b/doc/source/gcp.rst index ad92fb99f..96d5dabf4 100644 --- a/doc/source/gcp.rst +++ b/doc/source/gcp.rst @@ -12,8 +12,6 @@ In the bundle adjustment step, GCP observations are used as a constraint to refi GPSs can be specified in two file formats. If existing, both are loaded. -.. _json-gcps: - JSON file format ~~~~~~~~~~~~~~~~ GCPs can be specified by adding a text file named ``ground_control_points.json`` at the root folder of the dataset. The format of the file should be as follows:: diff --git a/doc/source/geometry.rst b/doc/source/geometry.rst index b209bef01..199672d2e 100644 --- a/doc/source/geometry.rst +++ b/doc/source/geometry.rst @@ -163,34 +163,50 @@ Identifier `fisheye` v = f\ d\ \theta\ \frac{y}{r} \end{array} +Fisheye OpenCV +~~~~~~~~~~~~~~ +Identifier `fisheye_opencv` + +.. math:: + \begin{array}{l} + r = \sqrt(x^2 + y^2) \\ + \theta = \arctan(r / z) \\ + d_r = 1 + k_1\theta^2 + k_2\theta^4 + k_3\theta^6 + k_4\theta^8\\ + u = f\ (d\ \theta\ \frac{x}{r}) + c_x \\ + v = f\ (d\ \theta\ \frac{y}{r}) + c_y + \end{array} + + Fisheye 62 ~~~~~~~~~~~ Identifier `fisheye62` .. math:: \begin{array}{l} - r^2 = x^2 + y^2 \\ + r = \sqrt(x^2 + y^2) \\ \theta = \arctan(r / z) \\ - d_r = 1 + k_1\theta + k_2\theta^2 + k_3\theta^3 + k_4\theta^4 + k_5\theta^5 + k_6\theta^6\\ - d^t_x = 2p_1\ x_n\ y_n + p_2\ (r^2 + 2x)\\ - d^t_y = 2p_2\ x_n\ y_n + p_1\ (r^2 + 2y)\\ + d_r = 1 + k_1\theta^2 + k_2\theta^4 + k_3\theta^6 + k_4\theta^8 + k_5\theta^10 + k_6\theta^12\\ + d^t_x = 2p_1\ x_n\ y_n + p_2\ (\theta^2 + 2\ x_n^2)\\ + d^t_y = 2p_2\ x_n\ y_n + p_1\ (\theta^2 + 2\ y_n^2)\\ u = f\ (d_r\ \theta\ \frac{x}{r} + d^t_x) + c_x \\ v = f\ (d_r\ \theta\ \frac{y}{r} + d^t_y) + c_y \end{array} -Fisheye OpenCV -~~~~~~~~~~~~~~ -Identifier `fisheye_opencv` +Fisheye 624 +~~~~~~~~~~~ +Identifier `fisheye624` .. math:: \begin{array}{l} - r^2 = x^2 + y^2 \\ + r = \sqrt(x^2 + y^2) \\ \theta = \arctan(r / z) \\ - d_r = 1 + k_1\theta^2 + k_2\theta^4 + k_3\theta^6\\ - d^t_x = 2p_1\ x_n\ y_n + p_2\ (r^2 + 2x)\\ - d^t_y = 2p_2\ x_n\ y_n + p_1\ (r^2 + 2y)\\ - u = f\ (d\ \theta\ \frac{x}{r} + d^t_x) + c_x \\ - v = f\ (d\ \theta\ \frac{y}{r} + d^t_y) + c_y + d_r = 1 + k_1\theta^2 + k_2\theta^4 + k_3\theta^6 + k_4\theta^8 + k_5\theta^10 + k_6\theta^12\\ + d^t_x = 2p_1\ x_n\ y_n + p_2\ (\theta^2 + 2\ x_n^2)\\ + d^t_y = 2p_2\ x_n\ y_n + p_1\ (\theta^2 + 2\ y_n^2)\\ + d^s_x = s_0 * theta^2 + s_1 * theta^4\\ + d^s_y = s_2 * theta^2 + s_3 * theta^4\\ + u = f\ (d_r\ \theta\ \frac{x}{r} + d^t_x + d^p_x) + c_x \\ + v = f\ (d_r\ \theta\ \frac{y}{r} + d^t_y + d^p_y) + c_y \end{array} Spherical Camera diff --git a/doc/source/quality_report.rst b/doc/source/quality_report.rst index 5faebacc0..e910ef3cb 100644 --- a/doc/source/quality_report.rst +++ b/doc/source/quality_report.rst @@ -52,12 +52,12 @@ Reconstruction Details |rec| - - Average reprojection error (normalized/pixels): normalized (by features uncertainty) average norm of reprojection errors and same, but pixel-wise, - un-normalized, error. Errors bigger than 4 pixels are pruned out. - - Average Track Length : average number of images in which a reconstructed points has been detected. - - Average Track Length (> 2) : same as above but ignoring 2-images points. +- Average reprojection error (normalized/pixels): normalized (by features uncertainty) average norm of reprojection errors and same, but pixel-wise, + un-normalized, error. Errors bigger than 4 pixels are pruned out. +- Average Track Length : average number of images in which a reconstructed points has been detected. +- Average Track Length (> 2) : same as above but ignoring 2-images points. - |residual_histogram| +|residual_histogram| The tables are the histogram of the certainty-normalized and un-normalized reprojection errors norm. Errors bigger than 4 pixels are pruned out. diff --git a/doc/source/rig.rst b/doc/source/rig.rst index c261ee906..11029d0d4 100644 --- a/doc/source/rig.rst +++ b/doc/source/rig.rst @@ -12,18 +12,19 @@ Coordinate Systems Rig are defined by a fixed assembly of cameras that are triggered at the same instant. The following terms define such assembly and capture in OpenSfM terminology : - - A `RigCamera` is a camera of the rig assembly defined as a combination of an existing camera model (it refers only to its ID) and its pose wrt. the rig assembly coordinate frame. `RigCamera` are defined in the `rig_cameras.json` as the following:: +- A `RigCamera` is a camera of the rig assembly defined as a combination of an existing camera model (it refers only to its ID) and its pose wrt. the rig assembly coordinate frame. `RigCamera` are defined in the `rig_cameras.json` as the following:: - { - "RIG_CAMERA_ID": { - "translation": translation of the rig frame wrt. the RigCamera frame - "rotation": rotation bringing a point from rig frame to the RigCamera frame - "camera": camera model ID of this RigCamera - }, - ... + "RIG_CAMERA_ID": + { + "translation": translation of the rig frame wrt. the RigCamera frame + "rotation": rotation bringing a point from rig frame to the RigCamera frame + "camera": camera model ID of this RigCamera + }, + ... + } - - A `RigInstance` is a list of `Shots`, each of which correspond to a `RigCamera` of the `RigModel` and the actual pose of the `RigModel` in the world : it's indeed an instantiation of the `RigModel` by combining `Shots`. These instances are defined in the `rig_assignments.json` file as follows:: +- A `RigInstance` is a list of `Shots`, each of which correspond to a `RigCamera` of the `RigModel` and the actual pose of the `RigModel` in the world : it's indeed an instantiation of the `RigModel` by combining `Shots`. These instances are defined in the `rig_assignments.json` file as follows:: { "RIG_INSTANCE_ID1": { @@ -57,8 +58,7 @@ The following terms define such assembly and capture in OpenSfM terminology : ] }, ... - - + } A picture is often worth many words : |rig_frame| diff --git a/doc/source/sensor_database.rst b/doc/source/sensor_database.rst index 63fdc1eb4..400649a16 100644 --- a/doc/source/sensor_database.rst +++ b/doc/source/sensor_database.rst @@ -9,7 +9,7 @@ Calibration Database Overview -------- -In order to produce accurate geometry, structure-from-motion (SfM) needs to have correct estimates of the imaging sensor geometry, such as : lens type (fisheye, perspective, spherical), focal, distorsion, principal point. Please refer to the `Geometric Models`_ section for a comprehensive list of camera internal parameters (calibration). +In order to produce accurate geometry, structure-from-motion (SfM) needs to have correct estimates of the imaging sensor geometry, such as : lens type (fisheye, perspective, spherical), focal, distorsion, principal point. Please refer to the :doc:`geometry` section for a comprehensive list of camera internal parameters (calibration). While reconstructing the scene (using incremental SfM), OpenSfM will adjust for the camera calibration values that best explain the seen geometry. However, in order to get optimal and failsafe results, it is recommended to have a first good guess of the calibration values. By default, OpenSfM will try to get these values by reading the image EXIFs, where the focal length can be red, and is one of the most important of the calibration values. However, sometimes, EXIFs does not contain such value, or it is erroneous, and/or it is better to have other values than just the focal length. @@ -21,18 +21,18 @@ Here comes sensors databases to the rescue. These are files stored under ``opens sensor_data_detailed.json ------------------------- -This file contains physical sensor's width and height, in millimeters, for a given ``model make`` sensor (see `extract_metadata`_). It means that if only the focal length is available in the EXIFs, since we also have the sensor physical size, we know the full sensor geometry. +This file contains physical sensor's width and height, in millimeters, for a given ``model make`` sensor (see ``extract_metadata`` command). It means that if only the focal length is available in the EXIFs, since we also have the sensor physical size, we know the full sensor geometry. sensor_data.json ---------------- -This file contains a multiplicative factor for a given ``model make`` sensor (see `extract_metadata`_). When applied to the EXIFs focal length, this factor gives the focal 35mm equivalent. Since we know the dimensions of 35mm equivalent (24x32 mm), we again know the full sensor geometry. +This file contains a multiplicative factor for a given ``model make`` sensor (see ``extract_metadata`` command). When applied to the EXIFs focal length, this factor gives the focal 35mm equivalent. Since we know the dimensions of 35mm equivalent (24x32 mm), we again know the full sensor geometry. camera_calibration.json ------------------------ -This file contains the full definition (in OpenSfM format) of camera calibrations. Calibration are for a given ``make`` (see `extract_metadata`_), and then, they're further refined : +This file contains the full definition (in OpenSfM format) of camera calibrations. Calibration are for a given ``make`` (see ``extract_metadata`` command), and then, they're further refined : - If ``ALL`` is specified, then the calibration is valid for all ``make model`` camera independant of their ``model`` value - If ``MODEL`` is specified, then calibrations are per actual ``model`` - If ``FOCAL`` is specified, then calibrations are per focal length red from the EXIFs diff --git a/doc/source/using.rst b/doc/source/using.rst index 2dfb8e80d..3f83aa758 100644 --- a/doc/source/using.rst +++ b/doc/source/using.rst @@ -8,12 +8,27 @@ Using Quickstart ---------- -An example dataset is available at ``data/berlin``. You can reconstruct it using by running:: +An example dataset is available at ``data/berlin``. You can reconstruct it by running:: bin/opensfm_run_all data/berlin This will run the entire SfM pipeline and produce the file ``data/berlin/reconstruction.meshed.json`` as output. +Running in Docker +''''''''''''''''' + +First, build the OpenSfM Docker image, as described under "building". + + +Then, start a Docker container. The following command mounts the `data/` folder to `/data/` inside the Docker container:: + + docker run -it -p 8080:8080 -v ${PWD}/data/:/data/ opensfm.ubuntu24 /bin/bash + +Once inside the running Docker container, start the reconstruction process with the usual command:: + + bin/opensfm_run_all /data/berlin/ + +When done, exit the container by pressing Ctrl+d. The model generated will be available in the `data/` catalogue. Viewer setup '''''''''''' @@ -248,6 +263,20 @@ create_tracks ~~~~~~~~~~~~~ This command links the matches between pairs of images to build feature point tracks. The tracks are stored in the `tracks.csv` file. A track is a set of feature points from different images that have been recognized to correspond to the same pysical point. +Format of the data in the genrated tracks.csv file is as follows: + +image_name track_id feature_index normalized_x normalized_y size R G B +02.jpg 1479 2594 0.0379803 -0.0481853 0.00155505 155 145 143 + +image_name -- name of the image file from which the data was extracted +track_id -- id to keep 'track' of the tracks +feature_index -- index of the keypoint from which this track is associated +normalized_x -- x coordinate of the normalized images coordinate system +normalized_y -- y coordinate of the normalized images coordinate system +size -- size value according to the normalized images coordinate system +R -- pixel value for Red channel +G -- pixel value for Green channel +B -- pixel value for Blue channel reconstruct ~~~~~~~~~~~ @@ -287,4 +316,4 @@ Checkout `the default configuration <_modules/opensfm/config.html>`_ to see the .. include:: gcp.rst -.. _fisheye https://docs.opencv.org/master/db/d58/group__calib3d__fisheye.html +.. _fisheye: https://docs.opencv.org/master/db/d58/group__calib3d__fisheye.html diff --git a/export_pmvs.md b/export_pmvs.md deleted file mode 100644 index 8298df3d2..000000000 --- a/export_pmvs.md +++ /dev/null @@ -1,106 +0,0 @@ -# OpenSfM to PMVS dense point cloud reconstruction - -Download CMVS: http://www.di.ens.fr/cmvs/ - -- [Install PMVS/CMVS](#pmvs-installation-hints-ubuntu) -- [PMVS Inputs](#pmvs-inputs) -- [Usage](#usage) - -### PMVS Installation Hints (Linux/Ubuntu) -- Installation pointers here: http://www.di.ens.fr/cmvs/documentation.html - -- Type `make` in `cmvs/program/main`. Should make three binaries: - + `pmvs2` - + `cmvs` - + `genOptions` - -- Most dependencies installed with apt-get: - - `sudo apt-get install libgsl0-dev libblas-dev libatlas-dev liblapack-dev liblapacke-dev` - -- Updated Graclus link: http://www.cs.utexas.edu/users/dml/Software/graclus.html - -#### Lapack Errors: -http://mdda.net/oss-blog/2014-06/building-VisualSFM-on-FC20/ - - ERROR : ../base/numeric/mylapack.cc:6:25: fatal error: clapack/f2c.h: No such file or directory - -Update `../base/numeric/mylapack.cc` -From: - - extern "C" { - #include - #include - }; -To: - - extern "C" { - //#include - //#include - #include - }; - #define integer int - -Update `../base/numeric/mylapack.h` -From: - - static void lls(std::vector& A, - std::vector& b, - long int width, long int height); - - static void lls(std::vector& A, - std::vector& b, - long int width, long int height); -To: - - static void lls(std::vector& A, - std::vector& b, - int width, int height); - - static void lls(std::vector& A, - std::vector& b, - int width, int height); - -#### Accumulate Error: - - ../base/cmvs/bundle.cc: In member function ‘int CMVS::Cbundle::addImagesSub(const std::vector >&)’: - ../base/cmvs/bundle.cc:1134:52: error: ‘accumulate’ was not declared in this scope - return accumulate(addnum.begin(), addnum.end(), 0); - -Add this to `../base/cmvs/bundle.cc` - - #include - -#### Stdlib Error: - genOption.cc: In function ‘int main(int, char**)’: - genOption.cc:17:12: error: ‘exit’ was not declared in this scope - -Add this to `genOption.cc` - - #include - -### PMVS Inputs - -These are the files that `export_pmvs` generates for PMVS from OpenSfM output. More info: http://www.di.ens.fr/pmvs/documentation.html - -- Images: `visualize/%08d.jpg` (radially undistorted) -- Camera Parameters: `txt/%08d.txt` -- Image Co-Visibility file: `vis.dat` -- Options file: `options.txt` (includes mention of `vis.dat`) -- Output directory: `models/` - -### Usage - -From the root OpenSfM directory, run: - - bin/export_pmvs - -There will be an individual pmvs directory for each separate reconstruction. - -To perform the PMVS point cloud reconstruction, run: - - ./pmvs2 /pmvs/recon0/ pmvs_options.txt - -This will generate files in `/pmvs/recon0/models/` including a `pmvs_options.txt.ply` - -**Important:** note that the trailing `/` in `recon0/` is needed. Otherwise PMVS will fail to find the options file and will give an `Unrecognizable option` warning. diff --git a/licenses.csv b/licenses.csv index 7bba7efa6..4754aa52f 100644 --- a/licenses.csv +++ b/licenses.csv @@ -11,5 +11,4 @@ pyproj, 2.5.0, "OSI Approved" pytest, 3.0.7, MIT python-dateutil, 2.6.0, "Simplified BSD" scipy, 1.4.1, BSD -six, 1.14.0, MIT xmltodict, 0.10.2, MIT diff --git a/opensfm/__init__.py b/opensfm/__init__.py index b7a0406f6..49d800696 100644 --- a/opensfm/__init__.py +++ b/opensfm/__init__.py @@ -3,11 +3,25 @@ if sys.platform == 'win32': os.add_dll_directory(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) -from opensfm import pybundle -from opensfm import pydense -from opensfm import pyfeatures -from opensfm import pygeo -from opensfm import pygeometry -from opensfm import pymap -from opensfm import pyrobust -from opensfm import pysfm +# pyre-strict +from opensfm import ( + pybundle, + pydense, + pyfeatures, + pygeo, + pygeometry, + pymap, + pyrobust, + pysfm, +) + +__all__ = [ + "pybundle", + "pydense", + "pyfeatures", + "pygeo", + "pygeometry", + "pymap", + "pyrobust", + "pysfm", +] diff --git a/opensfm/actions/align_submodels.py b/opensfm/actions/align_submodels.py index 3e70501f4..91cf74a2f 100644 --- a/opensfm/actions/align_submodels.py +++ b/opensfm/actions/align_submodels.py @@ -1,10 +1,10 @@ -from opensfm.large import metadataset -from opensfm.large import tools +# pyre-strict from opensfm.dataset import DataSet +from opensfm.large import metadataset, tools def run_dataset(data: DataSet) -> None: - """ Align submodel reconstructions for of MetaDataSet. """ + """Align submodel reconstructions for of MetaDataSet.""" meta_data = metadataset.MetaDataSet(data.data_path) reconstruction_shots = tools.load_reconstruction_shots(meta_data) diff --git a/opensfm/actions/bundle.py b/opensfm/actions/bundle.py index fa4afdc48..daa775f46 100644 --- a/opensfm/actions/bundle.py +++ b/opensfm/actions/bundle.py @@ -1,9 +1,13 @@ +# pyre-strict +from typing import Optional + import opensfm.reconstruction as orec from opensfm.dataset_base import DataSetBase -from typing import Optional -def run_dataset(dataset: DataSetBase, input: Optional[str], output: Optional[str]) -> None: +def run_dataset( + dataset: DataSetBase, input: Optional[str], output: Optional[str] +) -> None: """Bundle a reconstructions. Args: @@ -23,6 +27,6 @@ def run_dataset(dataset: DataSetBase, input: Optional[str], output: Optional[str reconstruction.add_correspondences_from_tracks_manager(tracks_manager) gcp = dataset.load_ground_control_points() orec.bundle( - reconstruction, camera_priors, rig_cameras_priors, gcp, dataset.config + reconstruction, camera_priors, rig_cameras_priors, gcp, 0, dataset.config ) dataset.save_reconstruction(reconstructions, output) diff --git a/opensfm/actions/compute_depthmaps.py b/opensfm/actions/compute_depthmaps.py index 3d1a7e7a1..0a6801491 100644 --- a/opensfm/actions/compute_depthmaps.py +++ b/opensfm/actions/compute_depthmaps.py @@ -1,11 +1,11 @@ +# pyre-strict import os -from opensfm import dataset -from opensfm import dense +from opensfm import dataset, dense from opensfm.dataset import DataSet -def run_dataset(data: DataSet, subfolder, interactive) -> None: +def run_dataset(data: DataSet, subfolder: str, interactive: bool) -> None: """Compute depthmap on a dataset with has SfM ran already. Args: @@ -16,7 +16,7 @@ def run_dataset(data: DataSet, subfolder, interactive) -> None: udata_path = os.path.join(data.data_path, subfolder) udataset = dataset.UndistortedDataSet(data, udata_path, io_handler=data.io_handler) - data.config["interactive"] = interactive + udataset.config["interactive"] = interactive reconstructions = udataset.load_undistorted_reconstruction() tracks_manager = udataset.load_undistorted_tracks_manager() dense.compute_depthmaps(udataset, tracks_manager, reconstructions[0]) diff --git a/opensfm/actions/compute_statistics.py b/opensfm/actions/compute_statistics.py index bb9fd4ed3..6b4eee726 100644 --- a/opensfm/actions/compute_statistics.py +++ b/opensfm/actions/compute_statistics.py @@ -1,15 +1,15 @@ +# pyre-strict import logging import os -from opensfm import io -from opensfm import stats +from opensfm import io, stats from opensfm.dataset import DataSet logger: logging.Logger = logging.getLogger(__name__) logging.getLogger('matplotlib.font_manager').disabled = True -def run_dataset(data: DataSet, diagram_max_points: int=-1) -> None: +def run_dataset(data: DataSet, diagram_max_points: int = -1) -> None: """Compute various staistics of a datasets and write them to 'stats' folder Args: diff --git a/opensfm/actions/create_rig.py b/opensfm/actions/create_rig.py index 823f9f019..9747bdfde 100644 --- a/opensfm/actions/create_rig.py +++ b/opensfm/actions/create_rig.py @@ -1,15 +1,18 @@ +# pyre-strict import logging +from typing import Dict, List -from opensfm import pymap, rig, reconstruction_helpers as helpers, types +from opensfm import pymap, reconstruction_helpers as helpers, rig, types from opensfm.dataset import DataSet, DataSetBase from opensfm.types import Reconstruction -from typing import Dict, List logger: logging.Logger = logging.getLogger(__name__) -def run_dataset(data: DataSet, method, definition: Dict[str, str], output_debug) -> None: +def run_dataset( + data: DataSet, method: str, definition: Dict[str, str], output_debug: bool +) -> None: """Given a dataset that contains rigs, construct rig data files. Args: @@ -29,7 +32,9 @@ def run_dataset(data: DataSet, method, definition: Dict[str, str], output_debug) data.save_reconstruction(reconstructions, "rig_instances.json") -def _reconstruction_from_rigs_and_assignments(data: DataSetBase) -> List[Reconstruction]: +def _reconstruction_from_rigs_and_assignments( + data: DataSetBase, +) -> List[Reconstruction]: assignments = data.load_rig_assignments() rig_cameras = data.load_rig_cameras() diff --git a/opensfm/actions/create_submodels.py b/opensfm/actions/create_submodels.py index 08ef8e205..e151c84d5 100644 --- a/opensfm/actions/create_submodels.py +++ b/opensfm/actions/create_submodels.py @@ -1,17 +1,18 @@ +# pyre-strict import logging from collections import defaultdict import numpy as np from opensfm.dataset import DataSet -from opensfm.large.metadataset import MetaDataSet from opensfm.large import tools +from opensfm.large.metadataset import MetaDataSet logger: logging.Logger = logging.getLogger(__name__) def run_dataset(data: DataSet) -> None: - """ Split the dataset into smaller submodels. """ + """Split the dataset into smaller submodels.""" meta_data = MetaDataSet(data.data_path) @@ -31,7 +32,7 @@ def run_dataset(data: DataSet) -> None: meta_data.create_submodels(meta_data.load_clusters_with_neighbors()) -def _create_image_list(data: DataSet, meta_data) -> None: +def _create_image_list(data: DataSet, meta_data: MetaDataSet) -> None: ills = [] for image in data.images(): exif = data.load_exif(image) @@ -95,6 +96,7 @@ def _cluster_images(meta_data: MetaDataSet, cluster_size: float) -> None: K = float(images.shape[0]) / cluster_size K = int(np.ceil(K)) + # pyre-fixme[23]: Unable to unpack single value, 2 were expected. labels, centers = tools.kmeans(positions, K)[1:] images = images.ravel() @@ -103,7 +105,7 @@ def _cluster_images(meta_data: MetaDataSet, cluster_size: float) -> None: meta_data.save_clusters(images, positions, labels, centers) -def _add_cluster_neighbors(meta_data: MetaDataSet, max_distance) -> None: +def _add_cluster_neighbors(meta_data: MetaDataSet, max_distance: float) -> None: images, positions, labels, centers = meta_data.load_clusters() clusters = tools.add_cluster_neighbors(positions, labels, centers, max_distance) diff --git a/opensfm/actions/create_tracks.py b/opensfm/actions/create_tracks.py index e119eb07f..ae7b456f8 100644 --- a/opensfm/actions/create_tracks.py +++ b/opensfm/actions/create_tracks.py @@ -1,7 +1,7 @@ +# pyre-strict from timeit import default_timer as timer -from opensfm import io -from opensfm import tracking +from opensfm import io, pymap, tracking from opensfm.dataset_base import DataSetBase @@ -9,19 +9,23 @@ def run_dataset(data: DataSetBase) -> None: """Link matches pair-wise matches into tracks.""" start = timer() - features, colors, segmentations, instances = tracking.load_features( + features, colors, segmentations, instances, depths = tracking.load_features( data, data.images() ) features_end = timer() - matches = tracking.load_matches(data, data.images()) + matches = lambda: tracking.load_matches(data, data.images()) matches_end = timer() - tracks_manager = tracking.create_tracks_manager( + + tracks_manager = tracking.create_tracks_manager_from_matches_iter( features, colors, segmentations, instances, matches, data.config["min_track_length"], + depths, + data.config["depth_is_radial"], + data.config["depth_std_deviation_m_default"], ) tracks_end = timer() data.save_tracks_manager(tracks_manager) @@ -35,7 +39,11 @@ def run_dataset(data: DataSetBase) -> None: def write_report( - data: DataSetBase, tracks_manager, features_time, matches_time, tracks_time + data: DataSetBase, + tracks_manager: pymap.TracksManager, + features_time: float, + matches_time: float, + tracks_time: float, ) -> None: view_graph = [ (k[0], k[1], v) for k, v in tracks_manager.get_all_pairs_connectivity().items() diff --git a/opensfm/actions/detect_features.py b/opensfm/actions/detect_features.py index a84f6791b..0aa6ac6ad 100644 --- a/opensfm/actions/detect_features.py +++ b/opensfm/actions/detect_features.py @@ -1,3 +1,4 @@ +# pyre-strict import logging from timeit import default_timer as timer diff --git a/opensfm/actions/export_bundler.py b/opensfm/actions/export_bundler.py index 9a6002aa3..8c9ca5c93 100644 --- a/opensfm/actions/export_bundler.py +++ b/opensfm/actions/export_bundler.py @@ -1,11 +1,15 @@ +# pyre-strict import os +from typing import List import numpy as np -from opensfm import io +from opensfm import io, pymap, types from opensfm.dataset import DataSet -def run_dataset(data: DataSet, list_path, bundle_path, undistorted) -> None: +def run_dataset( + data: DataSet, list_path: str, bundle_path: str, undistorted: bool +) -> None: """Export reconstruction to bundler format. Args: @@ -36,7 +40,11 @@ def run_dataset(data: DataSet, list_path, bundle_path, undistorted) -> None: def export_bundler( - image_list, reconstructions, track_manager, bundle_file_path: str, list_file_path: str + image_list: List[str], + reconstructions: List[types.Reconstruction], + track_manager: pymap.TracksManager, + bundle_file_path: str, + list_file_path: str, ) -> None: """ Generate a reconstruction file that is consistent with Bundler's format diff --git a/opensfm/actions/export_colmap.py b/opensfm/actions/export_colmap.py index ab275e074..42e1b326b 100644 --- a/opensfm/actions/export_colmap.py +++ b/opensfm/actions/export_colmap.py @@ -29,6 +29,8 @@ # # Author: Johannes L. Schoenberger (jsch at inf.ethz.ch) +# pyre-strict + # This script is based on an original implementation by True Price. import math @@ -38,14 +40,31 @@ import tempfile import typing as t from struct import pack -from typing import Tuple +from typing import Any, Callable, Dict, List, Optional, Protocol, Tuple, TypeVar import numpy as np -from opensfm import features -from opensfm import matching +from numpy.typing import DTypeLike, NDArray +from opensfm import features, io, matching, pygeometry from opensfm.dataset import DataSet -I_3 = np.eye(3) +T = TypeVar("T") + +I_3: NDArray = np.eye(3) + + +class Camera(Protocol): + width: int + height: int + projection_type: str + focal: float + k1: float + k2: float + k3: float = 0.0 + k4: float = 0.0 + p1: float = 0.0 + p2: float = 0.0 + aspect_ratio: float = 1.0 + principal_point: Tuple[float, float] = (0.0, 0.0) def run_dataset(data: DataSet, binary: bool) -> None: @@ -85,14 +104,14 @@ def run_dataset(data: DataSet, binary: bool) -> None: db.close() data.io_handler.rm_if_exist(database_path) - with data.io_handler.open(tmp_database_path, "rb") as f: - with data.io_handler.open(database_path, "wb") as fwb: + with data.io_handler.open_rb(tmp_database_path) as f: + with data.io_handler.open_wb(database_path) as fwb: fwb.write(f.read()) IS_PYTHON3: bool = int(sys.version_info[0]) >= 3 -MAX_IMAGE_ID = 2 ** 31 - 1 +MAX_IMAGE_ID: int = 2**31 - 1 CREATE_CAMERAS_TABLE = """CREATE TABLE IF NOT EXISTS cameras ( camera_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, @@ -122,9 +141,7 @@ def run_dataset(data: DataSet, binary: bool) -> None: prior_tz REAL, CONSTRAINT image_id_check CHECK(image_id >= 0 and image_id < {}), FOREIGN KEY(camera_id) REFERENCES cameras(camera_id)) -""".format( - MAX_IMAGE_ID -) +""".format(MAX_IMAGE_ID) CREATE_TWO_VIEW_GEOMETRIES_TABLE = """ CREATE TABLE IF NOT EXISTS two_view_geometries ( @@ -167,27 +184,31 @@ def run_dataset(data: DataSet, binary: bool) -> None: ) -def image_ids_to_pair_id(image_id1, image_id2) -> int: +def image_ids_to_pair_id(image_id1: int, image_id2: int) -> int: if image_id1 > image_id2: image_id1, image_id2 = image_id2, image_id1 return image_id1 * MAX_IMAGE_ID + image_id2 -def pair_id_to_image_ids(pair_id) -> Tuple[int, int]: +def pair_id_to_image_ids(pair_id: int) -> Tuple[int, int]: image_id2 = pair_id % MAX_IMAGE_ID image_id1 = (pair_id - image_id2) // MAX_IMAGE_ID return image_id1, image_id2 -def array_to_blob(array) -> bytes: +def array_to_blob(array: NDArray) -> bytes: if IS_PYTHON3: return array.tobytes() else: + # pyre-fixme[16]: Module `numpy` has no attribute `getbuffer`. return np.getbuffer(array) -def blob_to_array(blob, dtype, shape: Tuple[int] = (-1,)): +def blob_to_array( + blob: bytes, dtype: DTypeLike, shape: Tuple[int, ...] = (-1,) +) -> NDArray: if IS_PYTHON3: + # pyre-fixme[20]: Argument `sep` expected. return np.fromstring(blob, dtype=dtype).reshape(*shape) else: return np.frombuffer(blob, dtype=dtype).reshape(*shape) @@ -195,28 +216,46 @@ def blob_to_array(blob, dtype, shape: Tuple[int] = (-1,)): class COLMAPDatabase(sqlite3.Connection): @staticmethod - def connect(database_path) -> t.Any: + def connect(database_path: str) -> "COLMAPDatabase": return sqlite3.connect(database_path, factory=COLMAPDatabase) - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args: Any, **kwargs: Any) -> None: super(COLMAPDatabase, self).__init__(*args, **kwargs) - self.create_tables = lambda: self.executescript(CREATE_ALL) - self.create_cameras_table = lambda: self.executescript(CREATE_CAMERAS_TABLE) - self.create_descriptors_table = lambda: self.executescript( - CREATE_DESCRIPTORS_TABLE + self.create_tables: Callable[[], sqlite3.Cursor] = lambda: self.executescript( + CREATE_ALL + ) + self.create_cameras_table: Callable[[], sqlite3.Cursor] = ( + lambda: self.executescript(CREATE_CAMERAS_TABLE) + ) + self.create_descriptors_table: Callable[[], sqlite3.Cursor] = ( + lambda: self.executescript(CREATE_DESCRIPTORS_TABLE) + ) + self.create_images_table: Callable[[], sqlite3.Cursor] = ( + lambda: self.executescript(CREATE_IMAGES_TABLE) ) - self.create_images_table = lambda: self.executescript(CREATE_IMAGES_TABLE) - self.create_two_view_geometries_table = lambda: self.executescript( - CREATE_TWO_VIEW_GEOMETRIES_TABLE + self.create_two_view_geometries_table: Callable[[], sqlite3.Cursor] = ( + lambda: self.executescript(CREATE_TWO_VIEW_GEOMETRIES_TABLE) + ) + self.create_keypoints_table: Callable[[], sqlite3.Cursor] = ( + lambda: self.executescript(CREATE_KEYPOINTS_TABLE) + ) + self.create_matches_table: Callable[[], sqlite3.Cursor] = ( + lambda: self.executescript(CREATE_MATCHES_TABLE) + ) + self.create_name_index: Callable[[], sqlite3.Cursor] = ( + lambda: self.executescript(CREATE_NAME_INDEX) ) - self.create_keypoints_table = lambda: self.executescript(CREATE_KEYPOINTS_TABLE) - self.create_matches_table = lambda: self.executescript(CREATE_MATCHES_TABLE) - self.create_name_index = lambda: self.executescript(CREATE_NAME_INDEX) def add_camera( - self, model, width, height, params, prior_focal_length=False, camera_id=None - ) -> t.Any: + self, + model: int, + width: int, + height: int, + params: NDArray, + prior_focal_length: bool = False, + camera_id: Optional[int] = None, + ) -> Optional[int]: params = np.asarray(params, np.float64) cursor = self.execute( "INSERT INTO cameras VALUES (?, ?, ?, ?, ?, ?)", @@ -232,8 +271,13 @@ def add_camera( return cursor.lastrowid def add_image( - self, name, camera_id, prior_q=(0, 0, 0, 0), prior_t=(0, 0, 0), image_id=None - ) -> t.Any: + self, + name: str, + camera_id: int, + prior_q: Tuple[float, float, float, float] = (0, 0, 0, 0), + prior_t: Tuple[float, float, float] = (0, 0, 0), + image_id: Optional[int] = None, + ) -> Optional[int]: cursor = self.execute( "INSERT INTO images VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ( @@ -251,7 +295,7 @@ def add_image( ) return cursor.lastrowid - def add_keypoints(self, image_id, keypoints) -> None: + def add_keypoints(self, image_id: int, keypoints: NDArray) -> None: assert len(keypoints.shape) == 2 assert keypoints.shape[1] in [2, 4, 6] @@ -261,14 +305,14 @@ def add_keypoints(self, image_id, keypoints) -> None: (image_id,) + keypoints.shape + (array_to_blob(keypoints),), ) - def add_descriptors(self, image_id, descriptors) -> None: + def add_descriptors(self, image_id: int, descriptors: NDArray) -> None: descriptors = np.ascontiguousarray(descriptors, np.uint8) self.execute( "INSERT INTO descriptors VALUES (?, ?, ?, ?)", (image_id,) + descriptors.shape + (array_to_blob(descriptors),), ) - def add_matches(self, image_id1, image_id2, matches) -> None: + def add_matches(self, image_id1: int, image_id2: int, matches: NDArray) -> None: assert len(matches.shape) == 2 assert matches.shape[1] == 2 @@ -283,7 +327,14 @@ def add_matches(self, image_id1, image_id2, matches) -> None: ) def add_two_view_geometry( - self, image_id1, image_id2, matches, F=I_3, E=I_3, H=I_3, config=2 + self, + image_id1: int, + image_id2: int, + matches: NDArray, + F: NDArray = I_3, + E: NDArray = I_3, + H: NDArray = I_3, + config: int = 2, ) -> None: assert len(matches.shape) == 2 assert matches.shape[1] == 2 @@ -319,7 +370,7 @@ def add_two_view_geometry( COLMAP_ID_MAP = {"brown": 6, "perspective": 3, "fisheye": 9, "fisheye_opencv": 5} -def camera_to_colmap_params(camera) -> t.Tuple[float, ...]: +def camera_to_colmap_params(camera: pygeometry.Camera) -> Tuple[float, ...]: w = camera.width h = camera.height normalizer = max(w, h) @@ -353,7 +404,9 @@ def camera_to_colmap_params(camera) -> t.Tuple[float, ...]: raise ValueError("Can't convert {camera.projection_type} to COLMAP") -def export_cameras(data, db) -> t.Tuple[t.Dict[str, int], t.Dict[str, int]]: +def export_cameras( + data: DataSet, db: COLMAPDatabase +) -> t.Tuple[t.Dict[str, int], t.Dict[str, int]]: camera_map = {} for camera_model, camera in data.load_camera_models().items(): if data.camera_models_overrides_exists(): @@ -379,7 +432,9 @@ def export_cameras(data, db) -> t.Tuple[t.Dict[str, int], t.Dict[str, int]]: return images_map, camera_map -def export_features(data, db, images_map) -> t.Dict[str, np.ndarray]: +def export_features( + data: DataSet, db: COLMAPDatabase, images_map: t.Dict[str, int] +) -> t.Dict[str, NDArray]: features_map = {} for image in data.images(): width = data.load_exif(image)["width"] @@ -395,7 +450,13 @@ def export_features(data, db, images_map) -> t.Dict[str, np.ndarray]: return features_map -def export_matches(data, db, features_map, images_map) -> None: +def export_matches( + data: DataSet, + db: COLMAPDatabase, + features_map: Dict[str, NDArray], + images_map: Dict[str, int], +) -> None: + """Export matches between images to the COLMAP database.""" matches_per_pair = {} for image1 in data.images(): matches = data.load_matches(image1) @@ -423,7 +484,9 @@ def export_matches(data, db, features_map, images_map) -> None: db.add_matches(images_map[pair[0]], images_map[pair[1]], inliers) -def export_cameras_reconstruction(data, path, camera_map, binary: bool = False) -> None: +def export_cameras_reconstruction( + data: DataSet, path: str, camera_map: Dict[str, int], binary: bool = False +) -> None: reconstructions = data.load_reconstruction() cameras = {} for reconstruction in reconstructions: @@ -431,10 +494,10 @@ def export_cameras_reconstruction(data, path, camera_map, binary: bool = False) cameras[camera_id] = camera if binary: - fout = data.io_handler.open(os.path.join(path, "cameras.bin"), "wb") - fout.write(pack(" None: reconstructions = data.load_reconstruction() tracks_manager = data.load_tracks_manager() if binary: - fout = data.io_handler.open(os.path.join(path, "images.bin"), "wb") + fout_bin = data.io_handler.open_wb(os.path.join(path, "images.bin")) n_ims = 0 for reconstruction in reconstructions: n_ims += len(reconstruction.shots) - fout.write(pack(" Dict[str, int]: reconstructions = data.load_reconstruction() tracks_manager = data.load_tracks_manager() points_map = {} if binary: - fout = data.io_handler.open(os.path.join(path, "points3D.bin"), "wb") + fout_bin = data.io_handler.open_wb(os.path.join(path, "points3D.bin")) n_points = 0 for reconstruction in reconstructions: n_points += len(reconstruction.points) - fout.write(pack(" t.List[float]: +def angle_axis_to_quaternion(angle_axis: NDArray) -> List[float]: angle = np.linalg.norm(angle_axis) x = angle_axis[0] / angle @@ -603,7 +672,9 @@ def angle_axis_to_quaternion(angle_axis: np.ndarray) -> t.List[float]: return [qw, qx, qy, qz] -def export_ini_file(path, db_path, images_path, io_handler) -> None: +def export_ini_file( + path: str, db_path: str, images_path: str, io_handler: io.IoFilesystemBase +) -> None: with io_handler.open_wt(os.path.join(path, "project.ini")) as fout: fout.write("log_to_stderr=false\nlog_level=2\n") fout.write("database_path=%s\n" % db_path) diff --git a/opensfm/actions/export_geocoords.py b/opensfm/actions/export_geocoords.py index 98294d4f3..2b27fb12c 100644 --- a/opensfm/actions/export_geocoords.py +++ b/opensfm/actions/export_geocoords.py @@ -1,14 +1,14 @@ +# pyre-strict import logging import os -from opensfm import types +from typing import List, Sequence import numpy as np import pyproj -from opensfm import io +from numpy.typing import NDArray +from opensfm import io, types +from opensfm import geo from opensfm.dataset import DataSet, UndistortedDataSet -from opensfm.geo import TopocentricConverter -from opensfm.reconstruction import bundle_shot_poses -from typing import List, Sequence logger: logging.Logger = logging.getLogger(__name__) @@ -19,7 +19,7 @@ def run_dataset( transformation: bool, image_positions: bool, reconstruction: bool, - dense : bool, + dense: bool, output: str, offset = (0, 0), mode = "affine" @@ -43,8 +43,8 @@ def run_dataset( reference = data.load_reference() - projection = pyproj.Proj(proj) - t = _get_transformation(reference, projection, offset) + projection = geo.construct_proj_transformer(proj, inverse=True) + t = geo.get_proj_transform_matrix(reference, projection) if transformation: output = output or "geocoords_transformation.txt" @@ -59,15 +59,8 @@ def run_dataset( if reconstruction: reconstructions = data.load_reconstruction() - if mode == "affine": - for r in reconstructions: - _transform_reconstruction(r, t) - elif mode == "projected": - for r in reconstructions: - _transform_reconstruction_projected(r, t, offset[0], offset[1], reference, projection, data) - else: - raise Exception(f"Invalid mode: {mode}") - + for r in reconstructions: + geo.transform_reconstruction_with_proj(r, projection) output = output or "reconstruction.geocoords.json" data.save_reconstruction(reconstructions, output) @@ -78,39 +71,16 @@ def run_dataset( _transform_dense_point_cloud(udata, t, output_path) -def _get_transformation(reference: TopocentricConverter, projection: pyproj.Proj, offset) -> np.ndarray: - """Get the linear transform from reconstruction coords to geocoords.""" - p = [[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]] - q = [_transform(point, reference, projection) for point in p] - - transformation = np.array( - [ - [q[0][0] - q[3][0], q[1][0] - q[3][0], q[2][0] - q[3][0], q[3][0] - offset[0]], - [q[0][1] - q[3][1], q[1][1] - q[3][1], q[2][1] - q[3][1], q[3][1] - offset[1]], - [q[0][2] - q[3][2], q[1][2] - q[3][2], q[2][2] - q[3][2], q[3][2]], - [0, 0, 0, 1], - ] - ) - return transformation - - -def _write_transformation(transformation: np.ndarray, filename: str) -> None: +def _write_transformation(transformation: NDArray, filename: str) -> None: """Write the 4x4 matrix transformation to a text file.""" with io.open_wt(filename) as fout: for row in transformation: - fout.write(u" ".join(map(str, row))) - fout.write(u"\n") - - -def _transform(point: Sequence, reference: TopocentricConverter, projection: pyproj.Proj) -> List[float]: - """Transform on point from local coords to a proj4 projection.""" - lat, lon, altitude = reference.to_lla(point[0], point[1], point[2]) - easting, northing = projection(lon, lat) - return [easting, northing, altitude] + fout.write(" ".join(map(str, row))) + fout.write("\n") def _transform_image_positions( - reconstructions: List[types.Reconstruction], transformation: np.ndarray, output: str + reconstructions: List[types.Reconstruction], transformation: NDArray, output: str ) -> None: A, b = transformation[:3, :3], transformation[:3, 3] @@ -127,58 +97,8 @@ def _transform_image_positions( fout.write(text) -def _transform_reconstruction( - reconstruction: types.Reconstruction, transformation: np.ndarray -) -> None: - """Apply a transformation to a reconstruction in-place.""" - A, b = transformation[:3, :3], transformation[:3, 3] - A1 = np.linalg.inv(A) - - for shot in reconstruction.shots.values(): - R = shot.pose.get_rotation_matrix() - shot.pose.set_rotation_matrix(np.dot(R, A1)) - shot.pose.set_origin(np.dot(A, shot.pose.get_origin()) + b) - - for point in reconstruction.points.values(): - point.coordinates = list(np.dot(A, point.coordinates) + b) - -def _transform_points_projected(pts, offset_x, offset_y, reference, projection): - lat, lon, alt = reference.to_lla(pts[:,0], pts[:,1], pts[:,2]) - easting, northing = projection(lon, lat) - return easting - offset_x, northing - offset_y, alt - -def _transform_reconstruction_projected( - reconstruction: types.Reconstruction, transformation: np.ndarray, offset_x, offset_y, reference, projection, data -) -> None: - """Apply a transformation to a reconstruction in-place by projection.""" - - # Points - pts = np.array([p.coordinates for p in reconstruction.points.values()]) - easting, northing, alt = _transform_points_projected(pts, offset_x, offset_y, reference, projection) - - for i, point in enumerate(reconstruction.points.values()): - point.coordinates = [easting[i], northing[i], alt[i]] - - # Cameras - A, b = transformation[:3, :3], transformation[:3, 3] - A1 = np.linalg.inv(A) - - pts = np.array([shot.pose.get_origin() for shot in reconstruction.shots.values()]) - easting, northing, alt = _transform_points_projected(pts, offset_x, offset_y, reference, projection) - - for i, shot in enumerate(reconstruction.shots.values()): - R = shot.pose.get_rotation_matrix() - shot.pose.set_rotation_matrix(np.dot(R, A1)) - shot.pose.set_origin([easting[i], northing[i], alt[i]]) - - logger.info("Bundle shot poses") - camera_priors = data.load_camera_models() - rig_camera_priors = data.load_rig_cameras() - bundle_shot_poses(reconstruction, set(reconstruction.shots.keys()), camera_priors, rig_camera_priors, data.config) - - def _transform_dense_point_cloud( - udata: UndistortedDataSet, transformation: np.ndarray, output_path: str + udata: UndistortedDataSet, transformation: NDArray, output_path: str ) -> None: """Apply a transformation to the merged point cloud.""" A, b = transformation[:3, :3], transformation[:3, 3] @@ -190,7 +110,9 @@ def _transform_dense_point_cloud( fout.write(line) else: x, y, z, nx, ny, nz, red, green, blue = line.split() + # pyre-fixme[6]: For 2nd argument expected `Union[Sequence[Sequen... x, y, z = np.dot(A, map(float, [x, y, z])) + b + # pyre-fixme[6]: For 2nd argument expected `Union[Sequence[Sequen... nx, ny, nz = np.dot(A, map(float, [nx, ny, nz])) fout.write( "{} {} {} {} {} {} {} {} {}\n".format( diff --git a/opensfm/actions/export_openmvs.py b/opensfm/actions/export_openmvs.py index 7f0f1259d..d19267c8e 100644 --- a/opensfm/actions/export_openmvs.py +++ b/opensfm/actions/export_openmvs.py @@ -1,13 +1,15 @@ +# pyre-strict import os +from typing import Dict, Optional + import numpy as np -from opensfm import io -from opensfm import pydense +from opensfm import io, pydense, pymap, types from opensfm.dataset import DataSet, UndistortedDataSet -def run_dataset(data: DataSet, image_list) -> None: - """ Export reconstruction to OpenMVS format. """ +def run_dataset(data: DataSet, image_list: str) -> None: + """Export reconstruction to OpenMVS format.""" udata = data.undistorted_dataset() reconstructions = udata.load_undistorted_reconstruction() @@ -24,7 +26,12 @@ def run_dataset(data: DataSet, image_list) -> None: export(reconstructions[0], tracks_manager, udata, export_only) -def export(reconstruction, tracks_manager, udata: UndistortedDataSet, export_only) -> None: +def export( + reconstruction: types.Reconstruction, + tracks_manager: pymap.TracksManager, + udata: UndistortedDataSet, + export_only: Optional[Dict[str, bool]], +) -> None: exporter = pydense.OpenMVSExporter() for camera in reconstruction.cameras.values(): if camera.projection_type == "perspective": diff --git a/opensfm/actions/export_ply.py b/opensfm/actions/export_ply.py index 3e718c01b..be84025de 100644 --- a/opensfm/actions/export_ply.py +++ b/opensfm/actions/export_ply.py @@ -1,12 +1,19 @@ +# pyre-strict import os import numpy as np from opensfm import io -from opensfm.dense import depthmap_to_ply, scale_down_image from opensfm.dataset import DataSet +from opensfm.dense import depthmap_to_ply, scale_down_image -def run_dataset(data: DataSet, no_cameras: bool, no_points: bool, depthmaps, point_num_views: bool) -> None: +def run_dataset( + data: DataSet, + no_cameras: bool, + no_points: bool, + depthmaps: bool, + point_num_views: bool, +) -> None: """Export reconstruction to PLY format Args: @@ -24,7 +31,14 @@ def run_dataset(data: DataSet, no_cameras: bool, no_points: bool, depthmaps, poi point_num_views = point_num_views if reconstructions: - data.save_ply(reconstructions[0], tracks_manager, None, no_cameras, no_points, point_num_views) + data.save_ply( + reconstructions[0], + tracks_manager, + None, + no_cameras, + no_points, + point_num_views, + ) if depthmaps: udata = data.undistorted_dataset() diff --git a/opensfm/actions/export_pmvs.py b/opensfm/actions/export_pmvs.py index 264fb2e4a..7eac4c239 100644 --- a/opensfm/actions/export_pmvs.py +++ b/opensfm/actions/export_pmvs.py @@ -1,18 +1,21 @@ +# pyre-strict import logging import os +from typing import Dict, Optional import cv2 +import networkx as nx import numpy as np -from opensfm import features -from opensfm import io -from opensfm import tracking +from opensfm import features, io, pymap, tracking, types from opensfm.dataset import DataSet, UndistortedDataSet logger: logging.Logger = logging.getLogger(__name__) -def run_dataset(data: DataSet, points, image_list, output, undistorted) -> None: +def run_dataset( + data: DataSet, points: bool, image_list: str, output: str, undistorted: bool +) -> None: """Export reconstruction to PLY format Args: @@ -70,16 +73,16 @@ def run_dataset(data: DataSet, points, image_list, output, undistorted) -> None: def export( - reconstruction, - index, - image_graph, - tracks_manager, - base_output_path, + reconstruction: types.Reconstruction, + index: int, + image_graph: Optional[nx.Graph], + tracks_manager: Optional[pymap.TracksManager], + base_output_path: str, data: DataSet, - undistorted, + undistorted: bool, udata: UndistortedDataSet, - with_points, - export_only, + with_points: bool, + export_only: Optional[Dict[str, bool]], ) -> None: logger.info("Reconstruction %d" % index) output_path = os.path.join(base_output_path, "recon%d" % index) @@ -103,7 +106,7 @@ def export( if image_graph: adj_indices = [] for adj_image in image_graph[image]: - weight = image_graph[image][adj_image]["weight"] + weight = float(image_graph[image][adj_image]["weight"]) if weight > 0 and adj_image in shot_index: adj_indices.append(shot_index[adj_image]) diff --git a/opensfm/actions/export_report.py b/opensfm/actions/export_report.py index 8b50e4f49..e717862d8 100644 --- a/opensfm/actions/export_report.py +++ b/opensfm/actions/export_report.py @@ -1,3 +1,4 @@ +# pyre-strict from opensfm import report from opensfm.dataset import DataSet diff --git a/opensfm/actions/export_rerun.py b/opensfm/actions/export_rerun.py new file mode 100644 index 000000000..d982cd0e9 --- /dev/null +++ b/opensfm/actions/export_rerun.py @@ -0,0 +1,1359 @@ +# pyre-strict +import logging +import re +from abc import ABC, abstractmethod +from collections import defaultdict +from io import BytesIO +from typing import Dict, List, Optional, Sequence, Tuple, Any +import base64 +import os + +import numpy as np +import pyproj +import cv2 +from PIL import Image as PILImage # type: ignore[import-not-found] +from opensfm import features, multiview, pymap, types, io, geometry +from opensfm import geo +from opensfm.dataset import DataSet +from opensfm import pygeometry +from opensfm.actions import export_geocoords + + +import matplotlib.cm as cm +import rerun as rr +import rerun.blueprint as rrb +from scipy.spatial import Delaunay, KDTree + +logger: logging.Logger = logging.getLogger(__name__) + +# --- Constants --- + +# Colors +GPS_COLOR = [100, 180, 255] +GCP_COMPUTED_COLOR = [120, 255, 140] +GCP_REFERENCE_COLOR = [255, 230, 100] +GCP_ERROR_COLOR = [255, 100, 100] +FEATURE_COLOR = [100, 255, 220] +LABEL_COLOR = [220, 220, 220] +CAMERA_PATH_COLOR = [255, 255, 255] + +# Sizes & Dimensions +SIZE_TIE_POINT = 0.2 +SIZE_GPS_ARROW = 1.0 +SIZE_GPS_RESIDUAL_LINE = 0.05 +SIZE_GCP_TARGET = 2.0 +SIZE_GCP_THICKNESS = 0.05 +SIZE_GCP_2D_THICKNESS = 0.5 +SIZE_GCP_COMPUTED_ARROW = 0.1 +SIZE_GCP_RESIDUAL_LINE = 0.02 +SIZE_2D_POINT_GCP = 5.0 +SIZE_2D_POINT_FEATURE = 2.0 +SIZE_LABEL_SHIFT_SHOT = 0.5 +SIZE_MATCHGRAPH_LINE = 0.1 +SIZE_CAMERA_PATH_LINE = 0.5 +SIZE_GCP_2D_BOX_HALF = 5.0 +SIZE_GCP_2D_OFFSET = 5.0 + +# Configuration +MAX_IMAGE_WIDTH = 1500 +IMAGE_COMPRESSION_QUALITY = 50 +DEFAULT_IMAGE_PLANE_DISTANCE = 2.0 +KNN_FOR_SCALE = 5 +MATCHGRAPH_MIN_INLIERS_FACTOR = 5 +COVERAGE_GRID_STEPS = 10 +COVERAGE_MARGIN = 0.01 +COVERAGE_EXTENT_MARGIN = 0.1 +COVERAGE_MIN_COUNT = 0.0 +COVERAGE_MAX_COUNT = 20.0 + +# Colormaps +MATCHGRAPH_CMAP = cm.get_cmap("plasma") +COVERAGE_CMAP = cm.get_cmap("viridis") + + +# --- Abstractions --- + +class Drawer(ABC): + """Abstract base class for visualization backends.""" + + @abstractmethod + def init(self, title: str, output_path: str) -> None: + pass + + @abstractmethod + def log_shot( + self, + shot_id: str, + pose: pygeometry.Pose, + camera: pygeometry.Camera, + image: Optional[np.ndarray], + image_plane_distance: float, + ) -> None: + pass + + @abstractmethod + def log_exif_orientation( + self, + shot_id: str, + R: np.ndarray, + t: np.ndarray, + camera: pygeometry.Camera, + image_plane_distance: float = 1.0, + ) -> None: + pass + + @abstractmethod + def log_gps( + self, + shot_id: str, + pose: pygeometry.Pose, + gps_topo: np.ndarray, + ) -> None: + pass + + @abstractmethod + def log_gcp_2d_observations( + self, + shot_id: str, + refs: List[Tuple[float, float]], + comps: List[Tuple[float, float]], + labels_refs: List[str], + labels_comps: List[str], + gcp_3d_errors: Dict[str, float], + ) -> None: + pass + + @abstractmethod + def log_points(self, positions: np.ndarray, colors: np.ndarray) -> None: + pass + + @abstractmethod + def log_camera_path(self, points: List[np.ndarray]) -> None: + pass + + @abstractmethod + def log_gcp_3d( + self, + gcp_id: str, + reference_pos: np.ndarray, + computed_pos: Optional[np.ndarray], + error: float = -1.0, + ) -> None: + pass + + @abstractmethod + def log_matchgraph(self, lines: List[List[np.ndarray]], colors: List[List[int]]) -> None: + pass + + @abstractmethod + def log_coverage_map( + self, + vertices: np.ndarray, + colors: np.ndarray, + indices: np.ndarray, + ) -> None: + pass + + @abstractmethod + def log_reference_lla(self, lat: float, lon: float, alt: float) -> None: + pass + + @abstractmethod + def log_stats_camera_models(self, stats: Dict[str, Any], data: DataSet) -> None: + pass + + @abstractmethod + def log_stats_summary(self, stats: Dict[str, Any]) -> None: + pass + + @abstractmethod + def log_stats_gps_bias(self, stats: Dict[str, Any]) -> None: + pass + + @abstractmethod + def setup_blueprint(self, camera_ids: Sequence[str]) -> None: + pass + + +class RerunDrawer(Drawer): + """Concrete implementation for Rerun visualization.""" + + def init(self, title: str, output_path: str) -> None: + rr.init(title, spawn=False) + rr.save(output_path) + rr.log("/", rr.ViewCoordinates.RIGHT_HAND_Z_UP) + + def setup_blueprint(self, camera_ids: Sequence[str]) -> None: + # Left: summary + processing time + GPS/GCP errors + per-camera biases (stacked vertically) + bias_views = [ + rrb.TextDocumentView( + origin=f"STATS/BIAS/{_sanitize_entity_path_component(cam)}", + name=f"CAMERA {cam} Bias", + ) + for cam in camera_ids + ] + + # Right: spatial viewport, then per-camera (params + residual + heatmap) rows + camera_rows = [ + rrb.Horizontal( + rrb.TextDocumentView( + origin=f"STATS/CAMERAS/{_sanitize_entity_path_component(cam)}/PARAMS", + name=f"{cam} Params", + ), + rrb.Spatial2DView( + origin=f"STATS/CAMERAS/{_sanitize_entity_path_component(cam)}/RESIDUAL", + name=f"{cam} Residuals", + ), + rrb.Spatial2DView( + origin=f"STATS/CAMERAS/{_sanitize_entity_path_component(cam)}/HEATMAP", + name=f"{cam} Heatmap", + ), + ) + for cam in camera_ids + ] + + blueprint = rrb.Blueprint( + rrb.Horizontal( + rrb.Vertical( + rrb.TextDocumentView( + origin="STATS/SUMMARY", name="Processing Summary"), + rrb.TextDocumentView( + origin="STATS/PROCESSING_TIME", name="Processing Time Details"), + rrb.TextDocumentView( + origin="STATS/ERRORS/GPS", name="GPS Errors"), + rrb.TextDocumentView( + origin="STATS/ERRORS/GCP", name="GCP Errors"), + rrb.TextDocumentView( + origin="STATS/ERRORS/ORIENTATION", name="Orientation Errors"), + *bias_views, + ), + rrb.Vertical( + rrb.Horizontal( + rrb.Spatial3DView( + origin="WORLD", + name="3D Scene", + background=[13, 17, 23], + line_grid=rrb.LineGrid3D(visible=False), + spatial_information=rrb.SpatialInformation( + show_axes=False, + show_bounding_box=False, + ), + ), + rrb.Spatial2DView(origin="SHOTS", name="Image View"), + ), + rrb.Vertical(*camera_rows), + row_shares=[0.8, 0.2], + ), + rrb.BlueprintPanel(state="expanded"), + rrb.SelectionPanel(state="collapsed"), + rrb.TimePanel(state="collapsed"), + column_shares=[0.15, 0.85], + ), + ) + rr.send_blueprint(blueprint) + + def log_shot( + self, + shot_id: str, + pose: pygeometry.Pose, + camera: pygeometry.Camera, + image: Optional[np.ndarray], + image_plane_distance: float, + ) -> None: + # Pose + t = pose.get_origin() + R_world_from_camera = pose.get_rotation_matrix().T + + rr.log( + f"WORLD/SHOTS/{shot_id}", + rr.Transform3D( + translation=t, + mat3x3=R_world_from_camera, + from_parent=False, + ), + static=True, + ) + + labels_shift = np.array([0, 0, SIZE_LABEL_SHIFT_SHOT]) + rr.log( + f"WORLD/SHOTS/{shot_id}", + rr.Points3D( + positions=labels_shift, + labels=[shot_id], + radii=0.0, + colors=[LABEL_COLOR], + ), + static=True, + ) + + # Pinhole & Image + width = int(camera.width) + height = int(camera.height) + width, height = _get_scaled_dimensions(width, height) + fx, fy, cx, cy = _get_camera_calibration(camera, width, height) + + rr.log( + f"WORLD/SHOTS/{shot_id}/CAMERA", + rr.Pinhole( + resolution=[width, height], + focal_length=[fx, fy], + principal_point=[cx, cy], + image_plane_distance=image_plane_distance, + ), + static=True, + ) + + if image is not None: + img_compressed = rr.Image(image).compress( + jpeg_quality=IMAGE_COMPRESSION_QUALITY) + rr.log(f"WORLD/SHOTS/{shot_id}/CAMERA/IMAGE", + img_compressed, static=True) + rr.log(f"SHOTS/IMAGE", img_compressed, static=True) + + def log_exif_orientation( + self, + shot_id: str, + R: np.ndarray, + t: np.ndarray, + camera: pygeometry.Camera, + image_plane_distance: float = 1.0, + ) -> None: + path = f"WORLD/EXIF_SHOTS/{shot_id}" + + # Log transform (camera-to-world) + rr.log( + path, + rr.Transform3D( + translation=t, + mat3x3=R.T, + from_parent=False, + ), + static=True, + ) + + # Log pinhole + width = int(camera.width) + height = int(camera.height) + width, height = _get_scaled_dimensions(width, height) + fx, fy, cx, cy = _get_camera_calibration(camera, width, height) + + rr.log( + f"{path}/EXIF_SHOTS/CAMERA", + rr.Pinhole( + resolution=[width, height], + focal_length=[fx, fy], + principal_point=[cx, cy], + image_plane_distance=image_plane_distance, + ), + static=True, + ) + + def log_gps( + self, + shot_id: str, + pose: pygeometry.Pose, + gps_topo: np.ndarray, + ) -> None: + base_path = f"WORLD/GPS/{shot_id}/" + origin = gps_topo.copy() + origin[2] += SIZE_GPS_ARROW + vector = np.array([0, 0, -SIZE_GPS_ARROW]) + + rr.log( + f"{base_path}/POSITION", + rr.Arrows3D( + origins=[origin], + vectors=[vector], + colors=[GPS_COLOR], + radii=SIZE_GPS_ARROW / 8.0, + ), + static=True, + ) + rr.log( + f"{base_path}/RESIDUAL", + rr.LineStrips3D( + [[pose.get_origin(), gps_topo]], + colors=[GPS_COLOR], + radii=SIZE_GPS_RESIDUAL_LINE, + ), + static=True, + ) + + def log_gcp_2d_observations( + self, + shot_id: str, + refs: List[Tuple[float, float]], + comps: List[Tuple[float, float]], + labels_refs: List[str], + labels_comps: List[str], + gcp_3d_errors: Dict[str, float], + ) -> None: + # Reference: Checkerboard + if refs: + box_centers = [] + box_half_sizes = [] + box_colors = [] + outline_centers = [] + outline_half_sizes = [] + outline_colors = [] + + q_half = SIZE_GCP_2D_BOX_HALF + offset = SIZE_GCP_2D_OFFSET + + for (x, y) in refs: + # Top-Left quadrant + box_centers.append([x - offset, y - offset]) + box_half_sizes.append([q_half, q_half]) + box_colors.append(GCP_REFERENCE_COLOR) + # Bottom-Right quadrant + box_centers.append([x + offset, y + offset]) + box_half_sizes.append([q_half, q_half]) + box_colors.append(GCP_REFERENCE_COLOR) + # Outline + outline_centers.append([x, y]) + outline_half_sizes.append([q_half * 2, q_half * 2]) + outline_colors.append(GCP_REFERENCE_COLOR) + + rr.log( + f"SHOTS/IMAGE/GCP/REFERENCE", + rr.Boxes2D( + centers=box_centers, + half_sizes=box_half_sizes, + colors=box_colors, + radii=SIZE_GCP_2D_THICKNESS, + ), + static=True, + ) + rr.log( + f"SHOTS/IMAGE/GCP/REFERENCE/OUTLINE", + rr.Boxes2D( + centers=outline_centers, + half_sizes=outline_half_sizes, + colors=outline_colors, + radii=SIZE_GCP_2D_THICKNESS, + ), + static=True, + ) + else: + rr.log(f"SHOTS/IMAGE/GCP/REFERENCE", rr.Clear(recursive=True)) + rr.log(f"SHOTS/IMAGE/GCP/REFERENCE/OUTLINE", + rr.Clear(recursive=True)) + rr.log(f"SHOTS/IMAGE/GCP/REFERENCE/LABELS", + rr.Clear(recursive=True)) + + # Computed: Arrow + if comps: + # Match computed to reference to draw arrows + ref_map = {label: pos for label, pos in zip(labels_refs, refs)} + origins = [] + vectors = [] + valid_labels = [] + + for label, comp_pos in zip(labels_comps, comps): + if label in ref_map: + ref_pos = ref_map[label] + vec = [comp_pos[0] - ref_pos[0], comp_pos[1] - ref_pos[1]] + origins.append(ref_pos) + vectors.append(vec) + + # Log the error as label + error_3d = gcp_3d_errors.get(label, -1.0) + if error_3d >= 0: + valid_labels.append(f"{label}: {error_3d:.3f}m") + else: + valid_labels.append(label) + + if origins: + rr.log( + f"SHOTS/IMAGE/GCP/COMPUTED", + rr.Arrows2D( + origins=origins, + vectors=vectors, + colors=[GCP_COMPUTED_COLOR], + labels=valid_labels, + radii=SIZE_GCP_2D_THICKNESS, + ), + static=True, + ) + else: + rr.log(f"SHOTS/IMAGE/GCP/COMPUTED", rr.Clear(recursive=True)) + else: + rr.log(f"SHOTS/IMAGE/GCP/COMPUTED", rr.Clear(recursive=True)) + + def log_points(self, positions: np.ndarray, colors: np.ndarray) -> None: + rr.log( + "WORLD/POINTS", + rr.Points3D( + positions=positions, + colors=colors, + radii=SIZE_TIE_POINT, + ), + static=True, + ) + + def log_camera_path(self, points: List[np.ndarray]) -> None: + rr.log( + "WORLD/PATH", + rr.LineStrips3D( + [points], + colors=[CAMERA_PATH_COLOR], + radii=SIZE_CAMERA_PATH_LINE, + ), + static=True, + ) + + def log_gcp_3d( + self, + gcp_id: str, + reference_pos: np.ndarray, + computed_pos: Optional[np.ndarray], + error: float = -1.0, + ) -> None: + base_path = f"WORLD/GCP/{gcp_id}" + quad_r = SIZE_GCP_TARGET / 4.0 + thickness = SIZE_GCP_THICKNESS + pos_np = np.array(reference_pos) + color = GCP_REFERENCE_COLOR if computed_pos is not None else GCP_ERROR_COLOR + + # Checkerboard + centers = [ + pos_np + [-quad_r, quad_r, 0], + pos_np + [quad_r, -quad_r, 0] + ] + half_sizes = [[quad_r, quad_r, thickness]] * 2 + + rr.log( + f"{base_path}/TARGET", + rr.Boxes3D( + centers=centers, + half_sizes=half_sizes, + colors=[color] * 2, + fill_mode="solid", + ), + static=True, + ) + rr.log( + f"{base_path}/OUTLINE", + rr.Boxes3D( + centers=[pos_np], + half_sizes=[[SIZE_GCP_TARGET / 2.0, + SIZE_GCP_TARGET / 2.0, thickness]], + colors=[color], + radii=thickness, + fill_mode="major_wireframe", + ), + static=True, + ) + + label = gcp_id + if error >= 0: + label = f"{gcp_id}: {error:.3f}m" + + rr.log( + f"{base_path}", + rr.Points3D( + positions=[pos_np + [0, 0, SIZE_GCP_TARGET]], + labels=[label], + radii=0.0, + colors=[LABEL_COLOR], + ), + static=True, + ) + + if computed_pos is not None: + rr.log( + f"{base_path}/COMPUTED", + rr.Arrows3D( + origins=[reference_pos], + vectors=[computed_pos - reference_pos], + colors=[GCP_COMPUTED_COLOR], + radii=SIZE_GCP_COMPUTED_ARROW, + ), + static=True, + ) + + def log_matchgraph(self, lines: List[List[np.ndarray]], colors: List[List[int]]) -> None: + rr.log( + "WORLD/STATS/MATCHGRAPH", + rr.LineStrips3D( + lines, + colors=colors, + radii=SIZE_MATCHGRAPH_LINE, + ), + static=True, + ) + + def log_coverage_map( + self, + vertices: np.ndarray, + colors: np.ndarray, + indices: np.ndarray, + ) -> None: + rr.log( + "WORLD/STATS/COVERAGE", + rr.Mesh3D( + vertex_positions=vertices, + vertex_colors=colors, + triangle_indices=indices, + ), + static=True, + ) + + def log_reference_lla(self, lat: float, lon: float, alt: float) -> None: + rr.log( + "world/reference", + rr.TextDocument( + f"Reference LLA:\n" + f"Latitude: {lat:.6f}°\n" + f"Longitude: {lon:.6f}°\n" + f"Altitude: {alt:.2f}m", + ), + ) + + def log_stats_camera_models(self, stats: Dict[str, Any], data: DataSet) -> None: + if "camera_errors" not in stats: + return + + for camera, params in stats["camera_errors"].items(): + sanitized_id = _sanitize_entity_path_component(camera) + sanitized_id_spaces = _sanitize_entity_path_component( + camera, strip_spaces=False) + + initial = params.get("initial_values", {}) + optimized = params.get("optimized_values", {}) + + keys = sorted(list(set(initial.keys()) | set(optimized.keys()))) + columns = {"State": ["Initial", "Optimized"]} + for k in keys: + columns[k] = [initial.get(k), optimized.get(k)] + + _log_dataframe( + f"STATS/CAMERAS/{sanitized_id}/PARAMS", + columns, + static=True, + ) + + # Residuals + heatmap as real images for Spatial2D views + res_path = os.path.join( + data.data_path, "stats", f"residuals_{sanitized_id_spaces}.png") + heat_path = os.path.join( + data.data_path, "stats", f"heatmap_{sanitized_id_spaces}.png") + + res_img = _try_load_image_as_numpy(data, res_path) + if res_img is not None: + rr.log(f"STATS/CAMERAS/{sanitized_id}/RESIDUAL", + rr.Image(res_img), static=True) + + heat_img = _try_load_image_as_numpy(data, heat_path) + if heat_img is not None: + rr.log(f"STATS/CAMERAS/{sanitized_id}/HEATMAP", + rr.Image(heat_img), static=True) + + def log_stats_summary(self, stats: Dict[str, Any]) -> None: + # Keep Dataset + Reconstruction as markdown, but remove the time-details table (moved to DataFrame) + md = "# Processing Summary\n\n" + + if "processing_statistics" in stats: + ps = stats["processing_statistics"] + md += "## Dataset\n" + md += f"- **Date**: {ps.get('date', 'N/A')}\n" + if "area" in ps: + md += f"- **Area Covered**: {ps['area'] / 1e6:.6f} km²\n" + if "steps_times" in ps and "Total Time" in ps["steps_times"]: + md += f"- **Total Time**: {ps['steps_times']['Total Time']:.2f} s\n" + md += "\n" + + if "steps_times" in ps: + steps = list(ps["steps_times"].keys()) + times = [float(ps["steps_times"][s]) for s in steps] + _log_dataframe( + "STATS/PROCESSING_TIME", + {"Step": steps, "Time (seconds)": times}, + static=True, + ) + + if "reconstruction_statistics" in stats: + rs = stats["reconstruction_statistics"] + rec_shots = rs.get("reconstructed_shots_count", 0) + init_shots = rs.get("initial_shots_count", 0) + rec_points = rs.get("reconstructed_points_count", 0) + init_points = rs.get("initial_points_count", 0) + + ratio_shots = rec_shots / init_shots * 100 if init_shots > 0 else 0 + ratio_points = rec_points / init_points * 100 if init_points > 0 else 0 + + md += "## Reconstruction\n" + md += f"- **Images**: {rec_shots}/{init_shots} ({ratio_shots:.1f}%)\n" + md += f"- **Points**: {rec_points}/{init_points} ({ratio_points:.1f}%)\n" + md += f"- **Components**: {rs.get('components', 0)}\n" + + if "features_statistics" in stats: + fs = stats["features_statistics"] + if "detected_features" in fs: + md += f"- **Detected Features (median)**: {fs['detected_features'].get('median', 'N/A')}\n" + if "reconstructed_features" in fs: + md += f"- **Reconstructed Features (median)**: {fs['reconstructed_features'].get('median', 'N/A')}\n" + + rr.log( + "STATS/SUMMARY", + rr.TextDocument(md, media_type=rr.MediaType.MARKDOWN), + static=True, + ) + + def log_stats_gps_bias(self, stats: Dict[str, Any]) -> None: + def _log_errors_df( + dst_path: str, + err_stats: Dict[str, Any], + comps: Optional[List[str]] = None, + unit: str = "meters", + ) -> None: + if comps is None: + comps = ["x", "y", "z"] + + rows_component: List[str] = [] + rows_mean: List[Optional[float]] = [] + rows_sigma: List[Optional[float]] = [] + rows_rms: List[Optional[float]] = [] + + for c in comps: + rows_component.append(c.upper() if len(c) + == 1 else c.capitalize()) + rows_mean.append(float(err_stats.get("mean", {}).get(c, 0.0))) + rows_sigma.append(float(err_stats.get("std", {}).get(c, 0.0))) + rows_rms.append(float(err_stats.get("error", {}).get(c, 0.0))) + + rows_component.append("TOTAL") + rows_mean.append(None) + rows_sigma.append(None) + rows_rms.append(float(err_stats.get("average_error", 0.0))) + + _log_dataframe( + dst_path, + { + "Component": rows_component, + f"Mean ({unit})": rows_mean, + f"Sigma ({unit})": rows_sigma, + f"RMS ({unit})": rows_rms, + }, + static=True, + ) + + gps_key = "gps_errors" + if gps_key in stats and "average_error" in stats[gps_key]: + _log_errors_df("STATS/ERRORS/GPS", stats[gps_key]) + + gcp_key = "gcp_errors" + if gcp_key in stats and "average_error" in stats[gcp_key]: + _log_errors_df("STATS/ERRORS/GCP", stats[gcp_key]) + + opk_key = "opk_errors" + if opk_key in stats and "average_error" in stats[opk_key]: + _log_errors_df( + "STATS/ERRORS/ORIENTATION", + stats[opk_key], + comps=["omega", "phi", "kappa"], + unit="degrees", + ) + + if "camera_errors" in stats: + for camera, params in stats["camera_errors"].items(): + if "bias" not in params: + continue + bias = params["bias"] + s = float(bias.get("scale", 1.0)) + t = bias.get("translation", [0.0, 0.0, 0.0]) + r = bias.get("rotation", [0.0, 0.0, 0.0]) + + _log_dataframe( + f"STATS/BIAS/{_sanitize_entity_path_component(camera)}", + { + "Component": ["Scale", "Translation X", "Translation Y", "Translation Z", "Rotation X", "Rotation Y", "Rotation Z"], + "Value (meters or degrees)": [s, float(t[0]), float(t[1]), float(t[2]), float(r[0]), float(r[1]), float(r[2])], + }, + static=True, + ) + + +# --- Main Logic --- + +def run_dataset( + data: DataSet, + output: Optional[str] = None, + reconstruction_index: int = 0, + proj: bool = False, +) -> None: + """Export reconstruction to Rerun format for 3D visualization.""" + + # 1. Load Data + reconstructions = data.load_reconstruction() + if not reconstructions: + logger.error("No reconstructions found in dataset") + return + + if reconstruction_index >= len(reconstructions): + logger.error( + f"Reconstruction index {reconstruction_index} out of range") + return + + reconstruction = reconstructions[reconstruction_index] + + centering = None + projection = None + if proj: + gcp_list_file = data._gcp_list_file() + if data.io_handler.isfile(gcp_list_file): + with data.io_handler.open_rt(gcp_list_file) as f: + proj_str = io.read_gcp_projection_string(f) + + if proj_str: + logger.info( + f"Transforming reconstruction to geocoords: {proj_str}") + reference = data.load_reference() + + # Transform to the GCP CS + projection = geo.construct_proj_transformer( + proj_str, inverse=True) + geo.transform_reconstruction_with_proj( + reconstruction, projection) + + # Center the reconstruction to avoid large coordinates + all_points = [ + p.coordinates for p in reconstruction.points.values()] + if all_points: + center = np.mean(all_points, axis=0) + else: + all_origins = [shot.pose.get_origin() + for shot in reconstruction.shots.values()] + if all_origins: + center = np.mean(all_origins, axis=0) + else: + center = np.zeros(3) + + logger.info(f"Centering reconstruction at {center}") + centering = -center + + for point in reconstruction.points.values(): + point.coordinates += centering + for shot in reconstruction.shots.values(): + shot.pose.set_origin(shot.pose.get_origin() + centering) + else: + logger.warning("No gcp_list.txt found, skipping projection.") + + logger.info( + f"Exporting reconstruction {reconstruction_index} with " + f"{len(reconstruction.shots)} shots, " + f"{len(reconstruction.points)} points" + ) + + # 2. Initialize Drawer + output_path = output or data.data_path + "/rerun.rrd" + drawer = RerunDrawer() + drawer.init("OpenSfM Reconstruction", output_path) + + camera_ids = sorted( + {shot.camera.id for shot in reconstruction.shots.values()}) + + drawer.setup_blueprint(camera_ids) + logger.info(f"Saving Rerun data to {output_path}") + + # 3. Precompute Data + image_plane_distance = _compute_image_plane_distance(reconstruction) + + # Precompute GCP observations per shot + gcp = data.load_ground_control_points() or [] + gcp_triangulations = _compute_gcp_triangulations(data, reconstruction, gcp) + gcp_3d_errors = _compute_gcp_3d_errors( + reconstruction, gcp, gcp_triangulations, centering) + shot_gcp_obs = _precompute_gcp_observations( + data, reconstruction, gcp, gcp_triangulations) + + # 4. Time-Dependent Logging (Shots) + reference = reconstruction.reference + + for shot_id, shot in reconstruction.shots.items(): + sequence_id = _get_shot_sequence_id(shot_id) + + # Load image + image = None + try: + image = data.load_image(shot_id) + width = int(shot.camera.width) + height = int(shot.camera.height) + width, height = _get_scaled_dimensions(width, height) + if image.shape[1] != width or image.shape[0] != height: + image = cv2.resize(image, (width, height), + interpolation=cv2.INTER_AREA) + except Exception as e: + logger.warning(f"Could not load image for {shot_id}: {e}") + + # Log Shot Pose & Image + drawer.log_shot(shot_id, shot.pose, shot.camera, + image, image_plane_distance) + + # Log GPS & EXIF Orientation + gps_pos = None + if reference and shot.metadata and shot.metadata.gps_position.has_value: + gps_topo = shot.metadata.gps_position.value + gps_pos = gps_topo + if projection is not None: + gps_pos = np.array(geo.transform_to_proj( + gps_topo, reference, projection)) + if centering is not None: + gps_pos += centering + + drawer.log_gps(shot_id, shot.pose, gps_pos) + + has_opk = shot.metadata and shot.metadata.opk_angles.has_value + if gps_pos is not None or has_opk: + t_exif = shot.pose.get_origin() + if has_opk: + opk = shot.metadata.opk_angles.value + R_exif = geometry.rotation_from_opk( + np.radians(opk[0]), np.radians(opk[1]), np.radians(opk[2]) + ) + else: + R_exif = shot.pose.get_rotation_matrix() + + drawer.log_exif_orientation( + shot_id, R_exif, t_exif, shot.camera, image_plane_distance) + + # Log GCP 2D Observations + if shot_id in shot_gcp_obs: + obs = shot_gcp_obs[shot_id] + drawer.log_gcp_2d_observations( + shot_id, + obs["refs"], + obs["comps"], + obs["labels_refs"], + obs["labels_comps"], + gcp_3d_errors, + ) + else: + drawer.log_gcp_2d_observations( + shot_id, [], [], [], [], gcp_3d_errors) + + # 5. Static Logging (Structure & Stats) + + # Reference + if reconstruction.reference: + ref = reconstruction.reference + drawer.log_reference_lla(ref.lat, ref.lon, ref.alt) + + # Load and log stats.json if available + try: + stats = data.load_stats() + if stats: + drawer.log_stats_summary(stats) + drawer.log_stats_camera_models(stats, data) + drawer.log_stats_gps_bias(stats) + except Exception as e: + logger.warning(f"Could not load or log stats.json: {e}") + + # 3D Points + _export_points(reconstruction, drawer) + + # Camera Path + _export_camera_path(reconstruction, drawer) + + # GCP 3D Geometry + if gcp: + _export_gcp_3d(data, reconstruction, gcp, + gcp_triangulations, gcp_3d_errors, drawer, centering) + + # Match Graph + if data.tracks_exists(): + tracks_manager = data.load_tracks_manager() + _export_matchgraph(data, reconstruction, tracks_manager, drawer) + + # Coverage Map + _export_coverage_map(data, reconstruction, drawer) + + logger.info(f"Rerun export completed: {output_path}") + logger.info(f"Open with: rerun {output_path}") + + +# --- Helper Functions --- + +def _get_shot_sequence_id(shot_id: str) -> int: + try: + numbers = re.findall(r'\d+', shot_id) + if numbers: + return int(numbers[-1]) + except: + pass + return hash(shot_id) & 0x7FFFFFFF + + +def _get_scaled_dimensions(width: int, height: int) -> tuple[int, int]: + if width > MAX_IMAGE_WIDTH: + scale = MAX_IMAGE_WIDTH / width + return int(width * scale), int(height * scale) + return width, height + + +def _compute_image_plane_distance(reconstruction: types.Reconstruction) -> float: + positions = np.array([shot.pose.get_origin() + for shot in reconstruction.shots.values()]) + if len(positions) < 2: + return DEFAULT_IMAGE_PLANE_DISTANCE + try: + tree = KDTree(positions) + dists, _ = tree.query(positions, k=KNN_FOR_SCALE) + median_nn_dist = np.median(dists[:, 1]) + return float(median_nn_dist) + except ImportError: + return DEFAULT_IMAGE_PLANE_DISTANCE + + +def _get_camera_calibration(camera: pygeometry.Camera, width: int, height: int): + fx = fy = camera.focal * max(width, height) + if hasattr(camera, "focal_x") and hasattr(camera, "focal_y"): + fx = camera.focal_x * max(width, height) + fy = camera.focal_y * max(width, height) + cx = width / 2.0 + cy = height / 2.0 + if hasattr(camera, "c_x") and hasattr(camera, "c_y"): + cx = camera.c_x * max(width, height) + width / 2.0 + cy = camera.c_y * max(width, height) + height / 2.0 + return fx, fy, cx, cy + + +def _compute_gcp_triangulations( + data: DataSet, + reconstruction: types.Reconstruction, + gcp: List[pymap.GroundControlPoint] +) -> Dict[str, Optional[np.ndarray]]: + triangulations = {} + for point in gcp: + if point.lla: + triangulations[point.id] = multiview.triangulate_gcp( + point, reconstruction.shots, data.config["gcp_reprojection_error_threshold"] + ) + return triangulations + + +def _compute_gcp_3d_errors( + reconstruction: types.Reconstruction, + gcp: List[pymap.GroundControlPoint], + triangulations: Dict[str, Optional[np.ndarray]], + centering: Optional[np.ndarray] = None, +) -> Dict[str, float]: + errors = {} + reference = reconstruction.reference + for point in gcp: + if point.id in triangulations and triangulations[point.id] is not None: + comp_pos = triangulations[point.id] + + if centering is not None: + ref_pos = np.array(point.coordinates) + centering + errors[point.id] = np.linalg.norm(comp_pos - ref_pos) + elif point.lla: + ref_pos = reference.to_topocentric(*point.lla_vec) + errors[point.id] = np.linalg.norm(comp_pos - ref_pos) + else: + errors[point.id] = -1.0 + else: + errors[point.id] = -1.0 + return errors + + +def _precompute_gcp_observations( + data: DataSet, + reconstruction: types.Reconstruction, + gcp: List[pymap.GroundControlPoint], + triangulations: Dict[str, Optional[np.ndarray]] +) -> Dict[str, Dict[str, Any]]: + """Precompute 2D GCP observations per shot.""" + shot_obs = defaultdict( + lambda: {"refs": [], "comps": [], "labels_refs": [], "labels_comps": []}) + reference = reconstruction.reference + + for point in gcp: + if not point.lla: + continue + + triangulated = triangulations.get(point.id) + + # Project to shots + for obs in point.observations: + shot_id = obs.shot_id + if shot_id not in reconstruction.shots: + continue + + shot = reconstruction.shots[shot_id] + width = int(shot.camera.width) + height = int(shot.camera.height) + width, height = _get_scaled_dimensions(width, height) + normalizer = max(width, height) + + # Reference pixel + px = obs.projection[0] * normalizer + width / 2.0 + py = obs.projection[1] * normalizer + height / 2.0 + + shot_obs[shot_id]["refs"].append((px, py)) + shot_obs[shot_id]["labels_refs"].append(point.id) + + # Computed pixel + if triangulated is not None: + projected = shot.project(triangulated) + px_comp = projected[0] * normalizer + width / 2.0 + py_comp = projected[1] * normalizer + height / 2.0 + shot_obs[shot_id]["comps"].append((px_comp, py_comp)) + shot_obs[shot_id]["labels_comps"].append(point.id) + + return shot_obs + + +def _export_points(reconstruction: types.Reconstruction, drawer: Drawer) -> None: + if not reconstruction.points: + return + positions = [] + colors = [] + for point in reconstruction.points.values(): + positions.append(point.coordinates) + c = point.color + if all(val <= 1.0 for val in c): + c = [int(val * 255) for val in c] + colors.append(c) + + drawer.log_points(np.array(positions), np.array(colors, dtype=np.uint8)) + logger.info(f"Exported {len(positions)} automatic tie points") + + +def _export_camera_path(reconstruction: types.Reconstruction, drawer: Drawer) -> None: + shots_with_time = [] + for shot in reconstruction.shots.values(): + if shot.metadata and shot.metadata.capture_time.has_value: + shots_with_time.append(shot) + + if len(shots_with_time) < 2: + return + + shots_with_time.sort(key=lambda x: x.metadata.capture_time.value) + points = [shot.pose.get_origin() for shot in shots_with_time] + drawer.log_camera_path(points) + logger.info(f"Exported camera path with {len(points)} points") + + +def _export_gcp_3d( + data: DataSet, + reconstruction: types.Reconstruction, + gcp: List[pymap.GroundControlPoint], + triangulations: Dict[str, Optional[np.ndarray]], + gcp_3d_errors: Dict[str, float], + drawer: Drawer, + centering: Optional[np.ndarray] = None, +) -> None: + reference = reconstruction.reference + count = 0 + for point in gcp: + pos = None + if centering is not None: + pos = np.array(point.coordinates) + centering + elif point.lla: + pos = reference.to_topocentric(*point.lla_vec) + + if pos is not None: + triangulated = triangulations.get(point.id) + error = gcp_3d_errors.get(point.id, -1.0) + + drawer.log_gcp_3d(point.id, pos, triangulated, error) + count += 1 + logger.info(f"Exported {count} Ground Control Points") + + +def _export_matchgraph( + data: DataSet, + reconstruction: types.Reconstruction, + tracks_manager: pymap.TracksManager, + drawer: Drawer +) -> None: + logger.info("Exporting match graph...") + all_shots = list(reconstruction.shots.keys()) + all_points = list(reconstruction.points.keys()) + connectivity = tracks_manager.get_all_pairs_connectivity( + all_shots, all_points) + + if not connectivity: + return + + all_values = list(connectivity.values()) + if not all_values: + return + + lowest = np.percentile(all_values, 5) + highest = np.percentile(all_values, 95) + + lines = [] + colors = [] + min_inliers = data.config.get("resection_min_inliers", 15) + + for (node1, node2), edge in connectivity.items(): + if edge < MATCHGRAPH_MIN_INLIERS_FACTOR * min_inliers: + continue + if node1 not in reconstruction.shots or node2 not in reconstruction.shots: + continue + + p1 = reconstruction.shots[node1].pose.get_origin() + p2 = reconstruction.shots[node2].pose.get_origin() + + c_val = max(0.0, min(1.0, 1.0 - (float(edge) - lowest) / + (highest - lowest + 1e-6))) + rgba = MATCHGRAPH_CMAP(1.0 - c_val) + + lines.append([p1, p2]) + colors.append([int(c * 255) for c in rgba[:3]]) + + if lines: + drawer.log_matchgraph(lines, colors) + logger.info(f"Exported match graph with {len(lines)} edges") + + +def _export_coverage_map( + data: DataSet, + reconstruction: types.Reconstruction, + drawer: Drawer +) -> None: + logger.info("Exporting coverage map...") + if not reconstruction.points: + return + + points = np.array([p.coordinates for p in reconstruction.points.values()]) + if len(points) == 0: + return + + min_pt = np.percentile(points, 1, axis=0) + max_pt = np.percentile(points, 99, axis=0) + median_z = np.median(points[:, 2]) + + extent = max_pt - min_pt + min_pt -= extent * COVERAGE_EXTENT_MARGIN + max_pt += extent * COVERAGE_EXTENT_MARGIN + + vertices = [] + for shot in reconstruction.shots.values(): + w = shot.camera.width + h = shot.camera.height + + m = COVERAGE_MARGIN + steps = np.linspace(0, 1, COVERAGE_GRID_STEPS) + steps = m + steps * (1 - 2 * m) + + xs = w * steps + ys = h * steps + pixels = features.normalized_image_coordinates( + np.array([[x, y] for y in ys for x in xs]), w, h) + + bearings = shot.camera.pixel_bearing_many(pixels) + R_wc = shot.pose.get_rotation_matrix().T + bearings_world = bearings @ R_wc.T + origin = shot.pose.get_origin() + + for direction in bearings_world: + if abs(direction[2]) < 1e-6: + continue + t = (median_z - origin[2]) / direction[2] + if t <= 0: + continue + p_ground = origin + t * direction + + if (p_ground[0] >= min_pt[0] and p_ground[0] <= max_pt[0] and + p_ground[1] >= min_pt[1] and p_ground[1] <= max_pt[1]): + vertices.append(p_ground) + + if not vertices or len(vertices) < 3: + return + + vertices = np.array(vertices) + tri = Delaunay(vertices[:, :2]) + indices = tri.simplices + + counts = np.zeros(len(vertices), dtype=int) + for shot in reconstruction.shots.values(): + p_cam = shot.pose.transform_many(vertices) + valid_z = p_cam[:, 2] > 0 + indices_valid_z = np.where(valid_z)[0] + + if len(indices_valid_z) == 0: + continue + + p_cam_valid = p_cam[indices_valid_z] + projections = shot.camera.project_many(p_cam_valid) + + w = shot.camera.width + h = shot.camera.height + normalizer = max(w, h) + + px = projections[:, 0] * normalizer + w / 2.0 + py = projections[:, 1] * normalizer + h / 2.0 + + in_view = (px >= 0) & (px < w) & (py >= 0) & (py < h) + counts[indices_valid_z[in_view]] += 1 + + norm_counts = np.clip(counts, COVERAGE_MIN_COUNT, COVERAGE_MAX_COUNT) + norm_counts = (norm_counts - COVERAGE_MIN_COUNT) / \ + (COVERAGE_MAX_COUNT - COVERAGE_MIN_COUNT) + + colors = COVERAGE_CMAP(norm_counts) + vertex_colors = (colors[:, :3] * 255).astype(np.uint8) + + drawer.log_coverage_map(vertices, vertex_colors, indices) + logger.info(f"Exported coverage map with {len(vertices)} vertices") + + +def _sanitize_entity_path_component(value: str, strip_spaces: bool = True) -> str: + # Rerun entity paths use "/" as hierarchy separator. + sanitized = value.replace("/", "_") + if strip_spaces: + sanitized = sanitized.replace(" ", "_") + return sanitized + + +def _try_load_image_as_numpy(data: DataSet, path: str) -> Optional[np.ndarray]: + if not data.io_handler.isfile(path): + return None + try: + with data.io_handler.open_rb(path) as f: + raw = f.read() + img = PILImage.open(BytesIO(raw)) + if img.mode not in ("RGB", "RGBA"): + img = img.convert("RGB") + return np.array(img) + except Exception as e: + logger.warning(f"Failed to load image {path}: {e}") + return None + + +def _log_dataframe(path: str, columns: Dict[str, Sequence[Any]], *, static: bool = True) -> None: + """ + Log a dataframe in the way expected by `DataframeView` (Selection View). + + Columns must be equal-length sequences (pad with None if needed). + """ + if not columns: + return + + cols: Dict[str, List[Any]] = {k: list(v) for k, v in columns.items()} + max_len = max((len(v) for v in cols.values()), default=0) + for k, v in cols.items(): + if len(v) < max_len: + v.extend([None] * (max_len - len(v))) + + # Convert dict to a Markdown Table string + header = "| " + " | ".join(cols.keys()) + " |" + separator = "| " + \ + " | ".join([":---"] + ["---:"] * (len(cols) - 1)) + " |" + body = [] + for i in range(max_len): + cells = [] + for v in cols.values(): + val = v[i] + if isinstance(val, (float, np.floating)): + cells.append(f"{val:.2f}") + else: + cells.append(str(val) if val is not None else "") + body.append("| " + " | ".join(cells) + " |") + md_table = "\n".join([header, separator] + body) + + rr.log(path, rr.TextDocument( + md_table, media_type="text/markdown"), static=True) diff --git a/opensfm/actions/export_visualsfm.py b/opensfm/actions/export_visualsfm.py index a8f1d1db9..d9ae6dc77 100644 --- a/opensfm/actions/export_visualsfm.py +++ b/opensfm/actions/export_visualsfm.py @@ -1,16 +1,17 @@ +# pyre-strict import logging import os import sys +from typing import Dict, Optional -from opensfm import io -from opensfm import transformations as tf +from opensfm import io, pymap, transformations as tf, types from opensfm.dataset import DataSet, UndistortedDataSet logger: logging.Logger = logging.getLogger(__name__) -def run_dataset(data: DataSet, points, image_list) -> None: +def run_dataset(data: DataSet, points: bool, image_list: str) -> None: udata = data.undistorted_dataset() validate_image_names(data, udata) @@ -30,7 +31,11 @@ def run_dataset(data: DataSet, points, image_list) -> None: def export( - reconstruction, tracks_manager, udata: UndistortedDataSet, with_points, export_only + reconstruction: types.Reconstruction, + tracks_manager: pymap.TracksManager, + udata: UndistortedDataSet, + with_points: bool, + export_only: Optional[Dict[str, bool]], ) -> None: lines = ["NVM_V3", "", str(len(reconstruction.shots))] shot_size_cache = {} diff --git a/opensfm/actions/extend_reconstruction.py b/opensfm/actions/extend_reconstruction.py index 2d9bd303d..9d62fd5fe 100644 --- a/opensfm/actions/extend_reconstruction.py +++ b/opensfm/actions/extend_reconstruction.py @@ -1,6 +1,8 @@ +# pyre-strict +from typing import Optional + from opensfm import io, reconstruction from opensfm.dataset_base import DataSetBase -from typing import Optional def run_dataset(data: DataSetBase, input: Optional[str], output: Optional[str]) -> None: diff --git a/opensfm/actions/extract_metadata.py b/opensfm/actions/extract_metadata.py index baca35d75..c4df048de 100644 --- a/opensfm/actions/extract_metadata.py +++ b/opensfm/actions/extract_metadata.py @@ -1,7 +1,9 @@ +# pyre-strict import copy import logging from functools import partial from typing import Any, Dict + from opensfm import exif from opensfm.dataset_base import DataSetBase diff --git a/opensfm/actions/match_features.py b/opensfm/actions/match_features.py index 84b1937a1..561a7b237 100644 --- a/opensfm/actions/match_features.py +++ b/opensfm/actions/match_features.py @@ -1,7 +1,8 @@ +# pyre-strict from timeit import default_timer as timer +from typing import Any, Dict, List, Tuple -from opensfm import io -from opensfm import matching +from opensfm import io, matching from opensfm.dataset_base import DataSetBase @@ -18,7 +19,12 @@ def run_dataset(data: DataSetBase) -> None: write_report(data, preport, list(pairs_matches.keys()), end - start) -def write_report(data: DataSetBase, preport, pairs, wall_time) -> None: +def write_report( + data: DataSetBase, + preport: Dict[str, Any], + pairs: List[Tuple[str, str]], + wall_time: float, +) -> None: report = { "wall_time": wall_time, "num_pairs": len(pairs), diff --git a/opensfm/actions/mesh.py b/opensfm/actions/mesh.py index d3a952c93..4f753f6af 100644 --- a/opensfm/actions/mesh.py +++ b/opensfm/actions/mesh.py @@ -1,3 +1,4 @@ +# pyre-strict from opensfm import mesh from opensfm.dataset_base import DataSetBase diff --git a/opensfm/actions/reconstruct.py b/opensfm/actions/reconstruct.py index 59e93e6e6..10f1904d0 100644 --- a/opensfm/actions/reconstruct.py +++ b/opensfm/actions/reconstruct.py @@ -1,5 +1,6 @@ -from opensfm import io -from opensfm import reconstruction +# pyre-strict +import gc +from opensfm import io, reconstruction from opensfm.dataset_base import DataSetBase @@ -24,5 +25,8 @@ def run_dataset(data: DataSetBase) -> None: else: raise RuntimeError(f"Unsupported algorithm for reconstruction {algorithm}") - data.save_reconstruction(reconstructions) + del tracks_manager + gc.collect() + data.save_report(io.json_dumps(report), "reconstruction.json") + data.save_reconstruction(reconstructions) diff --git a/opensfm/actions/reconstruct_from_prior.py b/opensfm/actions/reconstruct_from_prior.py index 067be1fa2..41b37b9ad 100644 --- a/opensfm/actions/reconstruct_from_prior.py +++ b/opensfm/actions/reconstruct_from_prior.py @@ -1,5 +1,5 @@ -from opensfm import io -from opensfm import reconstruction +# pyre-strict +from opensfm import io, reconstruction from opensfm.dataset_base import DataSetBase diff --git a/opensfm/actions/undistort.py b/opensfm/actions/undistort.py index 49a4dbdcb..d9b97dc8a 100644 --- a/opensfm/actions/undistort.py +++ b/opensfm/actions/undistort.py @@ -1,3 +1,4 @@ +# pyre-strict import os from typing import Optional diff --git a/opensfm/align.py b/opensfm/align.py index 034799c96..1b36c092c 100644 --- a/opensfm/align.py +++ b/opensfm/align.py @@ -1,3 +1,4 @@ +# pyre-strict """Tools to align a reconstruction to GPS and GCP data.""" import logging @@ -7,6 +8,7 @@ import cv2 import numpy as np +from numpy.typing import NDArray from opensfm import multiview, pygeometry, pymap, transformations as tf, types @@ -19,10 +21,10 @@ def align_reconstruction( config: Dict[str, Any], use_gps: bool = True, bias_override: bool = False, -) -> Optional[Tuple[float, np.ndarray, np.ndarray]]: +) -> Optional[Tuple[float, NDArray, NDArray]]: """Align a reconstruction with GPS and GCP data.""" has_scaled_rigs = any( - [True for ri in reconstruction.rig_instances.values() if len(ri.shots) > 1] + True for ri in reconstruction.rig_instances.values() if len(ri.shots) > 1 ) use_scale = not has_scaled_rigs if bias_override and config["bundle_compensate_gps_bias"]: @@ -38,7 +40,7 @@ def align_reconstruction( def apply_similarity_pose( - pose: pygeometry.Pose, s: float, A: np.ndarray, b: np.ndarray + pose: pygeometry.Pose, s: float, A: NDArray, b: NDArray ) -> None: """Apply a similarity (y = s A x + b) to an object having a 'pose' member.""" R = pose.get_rotation_matrix() @@ -50,7 +52,7 @@ def apply_similarity_pose( def apply_similarity( - reconstruction: types.Reconstruction, s: float, A: np.ndarray, b: np.ndarray + reconstruction: types.Reconstruction, s: float, A: NDArray, b: NDArray ) -> None: """Apply a similarity (y = s A x + b) to a reconstruction. @@ -78,7 +80,7 @@ def compute_reconstruction_similarity( config: Dict[str, Any], use_gps: bool, use_scale: bool, -) -> Optional[Tuple[float, np.ndarray, np.ndarray]]: +) -> Optional[Tuple[float, NDArray, NDArray]]: """Compute similarity so that the reconstruction is aligned with GPS and GCP data. Config parameter `align_method` can be used to choose the alignment method. @@ -119,19 +121,19 @@ def alignment_constraints( reconstruction: types.Reconstruction, gcp: List[pymap.GroundControlPoint], use_gps: bool, -) -> Tuple[List[np.ndarray], List[np.ndarray]]: +) -> Tuple[List[NDArray], List[NDArray]]: """Gather alignment constraints to be used by checking bundle_use_gcp and bundle_use_gps.""" X, Xp = [], [] - # Get Ground Control Point correspondences if gcp and config["bundle_use_gcp"]: - triangulated, measured = triangulate_all_gcp(reconstruction, gcp) + triangulated, measured = triangulate_all_gcp( + reconstruction, gcp, config["gcp_reprojection_error_threshold"] + ) X.extend(triangulated) Xp.extend(measured) - # Get camera center correspondences - if use_gps and config["bundle_use_gps"]: + elif use_gps and config["bundle_use_gps"]: for rig_instance in reconstruction.rig_instances.values(): gpses = [ shot.metadata.gps_position.value @@ -141,7 +143,6 @@ def alignment_constraints( if len(gpses) > 0: X.append(rig_instance.pose.get_origin()) Xp.append(np.average(gpses, axis=0)) - return X, Xp @@ -189,7 +190,7 @@ def compute_naive_similarity( gcp: List[pymap.GroundControlPoint], use_gps: bool, use_scale: bool, -) -> Optional[Tuple[float, np.ndarray, np.ndarray]]: +) -> Optional[Tuple[float, NDArray, NDArray]]: """Compute similarity with GPS and GCP data using direct 3D-3D matches.""" X, Xp = alignment_constraints(config, reconstruction, gcp, use_gps) @@ -234,7 +235,7 @@ def compute_orientation_prior_similarity( gcp: List[pymap.GroundControlPoint], use_gps: bool, use_scale: bool, -) -> Optional[Tuple[float, np.ndarray, np.ndarray]]: +) -> Optional[Tuple[float, NDArray, NDArray]]: """Compute similarity with GPS data assuming particular a camera orientation. In some cases, using 3D-3D matches directly fails to find proper @@ -278,7 +279,7 @@ def compute_orientation_prior_similarity( # Clamp shots pair scale to 1km, so the # optimizer can still catch-up acceptable error - max_scale = 1000 + max_scale = 1000.0 current_scale = np.linalg.norm(b) if two_shots and current_scale > max_scale: b = max_scale * b / current_scale @@ -309,7 +310,7 @@ def set_gps_bias( config: Dict[str, Any], gcp: List[pymap.GroundControlPoint], use_scale: bool, -) -> Optional[Tuple[float, np.ndarray, np.ndarray]]: +) -> Optional[Tuple[float, NDArray, NDArray]]: """Compute and set the bias transform of the GPS coordinate system wrt. to the GCP one.""" # Compute similarity ('gps_bias') that brings the reconstruction on the GCPs ONLY @@ -335,7 +336,6 @@ def set_gps_bias( per_camera_transform = {} for camera_id, shots_id in per_camera_shots.items(): - # As we re-use 'compute_reconstruction_similarity', we need to construct a 'Reconstruction' subrec = types.Reconstruction() subrec.add_camera(reconstruction.cameras[camera_id]) @@ -345,7 +345,7 @@ def set_gps_bias( subrec, [], config, True, use_scale ) - if any([True for x in per_camera_transform.values() if not x]): + if any(True for x in per_camera_transform.values() if not x): logger.warning("Cannot compensate some shots, GPS bias won't be compensated.") else: for camera_id, transform in per_camera_transform.items(): @@ -363,7 +363,7 @@ def set_gps_bias( def estimate_ground_plane( reconstruction: types.Reconstruction, config: Dict[str, Any] -) -> Optional[np.ndarray]: +) -> Optional[NDArray]: """Estimate ground plane orientation. It assumes cameras are all at a similar height and uses the @@ -406,8 +406,8 @@ def estimate_ground_plane( def get_horizontal_and_vertical_directions( - R: np.ndarray, orientation: int -) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + R: NDArray, orientation: int +) -> Tuple[NDArray, NDArray, NDArray]: """Get orientation vectors from camera rotation matrix and orientation tag. Return a 3D vectors pointing to the positive XYZ directions of the image. @@ -435,14 +435,17 @@ def get_horizontal_and_vertical_directions( def triangulate_all_gcp( - reconstruction: types.Reconstruction, gcp: List[pymap.GroundControlPoint] -) -> Tuple[List[np.ndarray], List[np.ndarray]]: + reconstruction: types.Reconstruction, + gcp: List[pymap.GroundControlPoint], + threshold: float, +) -> Tuple[List[NDArray], List[NDArray]]: """Group and triangulate Ground Control Points seen in 2+ images.""" triangulated, measured = [], [] for point in gcp: x = multiview.triangulate_gcp( point, reconstruction.shots, + threshold, ) if x is not None and len(point.lla): point_enu = np.array( diff --git a/opensfm/bow.py b/opensfm/bow.py index 544bcb079..0202ca066 100644 --- a/opensfm/bow.py +++ b/opensfm/bow.py @@ -1,20 +1,25 @@ +# pyre-strict import os.path +from typing import Any, Dict, Optional, Tuple import cv2 import numpy as np +from numpy.typing import NDArray from opensfm import context class BagOfWords: - def __init__(self, words, frequencies): + def __init__(self, words: NDArray, frequencies: NDArray) -> None: self.words = words self.frequencies = frequencies - self.weights = np.log(frequencies.sum() / frequencies) + self.weights: NDArray = np.log(frequencies.sum() / frequencies) FLANN_INDEX_KDTREE = 1 flann_params = {"algorithm": FLANN_INDEX_KDTREE, "trees": 8, "checks": 300} - self.index = context.flann_Index(words, flann_params) + self.index: cv2.flann_Index = context.flann_Index(words, flann_params) - def map_to_words(self, descriptors, k, matcher_type="FLANN"): + def map_to_words( + self, descriptors: NDArray, k: int, matcher_type: str = "FLANN" + ) -> NDArray: if matcher_type == "FLANN": params = {"checks": 200} idx, dist = self.index.knnSearch(descriptors, k, params=params) @@ -25,11 +30,17 @@ def map_to_words(self, descriptors, k, matcher_type="FLANN"): idx = np.array(idx).astype(np.int32) return idx - def histogram(self, words): + def histogram(self, words: NDArray) -> NDArray: h = np.bincount(words, minlength=len(self.words)) * self.weights return h / h.sum() - def bow_distance(self, w1, w2, h1=None, h2=None): + def bow_distance( + self, + w1: NDArray, + w2: NDArray, + h1: Optional[NDArray] = None, + h2: Optional[NDArray] = None, + ) -> float: if h1 is None: h1 = self.histogram(w1) if h2 is None: @@ -37,7 +48,7 @@ def bow_distance(self, w1, w2, h1=None, h2=None): return np.fabs(h1 - h2).sum() -def load_bow_words_and_frequencies(config): +def load_bow_words_and_frequencies(config: Dict[str, Any]) -> Tuple[NDArray, NDArray]: if config["bow_file"] == "bow_hahog_root_uchar_10000.npz": assert config["feature_type"] == "HAHOG" assert config["feature_root"] @@ -48,7 +59,7 @@ def load_bow_words_and_frequencies(config): return bow["words"], bow["frequencies"] -def load_vlad_words_and_frequencies(config): +def load_vlad_words_and_frequencies(config: Dict[str, Any]) -> Tuple[NDArray, NDArray]: if config["vlad_file"] == "bow_hahog_root_uchar_64.npz": assert config["feature_type"] == "HAHOG" assert config["feature_root"] @@ -59,6 +70,6 @@ def load_vlad_words_and_frequencies(config): return vlad["words"], vlad["frequencies"] -def load_bows(config) -> BagOfWords: +def load_bows(config: Dict[str, Any]) -> BagOfWords: words, frequencies = load_bow_words_and_frequencies(config) return BagOfWords(words, frequencies) diff --git a/opensfm/commands/__init__.py b/opensfm/commands/__init__.py index d4b208c26..5031be9d2 100644 --- a/opensfm/commands/__init__.py +++ b/opensfm/commands/__init__.py @@ -1,3 +1,8 @@ +# pyre-strict + +from types import ModuleType +from typing import List + from . import ( align_submodels, bundle, @@ -14,9 +19,10 @@ export_ply, export_pmvs, export_report, + export_rerun, export_visualsfm, - extract_metadata, extend_reconstruction, + extract_metadata, match_features, mesh, reconstruct, @@ -27,7 +33,7 @@ from .command_runner import command_runner -opensfm_commands = [ +opensfm_commands: List[ModuleType] = [ extract_metadata, detect_features, match_features, @@ -48,6 +54,7 @@ export_colmap, export_geocoords, export_report, + export_rerun, extend_reconstruction, create_submodels, align_submodels, diff --git a/opensfm/commands/align_submodels.py b/opensfm/commands/align_submodels.py index f3bc8c917..8823d173e 100644 --- a/opensfm/commands/align_submodels.py +++ b/opensfm/commands/align_submodels.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import align_submodels +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/bundle.py b/opensfm/commands/bundle.py index b7e6f668a..238f80143 100644 --- a/opensfm/commands/bundle.py +++ b/opensfm/commands/bundle.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import bundle +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/command.py b/opensfm/commands/command.py index 077c7c811..3ce5a17aa 100644 --- a/opensfm/commands/command.py +++ b/opensfm/commands/command.py @@ -1,14 +1,17 @@ -from timeit import default_timer as timer +# pyre-strict import argparse +from timeit import default_timer as timer + from opensfm.dataset import DataSet + class CommandBase: - """ Base class for executable commands.""" + """Base class for executable commands.""" name = "Undefined command" help = "Undefined command help" - def run(self, data, args: argparse.Namespace) -> None: + def run(self, data: DataSet, args: argparse.Namespace) -> None: start = timer() self.run_impl(data, args) end = timer() diff --git a/opensfm/commands/command_runner.py b/opensfm/commands/command_runner.py index 6498a7ef6..22daa73dd 100644 --- a/opensfm/commands/command_runner.py +++ b/opensfm/commands/command_runner.py @@ -1,11 +1,18 @@ -from typing import Any, Callable, List +# pyre-strict import argparse +from types import ModuleType +from typing import Callable, ContextManager, List from opensfm import log +from opensfm.dataset import DataSet -def command_runner(all_commands_types: List[Any], dataset_factory: Callable, dataset_choices: List[str]) -> None: - """ Main entry point for running the passed SfM commands types.""" +def command_runner( + all_commands_types: List[ModuleType], + dataset_factory: Callable[[str, str], ContextManager[DataSet]], + dataset_choices: List[str], +) -> None: + """Main entry point for running the passed SfM commands types.""" log.setup() # Create the top-level parser diff --git a/opensfm/commands/compute_depthmaps.py b/opensfm/commands/compute_depthmaps.py index 57c647947..2e6b68ec2 100644 --- a/opensfm/commands/compute_depthmaps.py +++ b/opensfm/commands/compute_depthmaps.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import compute_depthmaps +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/compute_statistics.py b/opensfm/commands/compute_statistics.py index a445fc7c4..0117a93ae 100644 --- a/opensfm/commands/compute_statistics.py +++ b/opensfm/commands/compute_statistics.py @@ -1,7 +1,10 @@ -from . import command +# pyre-strict import argparse -from opensfm.dataset import DataSet + from opensfm.actions import compute_statistics +from opensfm.dataset import DataSet + +from . import command class Command(command.CommandBase): diff --git a/opensfm/commands/create_rig.py b/opensfm/commands/create_rig.py index 120e471b9..4a8766e36 100644 --- a/opensfm/commands/create_rig.py +++ b/opensfm/commands/create_rig.py @@ -1,10 +1,11 @@ +# pyre-strict +import argparse import json from opensfm.actions import create_rig +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/create_submodels.py b/opensfm/commands/create_submodels.py index bc76dcba8..bb62a497e 100644 --- a/opensfm/commands/create_submodels.py +++ b/opensfm/commands/create_submodels.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import create_submodels +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/create_tracks.py b/opensfm/commands/create_tracks.py index d5ee6e6ac..7d4d45244 100644 --- a/opensfm/commands/create_tracks.py +++ b/opensfm/commands/create_tracks.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import create_tracks +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/detect_features.py b/opensfm/commands/detect_features.py index b94f83289..a36cb1c78 100644 --- a/opensfm/commands/detect_features.py +++ b/opensfm/commands/detect_features.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import detect_features +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/export_bundler.py b/opensfm/commands/export_bundler.py index 13bb1a241..f06c66d4b 100644 --- a/opensfm/commands/export_bundler.py +++ b/opensfm/commands/export_bundler.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import export_bundler +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/export_colmap.py b/opensfm/commands/export_colmap.py index b5fab0a00..21c7fb5b2 100644 --- a/opensfm/commands/export_colmap.py +++ b/opensfm/commands/export_colmap.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import export_colmap +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/export_geocoords.py b/opensfm/commands/export_geocoords.py index f136732da..830213229 100644 --- a/opensfm/commands/export_geocoords.py +++ b/opensfm/commands/export_geocoords.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import export_geocoords +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/export_openmvs.py b/opensfm/commands/export_openmvs.py index 7476c6fad..a12d90633 100644 --- a/opensfm/commands/export_openmvs.py +++ b/opensfm/commands/export_openmvs.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import export_openmvs +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/export_ply.py b/opensfm/commands/export_ply.py index 7e4892968..2817065b2 100644 --- a/opensfm/commands/export_ply.py +++ b/opensfm/commands/export_ply.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import export_ply +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): @@ -10,7 +12,13 @@ class Command(command.CommandBase): help = "Export reconstruction to PLY format" def run_impl(self, dataset: DataSet, args: argparse.Namespace) -> None: - export_ply.run_dataset(dataset, args.no_cameras, args.no_points, args.depthmaps, args.point_num_views) + export_ply.run_dataset( + dataset, + args.no_cameras, + args.no_points, + args.depthmaps, + args.point_num_views, + ) def add_arguments_impl(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( @@ -32,5 +40,5 @@ def add_arguments_impl(self, parser: argparse.ArgumentParser) -> None: "--point-num-views", action="store_true", default=False, - help="Export the number of observations associated with each point" + help="Export the number of observations associated with each point", ) diff --git a/opensfm/commands/export_pmvs.py b/opensfm/commands/export_pmvs.py index 9a39d5ccd..a0d22bdca 100644 --- a/opensfm/commands/export_pmvs.py +++ b/opensfm/commands/export_pmvs.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import export_pmvs +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/export_report.py b/opensfm/commands/export_report.py index ab6722ee2..2ad3b08f2 100644 --- a/opensfm/commands/export_report.py +++ b/opensfm/commands/export_report.py @@ -1,7 +1,10 @@ -from . import command +# pyre-strict import argparse -from opensfm.dataset import DataSet + from opensfm.actions import export_report +from opensfm.dataset import DataSet + +from . import command class Command(command.CommandBase): diff --git a/opensfm/commands/export_rerun.py b/opensfm/commands/export_rerun.py new file mode 100644 index 000000000..93fc9f493 --- /dev/null +++ b/opensfm/commands/export_rerun.py @@ -0,0 +1,39 @@ +# pyre-strict +import argparse + +from opensfm.actions import export_rerun +from opensfm.dataset import DataSet + +from . import command + + +class Command(command.CommandBase): + name = "export_rerun" + help = "Export reconstruction to Rerun format for 3D visualization" + + def run_impl(self, dataset: DataSet, args: argparse.Namespace) -> None: + export_rerun.run_dataset( + dataset, + output=args.output, + reconstruction_index=args.reconstruction_index, + proj=args.proj, + ) + + def add_arguments_impl(self, parser: argparse.ArgumentParser) -> None: + parser.add_argument( + "--output", + "-o", + type=str, + help="Output .rrd file path (default: dataset_path/rerun.rrd)", + ) + parser.add_argument( + "--reconstruction-index", + type=int, + default=0, + help="Index of reconstruction to export (default: 0)", + ) + parser.add_argument( + "--proj", + action="store_true", + help="Use coordinate system from gcp_list.txt", + ) \ No newline at end of file diff --git a/opensfm/commands/export_visualsfm.py b/opensfm/commands/export_visualsfm.py index c0b65eead..1a6fbf9d0 100644 --- a/opensfm/commands/export_visualsfm.py +++ b/opensfm/commands/export_visualsfm.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import export_visualsfm +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/extend_reconstruction.py b/opensfm/commands/extend_reconstruction.py index 2804c5382..9ec01c604 100644 --- a/opensfm/commands/extend_reconstruction.py +++ b/opensfm/commands/extend_reconstruction.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import extend_reconstruction +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/extract_metadata.py b/opensfm/commands/extract_metadata.py index c51879103..f41dfba13 100644 --- a/opensfm/commands/extract_metadata.py +++ b/opensfm/commands/extract_metadata.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import extract_metadata +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/match_features.py b/opensfm/commands/match_features.py index 5739c1801..c8f08fc4a 100644 --- a/opensfm/commands/match_features.py +++ b/opensfm/commands/match_features.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import match_features +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/mesh.py b/opensfm/commands/mesh.py index c762d97c3..5451430a8 100644 --- a/opensfm/commands/mesh.py +++ b/opensfm/commands/mesh.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import mesh +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/reconstruct.py b/opensfm/commands/reconstruct.py index fd0395238..8714b6a04 100644 --- a/opensfm/commands/reconstruct.py +++ b/opensfm/commands/reconstruct.py @@ -1,9 +1,11 @@ +# pyre-strict +import argparse + from opensfm import reconstruction from opensfm.actions import reconstruct +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/reconstruct_from_prior.py b/opensfm/commands/reconstruct_from_prior.py index f4271e98a..0ffc91794 100644 --- a/opensfm/commands/reconstruct_from_prior.py +++ b/opensfm/commands/reconstruct_from_prior.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import reconstruct_from_prior +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): diff --git a/opensfm/commands/undistort.py b/opensfm/commands/undistort.py index d9f99eb62..51125380e 100644 --- a/opensfm/commands/undistort.py +++ b/opensfm/commands/undistort.py @@ -1,8 +1,10 @@ +# pyre-strict +import argparse + from opensfm.actions import undistort +from opensfm.dataset import DataSet from . import command -import argparse -from opensfm.dataset import DataSet class Command(command.CommandBase): @@ -16,14 +18,12 @@ def run_impl(self, dataset: DataSet, args: argparse.Namespace) -> None: args.reconstruction_index, args.tracks, args.output, - args.skip_images + args.skip_images, ) def add_arguments_impl(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( - "--reconstruction", - help="reconstruction to undistort", - type=str + "--reconstruction", help="reconstruction to undistort", type=str ) parser.add_argument( "--reconstruction-index", @@ -36,10 +36,7 @@ def add_arguments_impl(self, parser: argparse.ArgumentParser) -> None: help="tracks graph of the reconstruction", ) parser.add_argument( - "--output", - help="output folder", - default="undistorted", - type=str + "--output", help="output folder", default="undistorted", type=str ) parser.add_argument( "--skip-images", diff --git a/opensfm/config.py b/opensfm/config.py index 07921a3ce..19f21d760 100644 --- a/opensfm/config.py +++ b/opensfm/config.py @@ -1,5 +1,9 @@ +# pyre-strict + +from __future__ import annotations + import os -from dataclasses import dataclass, asdict +from dataclasses import asdict, dataclass from typing import Any, Dict, IO, Union import yaml @@ -33,6 +37,10 @@ class OpenSfMConfig: feature_use_adaptive_suppression: bool = False # Bake segmentation info (class and instance) in the feature data. Thus it is done once for all at extraction time. features_bake_segmentation: bool = False + # Maximum amount of memory to use for feature extraction (in MB). See default in features_processing.py. + mem_ceiling: int | None = None + # Ratio of the memory ceiling to use for feature extraction. See default in features_processing.py. + mem_ratio: float | None = None ################################## # Params for SIFT @@ -41,6 +49,12 @@ class OpenSfMConfig: sift_peak_threshold: float = 0.2 # See OpenCV doc sift_edge_threshold: int = 10 + # See OpenCV doc + sift_nfeatures: int = 0 + # See OpenCV doc + sift_octave_layers: int = 3 + # See OpenCV doc + sift_sigma: float = 1.6 ################################## # Params for SURF @@ -196,6 +210,8 @@ class OpenSfMConfig: triangulation_threshold: float = 0.006 # Minimum angle between views to accept a triangulated point triangulation_min_ray_angle: float = 1.0 + # Minimum depth to accept a triangulated point + triangulation_min_depth: float = 0.001 # Triangulation type : either considering all rays (FULL), or sing a RANSAC variant (ROBUST) triangulation_type: str = "FULL" # Number of LM iterations to run when refining a point @@ -210,6 +226,14 @@ class OpenSfMConfig: ################################## # Minimum number of features/images per track min_track_length: int = 2 + # Whether to use depth prior during BA + use_depth_prior: bool = False + # Depth prior default std deviation + depth_std_deviation_m_default: float = 1.0 + # Whether depth is radial (distance to camera center) or Z value + depth_is_radial: bool = False + # Whether depth is stored as inverted depth + depth_is_inverted: bool = False ################################## # Params for bundle adjustment @@ -222,6 +246,8 @@ class OpenSfMConfig: reprojection_error_sd: float = 0.004 # The standard deviation of the exif focal length in log-scale exif_focal_sd: float = 0.01 + # The standard deviation of aspect ratio, i.e. fu/fv, in log-scale + aspect_ratio_sd: float = 0.01 # The standard deviation of the principal point coordinates principal_point_sd: float = 0.01 # The standard deviation of the first radial distortion parameter @@ -241,7 +267,7 @@ class OpenSfMConfig: # The default vertical standard deviation of the GCPs (in meters) gcp_vertical_sd: float = 0.1 # Global weight for GCPs, expressed a ratio of the sum of (# projections) + (# shots) + (# relative motions) - gcp_global_weight: float = 0.01 + gcp_global_weight: float = 0.04 # The standard deviation of the rig translation rig_translation_sd: float = 0.1 # The standard deviation of the rig rotation @@ -257,6 +283,8 @@ class OpenSfMConfig: # Maximum optimizer iterations. bundle_max_iterations: int = 100 + # Ratio of (resection candidates / total tracks) of a given image so that it is culled at resection and resected later + resect_redundancy_threshold: float = 0.7 # Retriangulate all points from time to time retriangulation: bool = True # Retriangulate when the number of points grows by this ratio @@ -273,15 +301,24 @@ class OpenSfMConfig: local_bundle_min_common_points: int = 20 # Max number of shots to optimize during local bundle adjustment local_bundle_max_shots: int = 30 + # Number of grid division for seleccting tracks in local bundle adjustment + local_bundle_grid: int = 12 + # Number of grid division for selecting tracks in final bundle adjustment + final_bundle_grid: int = 32 + # For debugging purpose of large datasets: limit the maximum number of shots in incremental reconstruction + incremental_max_shots_count: int = 0 + + # Remove uncertain and isolated points from the final point cloud + filter_final_point_cloud: bool = False # Save reconstructions at every iteration save_partial_reconstructions: bool = False ################################## - # Params for GPS alignment + # Params for GPS/GCP alignment ################################## # Use or ignore EXIF altitude tag - use_altitude_tag: bool = False + use_altitude_tag: bool = True # orientation_prior or naive align_method: str = "auto" # horizontal, vertical or no_roll @@ -292,6 +329,8 @@ class OpenSfMConfig: bundle_use_gcp: bool = True # Compensate GPS with a per-camera similarity transform bundle_compensate_gps_bias: bool = False + # Thrershold for the reprojection error of GCPs to be considered outliers + gcp_reprojection_error_threshold: float = 0.05 ################################## # Params for rigs @@ -374,7 +413,7 @@ def default_config() -> Dict[str, Any]: return asdict(OpenSfMConfig()) -def load_config(filepath) -> Dict[str, Any]: +def load_config(filepath: str) -> Dict[str, Any]: """DEPRECATED: = Load config from a config.yaml filepath""" if not os.path.isfile(filepath): return default_config() @@ -384,7 +423,7 @@ def load_config(filepath) -> Dict[str, Any]: def load_config_from_fileobject( - f: Union[IO[bytes], IO[str], bytes, str] + f: Union[IO[bytes], IO[str], bytes, str], ) -> Dict[str, Any]: """Load config from a config.yaml fileobject""" config = default_config() diff --git a/opensfm/context.py b/opensfm/context.py index b15564c57..d61f33d91 100644 --- a/opensfm/context.py +++ b/opensfm/context.py @@ -1,3 +1,4 @@ +# pyre-strict import logging import os @@ -7,18 +8,16 @@ pass # Windows import ctypes import sys -from typing import Optional +from typing import Callable, List, Optional, TypeVar import cv2 -from joblib import Parallel, delayed, parallel_backend - +from joblib import delayed, Parallel, parallel_backend logger: logging.Logger = logging.getLogger(__name__) abspath = os.path.dirname(os.path.realpath(__file__)) SENSOR_DATA = os.path.join(abspath, "data", "sensor_data.json") -SENSOR_DATA_DB = os.path.join(abspath, "data", "sensor_data.sqlite") CAMERA_CALIBRATION = os.path.join(abspath, "data", "camera_calibration.yaml") BOW_PATH = os.path.join(abspath, "data", "bow") @@ -27,17 +26,18 @@ OPENCV5: bool = int(cv2.__version__.split(".")[0]) >= 5 OPENCV4: bool = int(cv2.__version__.split(".")[0]) >= 4 OPENCV44: bool = ( - int(cv2.__version__.split(".")[0]) == 4 and int(cv2.__version__.split(".")[1]) >= 4 + int(cv2.__version__.split(".")[0]) == 4 and int( + cv2.__version__.split(".")[1]) >= 4 ) OPENCV3: bool = int(cv2.__version__.split(".")[0]) >= 3 +flann_Index: Optional[cv2.flann_Index] = None if hasattr(cv2, "flann_Index"): flann_Index = cv2.flann_Index elif hasattr(cv2, "flann") and hasattr(cv2.flann, "Index"): flann_Index = cv2.flann.Index else: logger.warning("Unable to find flann Index") - flann_Index = None # Parallel processes @@ -56,7 +56,8 @@ def parallel_map(func, args, num_proc: int, max_batch_size: int = 1, backend="th batch_size = ( min(batch_size, max_batch_size) if max_batch_size else batch_size ) - res = Parallel(batch_size=batch_size)(delayed(func)(arg) for arg in args) + res = Parallel(batch_size=batch_size)( + delayed(func)(arg) for arg in args) cv2.setNumThreads(threads_used) return res @@ -121,6 +122,14 @@ def current_memory_usage() -> int: return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss * rusage_unit +def log_memory(stage: str) -> int: + """Log memory usage at a given stage and return usage in KB.""" + mem_kb = current_memory_usage() + mem_gb = mem_kb / 1024 / 1024 / 1024 + logger.info(f"[Memory] {stage}: {mem_gb:.1f} GB") + return mem_kb + + def processes_that_fit_in_memory(desired: int, per_process: int) -> int: """Amount of parallel BoW process that fit in memory.""" available_mem = memory_available() diff --git a/opensfm/data/camera_calibration.yaml b/opensfm/data/camera_calibration.yaml index 2cf333d99..934394deb 100644 --- a/opensfm/data/camera_calibration.yaml +++ b/opensfm/data/camera_calibration.yaml @@ -67,6 +67,18 @@ "focal": 0.5930666527479901, "k1": -0.012137318698010527, "k2": 0.016199087342953698 + }, + "zenmusep1": { + "projection_type": "brown", + "focal_x": 0.993855582350662, + "focal_y": 0.993855582350662, + "c_x": -0.000890260610982073, + "c_y": 0.0017658361729384116, + "k1": -0.04734574598470396, + "k2": 0.00875629525446153, + "p1": 0.002032062604630639, + "p2": -0.0007921675594001833, + "k3": -0.06874708730325006 } } }, diff --git a/opensfm/data/sensor_data.json b/opensfm/data/sensor_data.json index 63281b3f7..5f58a5b30 100644 --- a/opensfm/data/sensor_data.json +++ b/opensfm/data/sensor_data.json @@ -1144,6 +1144,7 @@ "GE X500": 6.16, "GE X550": 6.16, "GE X600": 6.16, + "Hasselblad L2D-20c": 17.5, "HP CA350": 6.16, "HP CB350": 6.16, "HP CW450": 6.16, @@ -3706,5 +3707,5 @@ "GITUP GIT2": 6.216, "DJI ZH20T": 7.68, "DJI XT2": 10.88, - "DJI ZENMUSEP1": 35.9 -} + "DJI ZENMUSEP1": 35.9 +} \ No newline at end of file diff --git a/opensfm/data/sensor_data_detailed.json b/opensfm/data/sensor_data_detailed.json index a41fe5e18..8a4320530 100644 --- a/opensfm/data/sensor_data_detailed.json +++ b/opensfm/data/sensor_data_detailed.json @@ -1,2391 +1,2391 @@ { "Acer CE-5330": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Acer CE-5430": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Acer CE-6430": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2909.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2909.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2187.0 - }, + }, "Acer CI-6330": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2862.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2862.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2152.0 - }, + }, "Acer CI-6530": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2871.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2871.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2159.0 - }, + }, "Acer CI-8330": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3310.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3310.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2489.0 - }, + }, "Acer CL-5300": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Acer CP-8531": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3272.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3272.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2460.0 - }, + }, "Acer CP-8660": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3302.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3302.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Acer CR-5130": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Acer CR-6530": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2894.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2894.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2176.0 - }, + }, "Acer CR-8530": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3272.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3272.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2460.0 - }, + }, "Acer CS-5530": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Acer CS-5531": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Acer CS-6530": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2862.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2862.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2152.0 - }, + }, "Acer CS-6531": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2862.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2862.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2152.0 - }, + }, "Acer CU-6530": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2862.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2862.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2152.0 - }, + }, "AeroVironment Quantix": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4864.0, - "Sensor": "1/2.3\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4864.0, + "Sensor": "1/2.3\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3648.0 - }, + }, "AgfaPhoto Compact 100": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "AgfaPhoto Compact 102": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "AgfaPhoto Compact 103": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "AgfaPhoto DC-1030i": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "AgfaPhoto DC-1033m": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "AgfaPhoto DC-1033x": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "AgfaPhoto DC-1338i": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "AgfaPhoto DC-1338sT": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "AgfaPhoto DC-2030m": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "AgfaPhoto DC-302": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1998.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1998.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 1502.0 - }, + }, "AgfaPhoto DC-500": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 2579.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 2579.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 1939.0 - }, + }, "AgfaPhoto DC-530i": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "AgfaPhoto DC-533": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "AgfaPhoto DC-600uw": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2124.0 - }, + }, "AgfaPhoto DC-630": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "AgfaPhoto DC-630i": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "AgfaPhoto DC-630x": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "AgfaPhoto DC-633x": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "AgfaPhoto DC-633xs": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "AgfaPhoto DC-730i": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "AgfaPhoto DC-733i": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "AgfaPhoto DC-733s": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "AgfaPhoto DC-735": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "AgfaPhoto DC-735i": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "AgfaPhoto DC-738i": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "AgfaPhoto DC-830": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "AgfaPhoto DC-830i": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "AgfaPhoto DC-8330i": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "AgfaPhoto DC-8338i": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "AgfaPhoto DC-833m": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "AgfaPhoto DC-8428s": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "AgfaPhoto Optima 1": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "AgfaPhoto Optima 100": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "AgfaPhoto Optima 102": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "AgfaPhoto Optima 103": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "AgfaPhoto Optima 104": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "AgfaPhoto Optima 105": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "AgfaPhoto Optima 1338mT": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "AgfaPhoto Optima 1438m": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "AgfaPhoto Optima 3": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "AgfaPhoto Optima 830UW": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "AgfaPhoto Optima 8328m": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "AgfaPhoto ePhoto 1280": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 964.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 964.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 725.0 - }, + }, "AgfaPhoto ePhoto 1680": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 950.0 - }, + }, "AgfaPhoto ePhoto CL18": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 632.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 632.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 475.0 - }, + }, "AgfaPhoto ePhoto CL30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 1095.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 1095.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 823.0 - }, + }, "AgfaPhoto ePhoto CL30 Clik!": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 1095.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 1095.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 823.0 - }, + }, "AgfaPhoto ePhoto CL45": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 1195.0 - }, + }, "AgfaPhoto ePhoto CL50": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 950.0 - }, + }, "AgfaPhoto sensor 505-D": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "AgfaPhoto sensor 505-X": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "AgfaPhoto sensor 530s": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "AgfaPhoto sensor 830s": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "BenQ AC100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ AE100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ C1420": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ DC 2300": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1604.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1604.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1197.0 - }, + }, "BenQ DC 2410": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2044.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2044.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1537.0 - }, + }, "BenQ DC 3400": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1637.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1637.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1222.0 - }, + }, "BenQ DC 3410": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1678.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1678.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1252.0 - }, + }, "BenQ DC 4330": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1575.0 - }, + }, "BenQ DC 4500": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "BenQ DC 5330": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2044.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2044.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1537.0 - }, + }, "BenQ DC C1000": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "BenQ DC C1020": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "BenQ DC C1030 Eco": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "BenQ DC C1035": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "BenQ DC C1050": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3661.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3661.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2732.0 - }, + }, "BenQ DC C1060": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "BenQ DC C1220": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "BenQ DC C1230": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "BenQ DC C1250": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "BenQ DC C1255": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "BenQ DC C1430": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ DC C1450": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ DC C1460": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ DC C1480": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ DC C30": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1585.0 - }, + }, "BenQ DC C35": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2067.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2067.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1554.0 - }, + }, "BenQ DC C40": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2374.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2374.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1785.0 - }, + }, "BenQ DC C420": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "BenQ DC C50": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "BenQ DC C500": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "BenQ DC C51": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "BenQ DC C510": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "BenQ DC C520": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "BenQ DC C530": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "BenQ DC C540": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "BenQ DC C60": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2909.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2909.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2187.0 - }, + }, "BenQ DC C610": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "BenQ DC C62": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2910.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2910.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2188.0 - }, + }, "BenQ DC C630": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "BenQ DC C640": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2835.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2835.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2116.0 - }, + }, "BenQ DC C740": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "BenQ DC C740i": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "BenQ DC C750": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "BenQ DC C800": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "BenQ DC C840": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "BenQ DC C850": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "BenQ DC E1000": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "BenQ DC E1020": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "BenQ DC E1030": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "BenQ DC E1035": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "BenQ DC E1050": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3661.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3661.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2732.0 - }, + }, "BenQ DC E1050t": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "BenQ DC E1220": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "BenQ DC E1230": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "BenQ DC E1240": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "BenQ DC E1250": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "BenQ DC E1260": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "BenQ DC E1280": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "BenQ DC E1420": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ DC E1430": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ DC E1460": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ DC E1465": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ DC E30": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2044.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2044.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1537.0 - }, + }, "BenQ DC E300": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2044.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2044.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1537.0 - }, + }, "BenQ DC E310": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1551.0 - }, + }, "BenQ DC E40": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1783.0 - }, + }, "BenQ DC E41": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1783.0 - }, + }, "BenQ DC E43": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2363.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2363.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1777.0 - }, + }, "BenQ DC E510": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "BenQ DC E520": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "BenQ DC E520 plus": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2558.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2558.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1923.0 - }, + }, "BenQ DC E53": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2629.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2629.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1977.0 - }, + }, "BenQ DC E600": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "BenQ DC E605": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "BenQ DC E610": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "BenQ DC E63 Plus": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "BenQ DC E720": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "BenQ DC E800": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "BenQ DC E820": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "BenQ DC L1020": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "BenQ DC P1410": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ DC P500": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "BenQ DC P860": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "BenQ DC S1430": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ DC S30": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "BenQ DC S40": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2374.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2374.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1785.0 - }, + }, "BenQ DC T1260": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "BenQ DC T700": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "BenQ DC T800": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2453.0 - }, + }, "BenQ DC T850": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "BenQ DC W1220": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "BenQ DC X600": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2909.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2909.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2187.0 - }, + }, "BenQ DC X710": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "BenQ DC X720": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "BenQ DC X725": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "BenQ DC X735": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "BenQ DC X800": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "BenQ DC X835": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "BenQ E1480": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ G1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ GH200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "BenQ GH600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "BenQ GH700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "BenQ LM100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ S1410": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ S1420": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "BenQ T1460": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Canon Digital IXUS": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Canon Digital IXUS 100 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon Digital IXUS 110 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon Digital IXUS 200 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon Digital IXUS 300": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Canon Digital IXUS 330": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Canon Digital IXUS 40": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Canon Digital IXUS 400": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Canon Digital IXUS 430": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Canon Digital IXUS 50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon Digital IXUS 500": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon Digital IXUS 60": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Canon Digital IXUS 65": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Canon Digital IXUS 80 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Canon Digital IXUS 800 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Canon Digital IXUS 85 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon Digital IXUS 850 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon Digital IXUS 860 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Canon Digital IXUS 870 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon Digital IXUS 90 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon Digital IXUS 900 Ti": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon Digital IXUS 95 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon Digital IXUS 950 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Canon Digital IXUS 960 IS": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Canon Digital IXUS 970 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon Digital IXUS 980 IS": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4438.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4438.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3312.0 - }, + }, "Canon Digital IXUS 990 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon Digital IXUS II": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Canon Digital IXUS IIs": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Canon Digital IXUS V": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Canon Digital IXUS i": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Canon Digital IXUS i Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon Digital IXUS i7": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon Digital IXUS v2": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Canon Digital IXUS v3": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Canon ELPH 135 / IXUS 145": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon ELPH 140 IS / IXUS 150": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon ELPH 150 IS / IXUS 155": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5158.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5158.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3878.0 - }, + }, "Canon EOS 1000D": { - "Sensor height (mm)": 14.8, - "Sensor width (mm)": 22.2, - "Sensor res. (width)": 3893.0, - "Sensor": "22.2 x 14.8 mm", + "Sensor height (mm)": 14.8, + "Sensor width (mm)": 22.2, + "Sensor res. (width)": 3893.0, + "Sensor": "22.2 x 14.8 mm", "Sensor res. (height)": 2595.0 - }, + }, "Canon EOS 10D": { - "Sensor height (mm)": 15.1, - "Sensor width (mm)": 22.7, - "Sensor res. (width)": 3074.0, - "Sensor": "22.7 x 15.1 mm", + "Sensor height (mm)": 15.1, + "Sensor width (mm)": 22.7, + "Sensor res. (width)": 3074.0, + "Sensor": "22.7 x 15.1 mm", "Sensor res. (height)": 2049.0 - }, + }, "Canon EOS 20D": { - "Sensor height (mm)": 15.0, - "Sensor width (mm)": 22.5, - "Sensor res. (width)": 3507.0, - "Sensor": "22.5 x 15 mm", + "Sensor height (mm)": 15.0, + "Sensor width (mm)": 22.5, + "Sensor res. (width)": 3507.0, + "Sensor": "22.5 x 15 mm", "Sensor res. (height)": 2338.0 - }, + }, "Canon EOS 20Da": { - "Sensor height (mm)": 15.0, - "Sensor width (mm)": 22.5, - "Sensor res. (width)": 3507.0, - "Sensor": "22.5 x 15 mm", + "Sensor height (mm)": 15.0, + "Sensor width (mm)": 22.5, + "Sensor res. (width)": 3507.0, + "Sensor": "22.5 x 15 mm", "Sensor res. (height)": 2338.0 - }, + }, "Canon EOS 300D": { - "Sensor height (mm)": 15.1, - "Sensor width (mm)": 22.7, - "Sensor res. (width)": 3074.0, - "Sensor": "22.7 x 15.1 mm", + "Sensor height (mm)": 15.1, + "Sensor width (mm)": 22.7, + "Sensor res. (width)": 3074.0, + "Sensor": "22.7 x 15.1 mm", "Sensor res. (height)": 2049.0 - }, + }, "Canon EOS 30D": { - "Sensor height (mm)": 15.0, - "Sensor width (mm)": 22.5, - "Sensor res. (width)": 3506.0, - "Sensor": "22.5 x 15 mm", + "Sensor height (mm)": 15.0, + "Sensor width (mm)": 22.5, + "Sensor res. (width)": 3506.0, + "Sensor": "22.5 x 15 mm", "Sensor res. (height)": 2337.0 - }, + }, "Canon EOS 350D": { - "Sensor height (mm)": 14.8, - "Sensor width (mm)": 22.2, - "Sensor res. (width)": 3464.0, - "Sensor": "22.2 x 14.8 mm", + "Sensor height (mm)": 14.8, + "Sensor width (mm)": 22.2, + "Sensor res. (width)": 3464.0, + "Sensor": "22.2 x 14.8 mm", "Sensor res. (height)": 2309.0 - }, + }, "Canon EOS 400D": { - "Sensor height (mm)": 14.8, - "Sensor width (mm)": 22.2, - "Sensor res. (width)": 3893.0, - "Sensor": "22.2 x 14.8 mm", + "Sensor height (mm)": 14.8, + "Sensor width (mm)": 22.2, + "Sensor res. (width)": 3893.0, + "Sensor": "22.2 x 14.8 mm", "Sensor res. (height)": 2595.0 - }, + }, "Canon EOS 40D": { - "Sensor height (mm)": 14.8, - "Sensor width (mm)": 22.2, - "Sensor res. (width)": 3893.0, - "Sensor": "22.2 x 14.8 mm", + "Sensor height (mm)": 14.8, + "Sensor width (mm)": 22.2, + "Sensor res. (width)": 3893.0, + "Sensor": "22.2 x 14.8 mm", "Sensor res. (height)": 2595.0 - }, + }, "Canon EOS 450D": { - "Sensor height (mm)": 14.8, - "Sensor width (mm)": 22.2, - "Sensor res. (width)": 4278.0, - "Sensor": "22.2 x 14.8 mm", + "Sensor height (mm)": 14.8, + "Sensor width (mm)": 22.2, + "Sensor res. (width)": 4278.0, + "Sensor": "22.2 x 14.8 mm", "Sensor res. (height)": 2852.0 - }, + }, "Canon EOS 500D": { - "Sensor height (mm)": 14.9, - "Sensor width (mm)": 22.3, - "Sensor res. (width)": 4760.0, - "Sensor": "22.3 x 14.9 mm", + "Sensor height (mm)": 14.9, + "Sensor width (mm)": 22.3, + "Sensor res. (width)": 4760.0, + "Sensor": "22.3 x 14.9 mm", "Sensor res. (height)": 3173.0 - }, + }, "Canon EOS 50D": { - "Sensor height (mm)": 14.9, - "Sensor width (mm)": 22.3, - "Sensor res. (width)": 4760.0, - "Sensor": "22.3 x 14.9 mm", + "Sensor height (mm)": 14.9, + "Sensor width (mm)": 22.3, + "Sensor res. (width)": 4760.0, + "Sensor": "22.3 x 14.9 mm", "Sensor res. (height)": 3173.0 - }, + }, "Canon EOS 5D": { - "Sensor height (mm)": 23.8, - "Sensor width (mm)": 35.8, - "Sensor res. (width)": 4365.0, - "Sensor": "35.8 x 23.8 mm", + "Sensor height (mm)": 23.8, + "Sensor width (mm)": 35.8, + "Sensor res. (width)": 4365.0, + "Sensor": "35.8 x 23.8 mm", "Sensor res. (height)": 2910.0 - }, + }, "Canon EOS 5D Mark II": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 5613.0, - "Sensor": "36 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 5613.0, + "Sensor": "36 x 24 mm", "Sensor res. (height)": 3742.0 - }, + }, "Canon EOS 5D Mark III": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 5784.0, - "Sensor": "36 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 5784.0, + "Sensor": "36 x 24 mm", "Sensor res. (height)": 3856.0 - }, + }, "Canon EOS 60D": { - "Sensor height (mm)": 14.9, - "Sensor width (mm)": 22.3, - "Sensor res. (width)": 5196.0, - "Sensor": "22.3 x 14.9 mm", + "Sensor height (mm)": 14.9, + "Sensor width (mm)": 22.3, + "Sensor res. (width)": 5196.0, + "Sensor": "22.3 x 14.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Canon EOS 60Da": { - "Sensor height (mm)": 14.9, - "Sensor width (mm)": 22.3, - "Sensor res. (width)": 5196.0, - "Sensor": "22.3 x 14.9 mm", + "Sensor height (mm)": 14.9, + "Sensor width (mm)": 22.3, + "Sensor res. (width)": 5196.0, + "Sensor": "22.3 x 14.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Canon EOS 6D": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 35.8, - "Sensor res. (width)": 5505.0, - "Sensor": "35.8 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 35.8, + "Sensor res. (width)": 5505.0, + "Sensor": "35.8 x 23.9 mm", "Sensor res. (height)": 3670.0 - }, + }, "Canon EOS 70D": { - "Sensor height (mm)": 15.0, - "Sensor width (mm)": 22.5, - "Sensor res. (width)": 5505.0, - "Sensor": "22.5 x 15 mm", + "Sensor height (mm)": 15.0, + "Sensor width (mm)": 22.5, + "Sensor res. (width)": 5505.0, + "Sensor": "22.5 x 15 mm", "Sensor res. (height)": 3670.0 - }, + }, "Canon EOS 7D": { - "Sensor height (mm)": 14.9, - "Sensor width (mm)": 22.3, - "Sensor res. (width)": 5196.0, - "Sensor": "22.3 x 14.9 mm", + "Sensor height (mm)": 14.9, + "Sensor width (mm)": 22.3, + "Sensor res. (width)": 5196.0, + "Sensor": "22.3 x 14.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Canon EOS 7D Mark II": { - "Sensor height (mm)": 15.0, - "Sensor width (mm)": 22.4, - "Sensor res. (width)": 5486.0, - "Sensor": "22.4 x 15 mm", + "Sensor height (mm)": 15.0, + "Sensor width (mm)": 22.4, + "Sensor res. (width)": 5486.0, + "Sensor": "22.4 x 15 mm", "Sensor res. (height)": 3682.0 - }, + }, "Canon EOS D30": { - "Sensor height (mm)": 15.1, - "Sensor width (mm)": 22.7, - "Sensor res. (width)": 2157.0, - "Sensor": "22.7 x 15.1 mm", + "Sensor height (mm)": 15.1, + "Sensor width (mm)": 22.7, + "Sensor res. (width)": 2157.0, + "Sensor": "22.7 x 15.1 mm", "Sensor res. (height)": 1438.0 - }, + }, "Canon EOS D60": { - "Sensor height (mm)": 15.1, - "Sensor width (mm)": 22.7, - "Sensor res. (width)": 3074.0, - "Sensor": "22.7 x 15.1 mm", + "Sensor height (mm)": 15.1, + "Sensor width (mm)": 22.7, + "Sensor res. (width)": 3074.0, + "Sensor": "22.7 x 15.1 mm", "Sensor res. (height)": 2049.0 - }, + }, "Canon EOS M": { - "Sensor height (mm)": 14.9, - "Sensor width (mm)": 22.3, - "Sensor res. (width)": 5196.0, - "Sensor": "22.3 x 14.9 mm", + "Sensor height (mm)": 14.9, + "Sensor width (mm)": 22.3, + "Sensor res. (width)": 5196.0, + "Sensor": "22.3 x 14.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Canon EOS Rebel SL1 / 100D": { - "Sensor height (mm)": 14.9, - "Sensor width (mm)": 22.3, - "Sensor res. (width)": 5196.0, - "Sensor": "22.3 x 14.9 mm", + "Sensor height (mm)": 14.9, + "Sensor width (mm)": 22.3, + "Sensor res. (width)": 5196.0, + "Sensor": "22.3 x 14.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Canon EOS Rebel T2i / 550D": { - "Sensor height (mm)": 14.9, - "Sensor width (mm)": 22.3, - "Sensor res. (width)": 5196.0, - "Sensor": "22.3 x 14.9 mm", + "Sensor height (mm)": 14.9, + "Sensor width (mm)": 22.3, + "Sensor res. (width)": 5196.0, + "Sensor": "22.3 x 14.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Canon EOS Rebel T3 / 1100D": { - "Sensor height (mm)": 14.8, - "Sensor width (mm)": 22.2, - "Sensor res. (width)": 4278.0, - "Sensor": "22.2 x 14.8 mm", + "Sensor height (mm)": 14.8, + "Sensor width (mm)": 22.2, + "Sensor res. (width)": 4278.0, + "Sensor": "22.2 x 14.8 mm", "Sensor res. (height)": 2852.0 - }, + }, "Canon EOS Rebel T3i / 600D": { - "Sensor height (mm)": 14.9, - "Sensor width (mm)": 22.3, - "Sensor res. (width)": 5196.0, - "Sensor": "22.3 x 14.9 mm", + "Sensor height (mm)": 14.9, + "Sensor width (mm)": 22.3, + "Sensor res. (width)": 5196.0, + "Sensor": "22.3 x 14.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Canon EOS Rebel T4i / 650D": { - "Sensor height (mm)": 14.9, - "Sensor width (mm)": 22.3, - "Sensor res. (width)": 5196.0, - "Sensor": "22.3 x 14.9 mm", + "Sensor height (mm)": 14.9, + "Sensor width (mm)": 22.3, + "Sensor res. (width)": 5196.0, + "Sensor": "22.3 x 14.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Canon EOS Rebel T5 / 1200D": { - "Sensor height (mm)": 14.9, - "Sensor width (mm)": 22.3, - "Sensor res. (width)": 5196.0, - "Sensor": "22.3 x 14.9 mm", + "Sensor height (mm)": 14.9, + "Sensor width (mm)": 22.3, + "Sensor res. (width)": 5196.0, + "Sensor": "22.3 x 14.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Canon EOS Rebel T5i / 700D": { - "Sensor height (mm)": 14.9, - "Sensor width (mm)": 22.3, - "Sensor res. (width)": 5196.0, - "Sensor": "22.3 x 14.9 mm", + "Sensor height (mm)": 14.9, + "Sensor width (mm)": 22.3, + "Sensor res. (width)": 5196.0, + "Sensor": "22.3 x 14.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Canon EOS-1D": { - "Sensor height (mm)": 19.1, - "Sensor width (mm)": 28.7, - "Sensor res. (width)": 2480.0, - "Sensor": "28.7 x 19.1 mm", + "Sensor height (mm)": 19.1, + "Sensor width (mm)": 28.7, + "Sensor res. (width)": 2480.0, + "Sensor": "28.7 x 19.1 mm", "Sensor res. (height)": 1653.0 - }, + }, "Canon EOS-1D C": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 5211.0, - "Sensor": "36 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 5211.0, + "Sensor": "36 x 24 mm", "Sensor res. (height)": 3474.0 - }, + }, "Canon EOS-1D Mark II": { - "Sensor height (mm)": 19.1, - "Sensor width (mm)": 28.7, - "Sensor res. (width)": 3507.0, - "Sensor": "28.7 x 19.1 mm", + "Sensor height (mm)": 19.1, + "Sensor width (mm)": 28.7, + "Sensor res. (width)": 3507.0, + "Sensor": "28.7 x 19.1 mm", "Sensor res. (height)": 2338.0 - }, + }, "Canon EOS-1D Mark II N": { - "Sensor height (mm)": 19.1, - "Sensor width (mm)": 28.7, - "Sensor res. (width)": 3507.0, - "Sensor": "28.7 x 19.1 mm", + "Sensor height (mm)": 19.1, + "Sensor width (mm)": 28.7, + "Sensor res. (width)": 3507.0, + "Sensor": "28.7 x 19.1 mm", "Sensor res. (height)": 2338.0 - }, + }, "Canon EOS-1D Mark III": { - "Sensor height (mm)": 18.7, - "Sensor width (mm)": 28.7, - "Sensor res. (width)": 3931.0, - "Sensor": "28.7 x 18.7 mm", + "Sensor height (mm)": 18.7, + "Sensor width (mm)": 28.7, + "Sensor res. (width)": 3931.0, + "Sensor": "28.7 x 18.7 mm", "Sensor res. (height)": 2569.0 - }, + }, "Canon EOS-1D Mark IV": { - "Sensor height (mm)": 18.6, - "Sensor width (mm)": 27.9, - "Sensor res. (width)": 4914.0, - "Sensor": "27.9 x 18.6 mm", + "Sensor height (mm)": 18.6, + "Sensor width (mm)": 27.9, + "Sensor res. (width)": 4914.0, + "Sensor": "27.9 x 18.6 mm", "Sensor res. (height)": 3276.0 - }, + }, "Canon EOS-1D X": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 5211.0, - "Sensor": "36 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 5211.0, + "Sensor": "36 x 24 mm", "Sensor res. (height)": 3474.0 - }, + }, "Canon EOS-1Ds": { - "Sensor height (mm)": 23.8, - "Sensor width (mm)": 35.8, - "Sensor res. (width)": 4062.0, - "Sensor": "35.8 x 23.8 mm", + "Sensor height (mm)": 23.8, + "Sensor width (mm)": 35.8, + "Sensor res. (width)": 4062.0, + "Sensor": "35.8 x 23.8 mm", "Sensor res. (height)": 2708.0 - }, + }, "Canon EOS-1Ds Mark II": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 5006.0, - "Sensor": "36 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 5006.0, + "Sensor": "36 x 24 mm", "Sensor res. (height)": 3337.0 - }, + }, "Canon EOS-1Ds Mark III": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 5627.0, - "Sensor": "36 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 5627.0, + "Sensor": "36 x 24 mm", "Sensor res. (height)": 3751.0 - }, + }, "Canon IXUS 1000 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon IXUS 105": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon IXUS 1100 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon IXUS 115 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon IXUS 125 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Canon IXUS 130": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Canon IXUS 132": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon IXUS 210": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Canon IXUS 220 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon IXUS 230 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon IXUS 240 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Canon IXUS 300 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon IXUS 310 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon IXUS 500 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Canon IXUS 510 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Canon PowerShot 350": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 632.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 632.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Canon PowerShot 600": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 815.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 815.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 613.0 - }, + }, "Canon PowerShot A1000 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot A1300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot A1400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot A2000 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot A2300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot A2400 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot A2500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot A2600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot A3400 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot A3500 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot A400": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 2038.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 2038.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1521.0 - }, + }, "Canon PowerShot A4000 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot A430": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 2306.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 2306.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Canon PowerShot A450": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 2579.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 2579.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon PowerShot A460": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 2579.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 2579.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon PowerShot A470": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot A480": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot A490": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot A495": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot A5": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 964.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 964.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 725.0 - }, + }, "Canon PowerShot A5 Zoom": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 964.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 964.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 725.0 - }, + }, "Canon PowerShot A50": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1264.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1264.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 950.0 - }, + }, "Canon PowerShot A530": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon PowerShot A550": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot A560": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot A570 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot A580": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Canon PowerShot A590 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Canon PowerShot A620": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot A630": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Canon PowerShot A640": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot A650 IS": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Canon PowerShot A700": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Canon PowerShot A710 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot A720 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Canon PowerShot A800": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot A810": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot A85": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Canon PowerShot A95": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon PowerShot D10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot D20": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot D30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot E1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot ELPH 100 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot ELPH 110 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Canon PowerShot ELPH 115 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot ELPH 130 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot ELPH 300 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot ELPH 310 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot ELPH 320 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Canon PowerShot ELPH 330 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot ELPH 340 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot ELPH 500 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot ELPH 510 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot ELPH 520 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Canon PowerShot Elph 530 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Canon PowerShot G1 X": { - "Sensor height (mm)": 14.0, - "Sensor width (mm)": 18.7, - "Sensor res. (width)": 4378.0, - "Sensor": "1.5\" (~ 18.7 x 14 mm)", + "Sensor height (mm)": 14.0, + "Sensor width (mm)": 18.7, + "Sensor res. (width)": 4378.0, + "Sensor": "1.5\" (~ 18.7 x 14 mm)", "Sensor res. (height)": 3267.0 - }, + }, "Canon PowerShot G1 X Mark II": { - "Sensor height (mm)": 14.0, - "Sensor width (mm)": 18.7, - "Sensor res. (width)": 4142.0, - "Sensor": "1.5\" (~ 18.7 x 14 mm)", + "Sensor height (mm)": 14.0, + "Sensor width (mm)": 18.7, + "Sensor res. (width)": 4142.0, + "Sensor": "1.5\" (~ 18.7 x 14 mm)", "Sensor res. (height)": 3091.0 - }, + }, "Canon PowerShot G10": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4438.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4438.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3312.0 - }, + }, "Canon PowerShot G11": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3661.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3661.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2732.0 - }, + }, "Canon PowerShot G12": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3661.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3661.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2732.0 - }, + }, "Canon PowerShot G15": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Canon PowerShot G16": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Canon PowerShot G6": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot G7": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot G7 X": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 5505.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 5505.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3670.0 - }, + }, "Canon PowerShot G9": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Canon PowerShot G9 X": { "Sensor height (mm)": 8.8, "Sensor width (mm)": 13.2, @@ -2394,20276 +2394,20276 @@ "Sensor res. (height)": 3670.0 }, "Canon PowerShot N": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot N100": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Canon PowerShot N2": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Canon PowerShot Pro1": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3262.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3262.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Canon PowerShot Pro70": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1412.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1412.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1062.0 - }, + }, "Canon PowerShot Pro90 IS": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1859.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1859.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1398.0 - }, + }, "Canon PowerShot S100": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Canon PowerShot S100 Digital IXUS": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Canon PowerShot S110": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Canon PowerShot S120": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Canon PowerShot S200": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Canon PowerShot S230": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Canon PowerShot S3 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Canon PowerShot S300": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Canon PowerShot S330": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Canon PowerShot S400": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Canon PowerShot S410": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Canon PowerShot S5 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Canon PowerShot S500": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon PowerShot S60": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon PowerShot S70": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot S90": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3661.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3661.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2732.0 - }, + }, "Canon PowerShot S95": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3661.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3661.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2732.0 - }, + }, "Canon PowerShot SD10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Canon PowerShot SD100": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Canon PowerShot SD1000": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot SD110": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Canon PowerShot SD1100 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Canon PowerShot SD1200 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot SD1300 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SD1400 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Canon PowerShot SD20": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon PowerShot SD200": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Canon PowerShot SD30": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon PowerShot SD300": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Canon PowerShot SD3500 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Canon PowerShot SD40": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot SD400": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon PowerShot SD4000 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot SD430 Wireless": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon PowerShot SD450": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon PowerShot SD4500 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot SD500": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot SD550": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot SD600": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Canon PowerShot SD630": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Canon PowerShot SD700 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Canon PowerShot SD750": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot SD770 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot SD780 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SD790 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot SD800 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon PowerShot SD850 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Canon PowerShot SD870 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Canon PowerShot SD880 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot SD890 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot SD900": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot SD940 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SD950 IS": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Canon PowerShot SD960 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SD970 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SD980 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SD990 IS": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4438.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4438.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3312.0 - }, + }, "Canon PowerShot SX1 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot SX10 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot SX100 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3322.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3322.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2498.0 - }, + }, "Canon PowerShot SX110 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3459.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3459.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2601.0 - }, + }, "Canon PowerShot SX120 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon PowerShot SX130 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Canon PowerShot SX150 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Canon PowerShot SX160 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot SX170 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot SX20 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SX200 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SX210 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Canon PowerShot SX220 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SX230 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SX240 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SX260 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SX270 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SX280 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SX30 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Canon PowerShot SX40 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SX400 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot SX50 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SX500 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot SX510 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon PowerShot SX520 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot SX60 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Canon PowerShot SX600 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon PowerShot SX700 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Canon PowerShot TX1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Canon Powershot A10": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Canon Powershot A100": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1268.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1268.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 946.0 - }, + }, "Canon Powershot A1100 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon Powershot A1200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon Powershot A20": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Canon Powershot A200": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1596.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1596.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1191.0 - }, + }, "Canon Powershot A2100 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon Powershot A2200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Canon Powershot A30": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Canon Powershot A300": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Canon Powershot A3000 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Canon Powershot A310": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Canon Powershot A3100 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Canon Powershot A3200 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Canon Powershot A3300 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Canon Powershot A40": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Canon Powershot A410": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 2070.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 2070.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1545.0 - }, + }, "Canon Powershot A420": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 2306.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 2306.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Canon Powershot A510": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Canon Powershot A520": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Canon Powershot A540": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Canon Powershot A60": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Canon Powershot A610": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon Powershot A70": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Canon Powershot A75": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Canon Powershot A80": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Canon Powershot G1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Canon Powershot G2": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Canon Powershot G3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Canon Powershot G5": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon Powershot S1 IS": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Canon Powershot S10": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Canon Powershot S2 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon Powershot S20": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Canon Powershot S30": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Canon Powershot S40": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Canon Powershot S45": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Canon Powershot S50": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Canon Powershot S80": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Canon Pro90 IS": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Canon S200": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3678.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3678.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2745.0 - }, + }, "Canon SX220 HS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EX-FR10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Casio EX-N1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EX-N10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EX-N20": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EX-N5": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EX-N50": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EX-TR10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EX-TR15": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EX-ZR400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EX-ZR700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EX-ZR800": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EX-ZS30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Casio EXILIM EX-JE10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EXILIM EX-FC100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3479.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3479.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Casio EXILIM EX-FC150": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Casio EXILIM EX-FC160s": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3755.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3755.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2823.0 - }, + }, "Casio EXILIM EX-FH100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Casio EXILIM EX-FH150": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Casio EXILIM EX-FH20": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3479.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3479.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Casio EXILIM EX-FH25": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Casio EXILIM EX-FS10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3479.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3479.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Casio EXILIM EX-G1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-H10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-H15": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Casio EXILIM EX-H20G": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Casio EXILIM EX-H30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EXILIM EX-H5": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-H50": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EXILIM EX-M1": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 989.0 - }, + }, "Casio EXILIM EX-M2": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1631.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1631.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Casio EXILIM EX-M20": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Casio EXILIM EX-S1": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 989.0 - }, + }, "Casio EXILIM EX-S10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Casio EXILIM EX-S12": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-S2": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1631.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1631.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Casio EXILIM EX-S20": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Casio EXILIM EX-S200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Casio EXILIM EX-S3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Casio EXILIM EX-S5": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3479.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3479.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Casio EXILIM EX-S600D": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Casio EXILIM EX-S7": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-S770": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Casio EXILIM EX-S770D": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Casio EXILIM EX-S8": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4059.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4059.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3052.0 - }, + }, "Casio EXILIM EX-S880": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Casio EXILIM EX-TR100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-TR150": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-V7": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Casio EXILIM EX-V8": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Casio EXILIM EX-Z1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Casio EXILIM EX-Z10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2655.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2655.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1996.0 - }, + }, "Casio EXILIM EX-Z100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Casio EXILIM EX-Z1000": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Casio EXILIM EX-Z1050": { - "Sensor height (mm)": 5.49, - "Sensor width (mm)": 7.31, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.75\" (~ 7.31 x 5.49 mm)", + "Sensor height (mm)": 5.49, + "Sensor width (mm)": 7.31, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.75\" (~ 7.31 x 5.49 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Casio EXILIM EX-Z1080": { - "Sensor height (mm)": 5.49, - "Sensor width (mm)": 7.31, - "Sensor res. (width)": 3665.0, - "Sensor": "1/1.75\" (~ 7.31 x 5.49 mm)", + "Sensor height (mm)": 5.49, + "Sensor width (mm)": 7.31, + "Sensor res. (width)": 3665.0, + "Sensor": "1/1.75\" (~ 7.31 x 5.49 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Casio EXILIM EX-Z110": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2918.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2918.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2194.0 - }, + }, "Casio EXILIM EX-Z120": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3137.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3137.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2359.0 - }, + }, "Casio EXILIM EX-Z150": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3321.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3321.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2497.0 - }, + }, "Casio EXILIM EX-Z16": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-Z19": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3517.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3517.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2644.0 - }, + }, "Casio EXILIM EX-Z2": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-Z20": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Casio EXILIM EX-Z200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Casio EXILIM EX-Z2000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Casio EXILIM EX-Z2300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Casio EXILIM EX-Z25": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-Z250": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3479.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3479.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Casio EXILIM EX-Z270": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Casio EXILIM EX-Z280": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-Z29": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Casio EXILIM EX-Z3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Casio EXILIM EX-Z30": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Casio EXILIM EX-Z300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Casio EXILIM EX-Z3000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4695.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4695.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3530.0 - }, + }, "Casio EXILIM EX-Z33": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Casio EXILIM EX-Z330": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4059.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4059.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3052.0 - }, + }, "Casio EXILIM EX-Z35": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-Z350": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4059.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4059.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3052.0 - }, + }, "Casio EXILIM EX-Z4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Casio EXILIM EX-Z40": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Casio EXILIM EX-Z400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-Z450": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-Z5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Casio EXILIM EX-Z50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Casio EXILIM EX-Z55": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Casio EXILIM EX-Z550": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Casio EXILIM EX-Z6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Casio EXILIM EX-Z60": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2825.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2825.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Casio EXILIM EX-Z600": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2867.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2867.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2156.0 - }, + }, "Casio EXILIM EX-Z65": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2910.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2910.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2188.0 - }, + }, "Casio EXILIM EX-Z7": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Casio EXILIM EX-Z70": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Casio EXILIM EX-Z700": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Casio EXILIM EX-Z75": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Casio EXILIM EX-Z77": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Casio EXILIM EX-Z8": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Casio EXILIM EX-Z80": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Casio EXILIM EX-Z800": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Casio EXILIM EX-Z85": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3479.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3479.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Casio EXILIM EX-Z850": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Casio EXILIM EX-Z9": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Casio EXILIM EX-Z90": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-ZR10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-ZR100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio EXILIM EX-ZR15": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EXILIM EX-ZR20": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EXILIM EX-ZR200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EXILIM EX-ZR300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EXILIM EX-ZS10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Casio EXILIM EX-ZS100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4389.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4389.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3300.0 - }, + }, "Casio EXILIM EX-ZS12": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EXILIM EX-ZS15": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Casio EXILIM EX-ZS150": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EXILIM EX-ZS20": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EXILIM EX-ZS6": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio EXILIM Pro EX-F1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2825.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2825.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Casio EXILIM QV-R100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Casio Exilim EX-10": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Casio Exilim EX-100": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Casio Exilim EX-P505": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Casio Exilim EX-P600": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2801.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2801.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2106.0 - }, + }, "Casio Exilim EX-P700": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Casio Exilim EX-S100": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 2070.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 2070.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1545.0 - }, + }, "Casio Exilim EX-S500": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Casio Exilim EX-S600": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Casio Exilim EX-Z1200 SR": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4011.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4011.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2993.0 - }, + }, "Casio Exilim EX-Z500": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Casio Exilim EX-Z57": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Casio Exilim EX-Z750": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Casio Exilim EX-ZR1000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio Exilim EX-ZR1100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Casio Exilim EX-ZS5": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Casio Exilim TRYX": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Casio GV-10": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1268.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1268.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 946.0 - }, + }, "Casio GV-20": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1596.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1596.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1191.0 - }, + }, "Casio QV-2000UX": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Casio QV-2100": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Casio QV-2300UX": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Casio QV-2400UX": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Casio QV-2800UX": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Casio QV-2900UX": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Casio QV-300": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 632.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 632.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Casio QV-3000EX": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Casio QV-3500EX": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Casio QV-3EX / XV-3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Casio QV-4000": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2218.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2218.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1668.0 - }, + }, "Casio QV-5000SX": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1264.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1264.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 950.0 - }, + }, "Casio QV-5500SX": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1264.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1264.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 950.0 - }, + }, "Casio QV-5700": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2552.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2552.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Casio QV-700": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 632.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 632.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Casio QV-7000SX": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1264.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1264.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 950.0 - }, + }, "Casio QV-770": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 632.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 632.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Casio QV-8000SX": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1264.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1264.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 950.0 - }, + }, "Casio QV-R3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Casio QV-R4": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Casio QV-R40": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Casio QV-R41": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Casio QV-R51": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2552.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2552.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Casio QV-R52": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2643.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2643.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1987.0 - }, + }, "Casio QV-R61": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2825.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2825.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Casio QV-R62": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2825.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2825.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Concord 00.": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Concord 2.": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 632.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 632.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 475.0 - }, + }, "Concord 3345z": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Concord 3346z": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Concord 40.": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2604.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2604.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Concord 42.": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Concord 43.": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Concord 4340z": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2374.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2374.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1785.0 - }, + }, "Concord 45.": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Concord 46.": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2044.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2044.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1537.0 - }, + }, "Concord 47.": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Concord 5340z": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2615.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2615.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1966.0 - }, + }, "Concord 5345z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2671.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2671.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2008.0 - }, + }, "Concord 6340z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2849.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2849.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Concord DVx": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Concord ES500z": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Concord ES510z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2604.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2604.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Concord Eye-Q 1000": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Concord Eye-Q 1300": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Concord Eye-Q 2040": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Concord Eye-Q 2133z": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Concord Eye-Q 3040AF": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Concord Eye-Q 3103": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Concord Eye-Q 3120 AF": { - "Sensor height (mm)": 3.0, - "Sensor width (mm)": 4.0, - "Sensor res. (width)": 1672.0, - "Sensor": "1/3.6\" (~ 4 x 3 mm)", + "Sensor height (mm)": 3.0, + "Sensor width (mm)": 4.0, + "Sensor res. (width)": 1672.0, + "Sensor": "1/3.6\" (~ 4 x 3 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Concord Eye-Q 3132z": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Concord Eye-Q 3340z": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Concord Eye-Q 3341z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Concord Eye-Q 3343z": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2047.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2047.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1539.0 - }, + }, "Concord Eye-Q 4060AF": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Concord Eye-Q 4330z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Concord Eye-Q 4342z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Concord Eye-Q 4360z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Concord Eye-Q 4363z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2324.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2324.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1747.0 - }, + }, "Concord Eye-Q 5062AF": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2590.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2590.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1947.0 - }, + }, "Concord Eye-Q 5330z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2640.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2640.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1985.0 - }, + }, "Concord Eye-Q Duo 2000": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1631.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1631.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Concord Eye-Q Duo LCD": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Concord Eye-Q Go 2000": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1631.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1631.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Concord Eye-Q Go LCD": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Concord Eye-Q Go Wireless": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1631.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1631.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Contax N Digital": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 3000.0, - "Sensor": "36 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 3000.0, + "Sensor": "36 x 24 mm", "Sensor res. (height)": 2000.0 - }, + }, "Contax SL300R T": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2054.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2054.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1544.0 - }, + }, "Contax TVS Digital": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2629.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2629.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Contax U4R": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Contax i4R": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "DJI FC6540": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 6016.0, + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 6016.0, "Sensor res. (height)": 3376.0 - }, - "DJI ZENMUSEP1": { - "Sensor height (mm)": 24, - "Sensor width (mm)": 35.9, - "Sensor res. (width)": 8192.0, - "Sensor": "35.9 x 24 mm", + }, + "DJI ZENMUSEP1": { + "Sensor height (mm)": 24, + "Sensor width (mm)": 35.9, + "Sensor res. (width)": 8192.0, + "Sensor": "35.9 x 24 mm", "Sensor res. (height)": 5460.0 - }, + }, "Epson L-500V": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2655.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2655.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1996.0 - }, + }, "Epson PhotoPC 3000 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Epson PhotoPC 3100 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Epson PhotoPC 500": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 632.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 632.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Epson PhotoPC 550": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 632.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 632.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Epson PhotoPC 600": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 964.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 964.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 725.0 - }, + }, "Epson PhotoPC 650": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1095.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1095.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 823.0 - }, + }, "Epson PhotoPC 700": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1264.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1264.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 950.0 - }, + }, "Epson PhotoPC 750 Zoom": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 950.0 - }, + }, "Epson PhotoPC 800": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Epson PhotoPC 850 Zoom": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Epson PhotoPC L-200": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Epson PhotoPC L-300": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2076.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2076.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1561.0 - }, + }, "Epson PhotoPC L-400": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Epson PhotoPC L-410": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Epson PhotoPC L-500V": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Epson R-D1": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 3020.0, - "Sensor": "23.7 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 3020.0, + "Sensor": "23.7 x 15.6 mm", "Sensor res. (height)": 1987.0 - }, + }, "Epson R-D1xG": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 3045.0, - "Sensor": "23.7 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 3045.0, + "Sensor": "23.7 x 15.6 mm", "Sensor res. (height)": 2003.0 - }, + }, "Epson RD-1s": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 3045.0, - "Sensor": "23.7 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 3045.0, + "Sensor": "23.7 x 15.6 mm", "Sensor res. (height)": 2003.0 - }, + }, "Fujifilm A850": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Fujifilm Bigjob HD-3W": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Fujifilm Bigjob HD1": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2044.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2044.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1537.0 - }, + }, "Fujifilm DS-260HD": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Fujifilm DS-300": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1264.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1264.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 950.0 - }, + }, "Fujifilm Digital Q1": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Fujifilm FinePix 1300": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Fujifilm FinePix 1400z": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1319.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1319.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 992.0 - }, + }, "Fujifilm FinePix 2300": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Fujifilm FinePix 2400 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Fujifilm FinePix 2600 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Fujifilm FinePix 2650": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Fujifilm FinePix 2800 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Fujifilm FinePix 3800": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Fujifilm FinePix 40i": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1786.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1786.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1343.0 - }, + }, "Fujifilm FinePix 4700 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1793.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1793.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1338.0 - }, + }, "Fujifilm FinePix 4800 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1793.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1793.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1338.0 - }, + }, "Fujifilm FinePix 4900 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1793.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1793.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1338.0 - }, + }, "Fujifilm FinePix 50i": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1793.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1793.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1338.0 - }, + }, "Fujifilm FinePix 6800 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2038.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2038.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1521.0 - }, + }, "Fujifilm FinePix 6900 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2038.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2038.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1521.0 - }, + }, "Fujifilm FinePix A100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix A101": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Fujifilm FinePix A120": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Fujifilm FinePix A150": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix A160": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Fujifilm FinePix A170": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Fujifilm FinePix A175": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Fujifilm FinePix A180": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Fujifilm FinePix A200": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Fujifilm FinePix A201": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Fujifilm FinePix A202": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Fujifilm FinePix A203": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Fujifilm FinePix A204": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Fujifilm FinePix A205 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Fujifilm FinePix A210 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Fujifilm FinePix A220": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix A225": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix A235": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix A303": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Fujifilm FinePix A310 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Fujifilm FinePix A330": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2095.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2095.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1575.0 - }, + }, "Fujifilm FinePix A340": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2363.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2363.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1777.0 - }, + }, "Fujifilm FinePix A345 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Fujifilm FinePix A350 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Fujifilm FinePix A400 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Fujifilm FinePix A500 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Fujifilm FinePix A510": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Fujifilm FinePix A600 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2860.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2860.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2134.0 - }, + }, "Fujifilm FinePix A610": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Fujifilm FinePix A700": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3116.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3116.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2343.0 - }, + }, "Fujifilm FinePix A800": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Fujifilm FinePix A820": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Fujifilm FinePix A825": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3322.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3322.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2498.0 - }, + }, "Fujifilm FinePix A900": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3479.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3479.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Fujifilm FinePix A920": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3479.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3479.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Fujifilm FinePix AV100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix AV105": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix AV110": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix AV130": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix AV140": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix AV150": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix AV180": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix AV200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix AV205": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix AV250": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix AV255": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix AX200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix AX205": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix AX230": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix AX245w": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix AX250": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix AX280": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix AX300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix AX305": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix AX350": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix AX355": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix AX500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix AX550": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix AX650": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix E500 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Fujifilm FinePix E510 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2629.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2629.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Fujifilm FinePix E550 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2835.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2835.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2116.0 - }, + }, "Fujifilm FinePix E900 Zoom": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3479.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3479.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Fujifilm FinePix EX-20": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Fujifilm FinePix F10 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2860.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2860.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2134.0 - }, + }, "Fujifilm FinePix F100fd": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3995.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3995.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix F11 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2860.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2860.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2134.0 - }, + }, "Fujifilm FinePix F20 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2860.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2860.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2134.0 - }, + }, "Fujifilm FinePix F200EXR": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3995.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3995.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix F30 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2860.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2860.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2134.0 - }, + }, "Fujifilm FinePix F300EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix F305EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix F31fd": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2860.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2860.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2134.0 - }, + }, "Fujifilm FinePix F401 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Fujifilm FinePix F402": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Fujifilm FinePix F40fd": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Fujifilm FinePix F410 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Fujifilm FinePix F420 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Fujifilm FinePix F440 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Fujifilm FinePix F450 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Fujifilm FinePix F455 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Fujifilm FinePix F45fd": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3322.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3322.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2498.0 - }, + }, "Fujifilm FinePix F460": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Fujifilm FinePix F470 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Fujifilm FinePix F47fd": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3466.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3466.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2606.0 - }, + }, "Fujifilm FinePix F480 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Fujifilm FinePix F500 EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix F50fd": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3995.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3995.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix F550 EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix F600 EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix F601 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2835.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2835.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2116.0 - }, + }, "Fujifilm FinePix F605EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix F60fd": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3995.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3995.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix F610": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2835.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2835.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2116.0 - }, + }, "Fujifilm FinePix F650 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Fujifilm FinePix F660EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix F700": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2038.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2038.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1521.0 - }, + }, "Fujifilm FinePix F70EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix F710": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2038.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2038.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1521.0 - }, + }, "Fujifilm FinePix F72EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix F750EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix F75EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix F770EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix F800EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix F80EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix F810 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2835.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2835.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2116.0 - }, + }, "Fujifilm FinePix F850EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix F85EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix F900EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix HS10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Fujifilm FinePix HS11": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Fujifilm FinePix HS20 EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix HS22 EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix HS25 EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix HS30 EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix HS35 EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix HS50 EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix IS Pro": { - "Sensor height (mm)": 15.5, - "Sensor width (mm)": 23.0, - "Sensor res. (width)": 3004.0, - "Sensor": "23 x 15.5 mm", + "Sensor height (mm)": 15.5, + "Sensor width (mm)": 23.0, + "Sensor res. (width)": 3004.0, + "Sensor": "23 x 15.5 mm", "Sensor res. (height)": 2030.0 - }, + }, "Fujifilm FinePix IS-1": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3479.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3479.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Fujifilm FinePix J10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Fujifilm FinePix J100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix J110w": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix J12": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Fujifilm FinePix J120": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix J15": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Fujifilm FinePix J150w": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix J20": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix J210": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix J22": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix J25": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix J250": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix J26": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Fujifilm FinePix J27": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix J28": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Fujifilm FinePix J29": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Fujifilm FinePix J30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix J32": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix J35": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix J37": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix J38": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix J50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Fujifilm FinePix JV100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix JV105": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix JV110": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix JV150": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Fujifilm FinePix JV200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix JV205": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix JV250": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix JV255": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix JX200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix JX205": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix JX210": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix JX250": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix JX280": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Fujifilm FinePix JX300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix JX305": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix JX350": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix JX355": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix JX370": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix JX375": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix JX400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix JX405": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix JX420": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix JX500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4376.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4376.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3290.0 - }, + }, "Fujifilm FinePix JX520": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix JX530": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix JX550": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix JX580": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix JX700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix JZ100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4376.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4376.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3290.0 - }, + }, "Fujifilm FinePix JZ200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix JZ250": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix JZ300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Fujifilm FinePix JZ305": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Fujifilm FinePix JZ310": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Fujifilm FinePix JZ500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Fujifilm FinePix JZ505": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Fujifilm FinePix JZ510": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Fujifilm FinePix JZ700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix M603": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2038.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2038.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1521.0 - }, + }, "Fujifilm FinePix PR21": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1749.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1749.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1315.0 - }, + }, "Fujifilm FinePix Real 3D W1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix Real 3D W3": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix S1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4671.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4671.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3512.0 - }, + }, "Fujifilm FinePix S1 Pro": { - "Sensor height (mm)": 15.5, - "Sensor width (mm)": 23.0, - "Sensor res. (width)": 2142.0, - "Sensor": "23 x 15.5 mm", + "Sensor height (mm)": 15.5, + "Sensor width (mm)": 23.0, + "Sensor res. (width)": 2142.0, + "Sensor": "23 x 15.5 mm", "Sensor res. (height)": 1447.0 - }, + }, "Fujifilm FinePix S1000fd": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix S100fs": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3842.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3842.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 2889.0 - }, + }, "Fujifilm FinePix S1500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix S1600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix S1700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix S1730": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix S1770": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix S1800": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix S1850": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix S1880": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix S1900": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix S2 Pro": { - "Sensor height (mm)": 15.5, - "Sensor width (mm)": 23.0, - "Sensor res. (width)": 3004.0, - "Sensor": "23 x 15.5 mm", + "Sensor height (mm)": 15.5, + "Sensor width (mm)": 23.0, + "Sensor res. (width)": 3004.0, + "Sensor": "23 x 15.5 mm", "Sensor res. (height)": 2030.0 - }, + }, "Fujifilm FinePix S20 Pro": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2860.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2860.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2134.0 - }, + }, "Fujifilm FinePix S2000hd": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix S200EXR": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3995.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3995.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix S205EXR": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3995.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3995.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix S2500hd": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix S2550hd": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix S2600hd": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix S2800hd": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix S2900hd": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix S2950": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix S2980": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix S2990": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix S3 Pro": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3035.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3035.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 2010.0 - }, + }, "Fujifilm FinePix S3000 Z": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Fujifilm FinePix S304": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Fujifilm FinePix S3200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix S3250": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix S3300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Fujifilm FinePix S3350": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Fujifilm FinePix S3400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Fujifilm FinePix S3450": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Fujifilm FinePix S3500 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Fujifilm FinePix S4000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix S4050": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix S4200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix S4300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix S4400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix S4500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix S4600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix S4700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix S4800": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix S5 Pro": { - "Sensor height (mm)": 15.5, - "Sensor width (mm)": 23.0, - "Sensor res. (width)": 3004.0, - "Sensor": "23 x 15.5 mm", + "Sensor height (mm)": 15.5, + "Sensor width (mm)": 23.0, + "Sensor res. (width)": 3004.0, + "Sensor": "23 x 15.5 mm", "Sensor res. (height)": 2030.0 - }, + }, "Fujifilm FinePix S5000 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Fujifilm FinePix S5100 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Fujifilm FinePix S5200 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Fujifilm FinePix S5500 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2363.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2363.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1777.0 - }, + }, "Fujifilm FinePix S5600 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2635.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2635.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1981.0 - }, + }, "Fujifilm FinePix S5700 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Fujifilm FinePix S5800": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Fujifilm FinePix S6000fd": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2860.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2860.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2134.0 - }, + }, "Fujifilm FinePix S602 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2038.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2038.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1521.0 - }, + }, "Fujifilm FinePix S602Z Pro": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2038.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2038.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1521.0 - }, + }, "Fujifilm FinePix S6500fd": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2905.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2905.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2168.0 - }, + }, "Fujifilm FinePix S6600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Fujifilm FinePix S6700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Fujifilm FinePix S6800": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Fujifilm FinePix S7000 Zoom": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2835.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2835.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2116.0 - }, + }, "Fujifilm FinePix S8000fd": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Fujifilm FinePix S8100fd": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix S8200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Fujifilm FinePix S8300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Fujifilm FinePix S8400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Fujifilm FinePix S8500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Fujifilm FinePix S8600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix S9000 Zoom": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3479.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3479.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Fujifilm FinePix S9100": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3479.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3479.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Fujifilm FinePix S9200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Fujifilm FinePix S9400W": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Fujifilm FinePix S9500": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3506.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3506.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2636.0 - }, + }, "Fujifilm FinePix S9600": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.0, - "Sensor res. (width)": 3459.0, - "Sensor": "1/1.6\" (~ 8 x 6 mm)", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.0, + "Sensor res. (width)": 3459.0, + "Sensor": "1/1.6\" (~ 8 x 6 mm)", "Sensor res. (height)": 2601.0 - }, + }, "Fujifilm FinePix SL1000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Fujifilm FinePix SL240": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix SL260": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix SL280": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix SL300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix T200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix T205": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix T300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix T305": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix T350": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix T400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix T500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix T550": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix V10 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Fujifilm FinePix X100": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4281.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4281.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2873.0 - }, + }, "Fujifilm FinePix XP10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix XP100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4376.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4376.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3290.0 - }, + }, "Fujifilm FinePix XP11": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix XP150": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4376.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4376.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3290.0 - }, + }, "Fujifilm FinePix XP170": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4376.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4376.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3290.0 - }, + }, "Fujifilm FinePix XP20": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Fujifilm FinePix XP200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4671.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4671.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3512.0 - }, + }, "Fujifilm FinePix XP22": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Fujifilm FinePix XP30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Fujifilm FinePix XP33": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Fujifilm FinePix XP50": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4376.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4376.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3290.0 - }, + }, "Fujifilm FinePix XP60": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4671.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4671.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3512.0 - }, + }, "Fujifilm FinePix XP70": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4671.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4671.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3512.0 - }, + }, "Fujifilm FinePix Z1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Fujifilm FinePix Z1000EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix Z100fd": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Fujifilm FinePix Z10fd": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Fujifilm FinePix Z110": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Fujifilm FinePix Z2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Fujifilm FinePix Z200fd": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix Z20fd": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix Z3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Fujifilm FinePix Z30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix Z300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix Z31": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix Z33WP": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix Z35": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix Z37": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Fujifilm FinePix Z5fd": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2894.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2894.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2176.0 - }, + }, "Fujifilm FinePix Z70": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix Z700EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix Z707EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix Z71": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Fujifilm FinePix Z80": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Fujifilm FinePix Z800EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix Z808EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm FinePix Z81": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Fujifilm FinePix Z90": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Fujifilm FinePix Z900EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix Z909EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm FinePix Z91": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Fujifilm FinePix Z950EXR": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Fujifilm Finepix 30i": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Fujifilm MX-1200": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 950.0 - }, + }, "Fujifilm MX-1400": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Fujifilm MX-1500": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Fujifilm MX-1700": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Fujifilm MX-2700": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1710.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1710.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1286.0 - }, + }, "Fujifilm MX-2900 Zoom": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1710.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1710.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1286.0 - }, + }, "Fujifilm MX-500": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Fujifilm MX-600 Zoom": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Fujifilm MX-700": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Fujifilm X-A1": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4962.0, - "Sensor": "23.6 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4962.0, + "Sensor": "23.6 x 15.6 mm", "Sensor res. (height)": 3286.0 - }, + }, "Fujifilm X-E1": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4962.0, - "Sensor": "23.6 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4962.0, + "Sensor": "23.6 x 15.6 mm", "Sensor res. (height)": 3286.0 - }, + }, "Fujifilm X-E2": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4962.0, - "Sensor": "23.6 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4962.0, + "Sensor": "23.6 x 15.6 mm", "Sensor res. (height)": 3286.0 - }, + }, "Fujifilm X-M1": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4962.0, - "Sensor": "23.6 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4962.0, + "Sensor": "23.6 x 15.6 mm", "Sensor res. (height)": 3286.0 - }, + }, "Fujifilm X-Pro1": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4962.0, - "Sensor": "23.6 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4962.0, + "Sensor": "23.6 x 15.6 mm", "Sensor res. (height)": 3286.0 - }, + }, "Fujifilm X-S1": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3995.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3995.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm X-T1": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4962.0, - "Sensor": "23.6 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4962.0, + "Sensor": "23.6 x 15.6 mm", "Sensor res. (height)": 3286.0 - }, + }, "Fujifilm X10": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3995.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3995.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm X100S": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4929.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4929.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 3308.0 - }, + }, "Fujifilm X100T": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4962.0, - "Sensor": "23.6 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4962.0, + "Sensor": "23.6 x 15.6 mm", "Sensor res. (height)": 3286.0 - }, + }, "Fujifilm X20": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3995.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3995.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm X30": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3995.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3995.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm XF1": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3995.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3995.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Fujifilm XQ1": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3995.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3995.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 3004.0 - }, + }, "GE A1030": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3678.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3678.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2745.0 - }, + }, "GE A1035": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "GE A1050": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "GE A1235": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "GE A1250": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "GE A1255": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3053.0 - }, + }, "GE A1455": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "GE A1456W": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4376.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4376.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3290.0 - }, + }, "GE A730": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3137.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3137.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2359.0 - }, + }, "GE A735": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3137.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3137.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2359.0 - }, + }, "GE A830": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3322.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3322.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2498.0 - }, + }, "GE A835": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3362.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3362.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2528.0 - }, + }, "GE A950": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3479.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3479.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2616.0 - }, + }, "GE C1033": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "GE C1233": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3053.0 - }, + }, "GE C1433": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4376.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4376.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3290.0 - }, + }, "GE C1440W": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "GE Create": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3053.0 - }, + }, "GE E1030": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3759.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3759.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2805.0 - }, + }, "GE E1035": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3759.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3759.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2805.0 - }, + }, "GE E1040": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3759.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3759.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2805.0 - }, + }, "GE E1050": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3772.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3772.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2836.0 - }, + }, "GE E1050 TW": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "GE E1055 W": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "GE E1235": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4060.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4060.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3053.0 - }, + }, "GE E1240": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4092.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4092.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3054.0 - }, + }, "GE E1250TW": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "GE E1255W": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "GE E1276W": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "GE E1410SW": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4376.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4376.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3290.0 - }, + }, "GE E1450W": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4376.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4376.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3290.0 - }, + }, "GE E1480W": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "GE E1486TW": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "GE E1680W": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4684.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4684.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3522.0 - }, + }, "GE E840S": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3333.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3333.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2506.0 - }, + }, "GE E850": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3322.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3322.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2498.0 - }, + }, "GE G 1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3137.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3137.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2359.0 - }, + }, "GE G100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4378.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4378.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3292.0 - }, + }, "GE G2": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3325.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3325.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2500.0 - }, + }, "GE G3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3772.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3772.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2836.0 - }, + }, "GE G3WP": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "GE G5WP": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "GE J1050": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "GE J1250": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "GE J1455": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "GE J1456W": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "GE J1458W": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "GE J1470S": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4389.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4389.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3300.0 - }, + }, "GE PJ1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "GE X1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 3029.0 - }, + }, "GE X3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3772.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3772.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2836.0 - }, + }, "GE X500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4684.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4684.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3522.0 - }, + }, "GE X550": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "GE X600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4378.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4378.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3292.0 - }, + }, "HP CA350": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "HP CB350": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "HP CW450": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4078.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4078.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3066.0 - }, + }, "HP CW450t": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4078.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4078.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3066.0 - }, + }, "HP PW460t": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4078.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4078.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3066.0 - }, + }, "HP PW550": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3053.0 - }, + }, "HP Photosmart 120": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1153.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1153.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 867.0 - }, + }, "HP Photosmart 318": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1749.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1749.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1315.0 - }, + }, "HP Photosmart 320": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "HP Photosmart 435": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "HP Photosmart 612": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1749.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1749.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1315.0 - }, + }, "HP Photosmart 620": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1672.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1672.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 1257.0 - }, + }, "HP Photosmart 635": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1678.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1678.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1252.0 - }, + }, "HP Photosmart 715": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "HP Photosmart 720": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "HP Photosmart 733": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "HP Photosmart 735": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "HP Photosmart 812": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "HP Photosmart 850": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "HP Photosmart 935": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2640.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2640.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1985.0 - }, + }, "HP Photosmart 945": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2604.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2604.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1958.0 - }, + }, "HP Photosmart C20": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1153.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1153.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 867.0 - }, + }, "HP Photosmart C200": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1153.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1153.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 867.0 - }, + }, "HP Photosmart C215": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 989.0 - }, + }, "HP Photosmart C30": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1153.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1153.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 867.0 - }, + }, "HP Photosmart C315": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "HP Photosmart C500": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1631.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1631.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1226.0 - }, + }, "HP Photosmart C618": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "HP Photosmart C912": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1710.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1710.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1286.0 - }, + }, "HP Photosmart E317": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "HP Photosmart E327": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "HP Photosmart E337": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "HP Photosmart E427": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "HP Photosmart M22": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "HP Photosmart M23": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "HP Photosmart M307": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "HP Photosmart M407": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1756.0 - }, + }, "HP Photosmart M417": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2627.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2627.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1975.0 - }, + }, "HP Photosmart M425": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "HP Photosmart M437": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2629.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2629.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1977.0 - }, + }, "HP Photosmart M447": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "HP Photosmart M517": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "HP Photosmart M525": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "HP Photosmart M527": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "HP Photosmart M537": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2894.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2894.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2176.0 - }, + }, "HP Photosmart M547": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2871.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2871.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2159.0 - }, + }, "HP Photosmart M627": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "HP Photosmart M637": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "HP Photosmart M737": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "HP Photosmart Mz67": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "HP Photosmart R507": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2363.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2363.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1777.0 - }, + }, "HP Photosmart R607": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1756.0 - }, + }, "HP Photosmart R707": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2604.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2604.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1958.0 - }, + }, "HP Photosmart R717": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2871.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2871.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2159.0 - }, + }, "HP Photosmart R725": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2871.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2871.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2159.0 - }, + }, "HP Photosmart R727": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2871.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2871.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2159.0 - }, + }, "HP Photosmart R742": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "HP Photosmart R817": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2655.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2655.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1996.0 - }, + }, "HP Photosmart R818": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2655.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2655.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1996.0 - }, + }, "HP Photosmart R827": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "HP Photosmart R837": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3137.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3137.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2359.0 - }, + }, "HP Photosmart R847": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "HP Photosmart R927": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3302.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3302.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2483.0 - }, + }, "HP Photosmart R937": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "HP Photosmart R967": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3665.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3665.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2756.0 - }, + }, "HP R607 BMW": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1756.0 - }, + }, "HP R607 Harajuku": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "HP SB360": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "HP SW450": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4078.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4078.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3066.0 - }, + }, "JVC GC-QX3HD": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "JVC GC-QX5HD": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Jenoptik JD 1300 D": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Jenoptik JD 1300 F": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Jenoptik JD 1500 z3": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1412.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1412.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1062.0 - }, + }, "Jenoptik JD 2.1 FF": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1678.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1678.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1252.0 - }, + }, "Jenoptik JD 2.1 xz3": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1678.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1678.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1252.0 - }, + }, "Jenoptik JD 2100 AF": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Jenoptik JD 2100 F": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Jenoptik JD 2100 M": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Jenoptik JD 2100 z3 S": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Jenoptik JD 2300 z3": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1755.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1755.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1310.0 - }, + }, "Jenoptik JD 3.1 exclusiv": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Jenoptik JD 3.1 z3 MPEG 4": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2047.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2047.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1539.0 - }, + }, "Jenoptik JD 3.3 AF": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2095.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2095.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1575.0 - }, + }, "Jenoptik JD 3.3 xz3": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Jenoptik JD 3.3x4 ie": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Jenoptik JD 3.3z10": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Jenoptik JD 3300 z3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2079.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2079.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1563.0 - }, + }, "Jenoptik JD 3300 z3 S": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2076.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2076.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1561.0 - }, + }, "Jenoptik JD 4.0 LCD": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Jenoptik JD 4.1 xz3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2343.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2343.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1762.0 - }, + }, "Jenoptik JD 4.1 z3 MPEG4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Jenoptik JD 4.1 z8": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Jenoptik JD 4.1 zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Jenoptik JD 4100 z3": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2353.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2353.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Jenoptik JD 4100 z3 S": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Jenoptik JD 4100 zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2343.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2343.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1762.0 - }, + }, "Jenoptik JD 4360z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Jenoptik JD 4363z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Jenoptik JD 5.0z3 EasyShot": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Jenoptik JD 5.2 z3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Jenoptik JD 5.2 z3 MPEG4": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2552.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2552.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Jenoptik JD 5.2 zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Jenoptik JD 5200 z3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2629.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2629.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Jenoptik JD 6.0 z3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2910.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2910.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2188.0 - }, + }, "Jenoptik JD 6.0 z3 MPEG4": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2862.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2862.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2152.0 - }, + }, "Jenoptik JD 6.0 z3 exclusiv": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2910.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2910.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2188.0 - }, + }, "Jenoptik JD 8.0 exclusiv": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Jenoptik JD 8.0z3 EasyShot": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Jenoptik JD C 1.3 LCD": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Jenoptik JD C 1.3 SD": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Jenoptik JD C 1300": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Jenoptik JD C 2.1 LCD": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Jenoptik JD C 3.0 S": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1998.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1998.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1502.0 - }, + }, "Jenoptik JD C 3.1 LCD": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2044.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2044.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1537.0 - }, + }, "Jenoptik JD C 3.1 LI": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Jenoptik JD C 3.1 SL": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Jenoptik JD C 3.1 z3": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2095.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2095.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1575.0 - }, + }, "Jenoptik JD C 5.0 SL": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak DC200": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 1095.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 1095.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 823.0 - }, + }, "Kodak DC200 plus": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 1095.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 1095.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 823.0 - }, + }, "Kodak DC210 plus": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 1095.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 1095.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 823.0 - }, + }, "Kodak DC215": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 1095.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 1095.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 823.0 - }, + }, "Kodak DC220": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 1095.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 1095.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 823.0 - }, + }, "Kodak DC240": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 1264.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 1264.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 950.0 - }, + }, "Kodak DC260": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 1412.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 1412.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 1062.0 - }, + }, "Kodak DC265": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 1412.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 1412.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 1062.0 - }, + }, "Kodak DC280": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1637.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1637.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1222.0 - }, + }, "Kodak DC290": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 1710.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 1710.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 1286.0 - }, + }, "Kodak DC3200": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1099.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1099.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 820.0 - }, + }, "Kodak DC3400": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1637.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1637.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1222.0 - }, + }, "Kodak DC3800": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1637.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1637.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1222.0 - }, + }, "Kodak DC4800": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Kodak DC5000": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 1631.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 1631.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Kodak DCS Pro 14n": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 4533.0, - "Sensor": "36 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 4533.0, + "Sensor": "36 x 24 mm", "Sensor res. (height)": 3022.0 - }, + }, "Kodak DCS Pro SLR/c": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 4500.0, - "Sensor": "36 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 4500.0, + "Sensor": "36 x 24 mm", "Sensor res. (height)": 3000.0 - }, + }, "Kodak DCS Pro SLR/n": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 4500.0, - "Sensor": "36 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 4500.0, + "Sensor": "36 x 24 mm", "Sensor res. (height)": 3000.0 - }, + }, "Kodak DCS315": { - "Sensor height (mm)": 18.43, - "Sensor width (mm)": 27.65, - "Sensor res. (width)": 1500.0, - "Sensor": "27.65 x 18.43 mm", + "Sensor height (mm)": 18.43, + "Sensor width (mm)": 27.65, + "Sensor res. (width)": 1500.0, + "Sensor": "27.65 x 18.43 mm", "Sensor res. (height)": 1000.0 - }, + }, "Kodak DCS330": { - "Sensor height (mm)": 13.5, - "Sensor width (mm)": 18.1, - "Sensor res. (width)": 2005.0, - "Sensor": "18.1 x 13.5 mm", + "Sensor height (mm)": 13.5, + "Sensor width (mm)": 18.1, + "Sensor res. (width)": 2005.0, + "Sensor": "18.1 x 13.5 mm", "Sensor res. (height)": 1496.0 - }, + }, "Kodak DCS420": { - "Sensor height (mm)": 9.3, - "Sensor width (mm)": 14.0, - "Sensor res. (width)": 1505.0, - "Sensor": "14 x 9.3 mm", + "Sensor height (mm)": 9.3, + "Sensor width (mm)": 14.0, + "Sensor res. (width)": 1505.0, + "Sensor": "14 x 9.3 mm", "Sensor res. (height)": 997.0 - }, + }, "Kodak DCS460": { - "Sensor height (mm)": 18.43, - "Sensor width (mm)": 27.65, - "Sensor res. (width)": 3050.0, - "Sensor": "27.65 x 18.43 mm", + "Sensor height (mm)": 18.43, + "Sensor width (mm)": 27.65, + "Sensor res. (width)": 3050.0, + "Sensor": "27.65 x 18.43 mm", "Sensor res. (height)": 2033.0 - }, + }, "Kodak DCS520": { - "Sensor height (mm)": 18.43, - "Sensor width (mm)": 27.65, - "Sensor res. (width)": 1733.0, - "Sensor": "27.65 x 18.43 mm", + "Sensor height (mm)": 18.43, + "Sensor width (mm)": 27.65, + "Sensor res. (width)": 1733.0, + "Sensor": "27.65 x 18.43 mm", "Sensor res. (height)": 1155.0 - }, + }, "Kodak DCS560": { - "Sensor height (mm)": 18.43, - "Sensor width (mm)": 27.65, - "Sensor res. (width)": 3026.0, - "Sensor": "27.65 x 18.43 mm", + "Sensor height (mm)": 18.43, + "Sensor width (mm)": 27.65, + "Sensor res. (width)": 3026.0, + "Sensor": "27.65 x 18.43 mm", "Sensor res. (height)": 2017.0 - }, + }, "Kodak DCS620": { - "Sensor height (mm)": 18.43, - "Sensor width (mm)": 27.65, - "Sensor res. (width)": 1733.0, - "Sensor": "27.65 x 18.43 mm", + "Sensor height (mm)": 18.43, + "Sensor width (mm)": 27.65, + "Sensor res. (width)": 1733.0, + "Sensor": "27.65 x 18.43 mm", "Sensor res. (height)": 1155.0 - }, + }, "Kodak DCS620x": { - "Sensor height (mm)": 15.5, - "Sensor width (mm)": 22.8, - "Sensor res. (width)": 1714.0, - "Sensor": "22.8 x 15.5 mm", + "Sensor height (mm)": 15.5, + "Sensor width (mm)": 22.8, + "Sensor res. (width)": 1714.0, + "Sensor": "22.8 x 15.5 mm", "Sensor res. (height)": 1166.0 - }, + }, "Kodak DCS660": { - "Sensor height (mm)": 18.43, - "Sensor width (mm)": 27.65, - "Sensor res. (width)": 3026.0, - "Sensor": "27.65 x 18.43 mm", + "Sensor height (mm)": 18.43, + "Sensor width (mm)": 27.65, + "Sensor res. (width)": 3026.0, + "Sensor": "27.65 x 18.43 mm", "Sensor res. (height)": 2017.0 - }, + }, "Kodak DCS720x": { - "Sensor height (mm)": 15.5, - "Sensor width (mm)": 22.8, - "Sensor res. (width)": 1714.0, - "Sensor": "22.8 x 15.5 mm", + "Sensor height (mm)": 15.5, + "Sensor width (mm)": 22.8, + "Sensor res. (width)": 1714.0, + "Sensor": "22.8 x 15.5 mm", "Sensor res. (height)": 1166.0 - }, + }, "Kodak DCS760": { - "Sensor height (mm)": 18.43, - "Sensor width (mm)": 27.65, - "Sensor res. (width)": 3026.0, - "Sensor": "27.65 x 18.43 mm", + "Sensor height (mm)": 18.43, + "Sensor width (mm)": 27.65, + "Sensor res. (width)": 3026.0, + "Sensor": "27.65 x 18.43 mm", "Sensor res. (height)": 2017.0 - }, + }, "Kodak DX3215": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Kodak DX3500": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1710.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1710.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1286.0 - }, + }, "Kodak DX3600": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1710.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1710.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1286.0 - }, + }, "Kodak DX3700": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Kodak DX3900": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Kodak DX4330": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Kodak DX4530": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak DX4900": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kodak DX6340": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Kodak DX6440": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kodak DX6490": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kodak DX7440": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kodak DX7590": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak DX7630": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2871.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2871.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2159.0 - }, + }, "Kodak EasyShare C1013": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Kodak EasyShare C135": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak EasyShare C140": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Kodak EasyShare C142": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Kodak EasyShare C143": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4078.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4078.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3066.0 - }, + }, "Kodak EasyShare C1505": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare C1530": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Kodak EasyShare C1550": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4699.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4699.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3533.0 - }, + }, "Kodak EasyShare C160": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3498.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3498.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2630.0 - }, + }, "Kodak EasyShare C180": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3755.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3755.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2823.0 - }, + }, "Kodak EasyShare C182": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4110.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4110.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3090.0 - }, + }, "Kodak EasyShare C183": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4406.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4406.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3313.0 - }, + }, "Kodak EasyShare C190": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4110.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4110.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3090.0 - }, + }, "Kodak EasyShare C195": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Kodak EasyShare C300": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2063.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2063.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Kodak EasyShare C310": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kodak EasyShare C330": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kodak EasyShare C340": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak EasyShare C360": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak EasyShare C433": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kodak EasyShare C503": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak EasyShare C513": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak EasyShare C530": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2671.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2671.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2008.0 - }, + }, "Kodak EasyShare C533": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak EasyShare C610": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Kodak EasyShare C613": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2871.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2871.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2159.0 - }, + }, "Kodak EasyShare C623": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Kodak EasyShare C643": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Kodak EasyShare C653": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Kodak EasyShare C663": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Kodak EasyShare C703": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3137.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3137.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2359.0 - }, + }, "Kodak EasyShare C713": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Kodak EasyShare C743": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Kodak EasyShare C763": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Kodak EasyShare C813": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Kodak EasyShare C875": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Kodak EasyShare C913": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3498.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3498.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2630.0 - }, + }, "Kodak EasyShare CD1013": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Kodak EasyShare CD703": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Kodak EasyShare CD80": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Kodak EasyShare CD82": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare CD90": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare CD93": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3555.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3555.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2673.0 - }, + }, "Kodak EasyShare CX4200": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Kodak EasyShare CX4230": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Kodak EasyShare CX4300": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Kodak EasyShare CX6200": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Kodak EasyShare CX6230": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Kodak EasyShare CX6330": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Kodak EasyShare CX6445": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Kodak EasyShare CX7220": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Kodak EasyShare CX7300": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Kodak EasyShare CX7330": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Kodak EasyShare CX7430": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kodak EasyShare CX7525": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Kodak EasyShare CX7530": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Kodak EasyShare LS745": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Kodak EasyShare M1033": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Kodak EasyShare M1063": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Kodak EasyShare M1073 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Kodak EasyShare M1093 IS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Kodak EasyShare M215": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 4315.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 4315.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak EasyShare M320": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3498.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3498.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2630.0 - }, + }, "Kodak EasyShare M340": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Kodak EasyShare M341": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare M380": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Kodak EasyShare M381": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare M420": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Kodak EasyShare M522": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Kodak EasyShare M530": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare M531": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Kodak EasyShare M532": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak EasyShare M5370": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Kodak EasyShare M550": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4045.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4045.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Kodak EasyShare M552": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak EasyShare M565": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak EasyShare M575": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak EasyShare M580": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak EasyShare M583": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak EasyShare M750": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Kodak EasyShare M753": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Kodak EasyShare M763": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Kodak EasyShare M853": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Kodak EasyShare M863": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Kodak EasyShare M873": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Kodak EasyShare M883": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Kodak EasyShare M893 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Kodak EasyShare MD1063": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Kodak EasyShare MD30": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Kodak EasyShare MD41": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Kodak EasyShare MD81": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Kodak EasyShare MD853": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Kodak EasyShare MD863": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Kodak EasyShare MX1063": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3755.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3755.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2823.0 - }, + }, "Kodak EasyShare Max Z990": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare Mini": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 3647.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 3647.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Kodak EasyShare P712": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Kodak EasyShare P850": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak EasyShare P880": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Kodak EasyShare Sport": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare Touch M577": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak EasyShare V1003": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Kodak EasyShare V1073": { - "Sensor height (mm)": 5.89, - "Sensor width (mm)": 7.85, - "Sensor res. (width)": 3842.0, - "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", + "Sensor height (mm)": 5.89, + "Sensor width (mm)": 7.85, + "Sensor res. (width)": 3842.0, + "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", "Sensor res. (height)": 2889.0 - }, + }, "Kodak EasyShare V1233": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 3995.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 3995.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare V1253": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 3995.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 3995.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare V1273": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 3995.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 3995.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare V530": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak EasyShare V550": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak EasyShare V570": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak EasyShare V603": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Kodak EasyShare V610": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Kodak EasyShare V705": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Kodak EasyShare V803": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Kodak EasyShare Z1012 IS": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Kodak EasyShare Z1015 IS": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Kodak EasyShare Z1085 IS": { - "Sensor height (mm)": 5.89, - "Sensor width (mm)": 7.85, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", + "Sensor height (mm)": 5.89, + "Sensor width (mm)": 7.85, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Kodak EasyShare Z1275": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4011.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4011.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Kodak EasyShare Z1285": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4011.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4011.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Kodak EasyShare Z1485 IS": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4315.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4315.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak EasyShare Z5010": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak EasyShare Z5120": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Kodak EasyShare Z612": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Kodak EasyShare Z650": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Kodak EasyShare Z700": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kodak EasyShare Z710": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Kodak EasyShare Z712 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Kodak EasyShare Z730": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak EasyShare Z740": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak EasyShare Z7590": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak EasyShare Z760": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2849.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2849.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Kodak EasyShare Z812 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Kodak EasyShare Z8612 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Kodak EasyShare Z885": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Kodak EasyShare Z915": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Kodak EasyShare Z950": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare Z980": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare Z981": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak EasyShare Z990": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Kodak EasyShare ZD15": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Kodak EasyShare ZD710": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Kodak EasyShare ZD8612 IS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Kodak EasyShare-One 6MP": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Kodak Easyshare One": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kodak LS420": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1631.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1631.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Kodak LS443": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kodak LS633": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Kodak LS743": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kodak LS753": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak LS755": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kodak M590": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 4315.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 4315.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak PixPro AZ251": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4635.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4635.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3485.0 - }, + }, "Kodak PixPro AZ361": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4635.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4635.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3485.0 - }, + }, "Kodak PixPro AZ362": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4667.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4667.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3509.0 - }, + }, "Kodak PixPro AZ501": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4635.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4635.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3485.0 - }, + }, "Kodak PixPro AZ521": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4667.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4667.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3509.0 - }, + }, "Kodak PixPro AZ522": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4667.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4667.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3509.0 - }, + }, "Kodak PixPro AZ651": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5244.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5244.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3943.0 - }, + }, "Kodak PixPro FZ151": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4635.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4635.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3485.0 - }, + }, "Kodak PixPro FZ201": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4635.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4635.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3485.0 - }, + }, "Kodak PixPro FZ41": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4635.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4635.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3485.0 - }, + }, "Kodak PixPro FZ51": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4635.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4635.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3485.0 - }, + }, "Kodak S-1": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4731.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4731.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3557.0 - }, + }, "Kodak Slice": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Kodak mc3": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 632.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 632.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 475.0 - }, + }, "Konica DG-2": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Konica DG-3Z": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2076.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2076.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1561.0 - }, + }, "Konica Q-M100": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1198.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1198.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 901.0 - }, + }, "Konica Q-M200": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Konica Revio C2": { - "Sensor height (mm)": 3.17, - "Sensor width (mm)": 4.23, - "Sensor res. (width)": 1264.0, - "Sensor": "1/3.4\" (~ 4.23 x 3.17 mm)", + "Sensor height (mm)": 3.17, + "Sensor width (mm)": 4.23, + "Sensor res. (width)": 1264.0, + "Sensor": "1/3.4\" (~ 4.23 x 3.17 mm)", "Sensor res. (height)": 950.0 - }, + }, "Konica Revio KD-200Z": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Konica Revio KD-210Z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1676.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1676.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Konica Revio KD-220Z": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1637.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1637.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1222.0 - }, + }, "Konica Revio KD-25": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1672.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1672.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Konica Revio KD-300Z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Konica Revio KD-310Z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2063.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2063.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Konica Revio KD-3300Z": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Konica Revio KD-4000Z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Konica Revio KD-400Z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Konica Revio KD-410Z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2343.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2343.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1762.0 - }, + }, "Konica Revio KD-420Z": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Konica Revio KD-500Z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2629.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2629.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Konica Revio KD-510Z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2671.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2671.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2008.0 - }, + }, "Konica-Minolta DG-5W": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2363.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2363.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1777.0 - }, + }, "Konica-Minolta DiMAGE A2": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3262.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3262.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Konica-Minolta DiMAGE A200": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3322.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3322.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 2498.0 - }, + }, "Konica-Minolta DiMAGE E40": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2363.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2363.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1777.0 - }, + }, "Konica-Minolta DiMAGE E50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Konica-Minolta DiMAGE E500": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2629.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2629.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Konica-Minolta DiMAGE G530": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Konica-Minolta DiMAGE G600": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 2918.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 2918.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 2194.0 - }, + }, "Konica-Minolta DiMAGE X1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3322.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3322.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2498.0 - }, + }, "Konica-Minolta DiMAGE X31": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 2070.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 2070.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1545.0 - }, + }, "Konica-Minolta DiMAGE X50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Konica-Minolta DiMAGE X60": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2680.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2680.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2015.0 - }, + }, "Konica-Minolta DiMAGE Xg": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Konica-Minolta DiMAGE Z10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Konica-Minolta DiMAGE Z2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Konica-Minolta DiMAGE Z20": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Konica-Minolta DiMAGE Z3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Konica-Minolta DiMAGE Z5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Konica-Minolta DiMAGE Z6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2918.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2918.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2194.0 - }, + }, "Konica-Minolta Dynax 5D": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3074.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3074.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2049.0 - }, + }, "Konica-Minolta Dynax 7D": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3077.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3077.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2051.0 - }, + }, "Konica-Minolta e-mini": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 632.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 632.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Konica-Minolta e-mini D": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 632.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 632.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Konica-Minolta e-mini M": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 632.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 632.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Kyocera Finecam 3300": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Kyocera Finecam L3": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Kyocera Finecam L30": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Kyocera Finecam L3v": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Kyocera Finecam L4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kyocera Finecam L4v": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2343.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2343.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1762.0 - }, + }, "Kyocera Finecam M400R": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kyocera Finecam M410R": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Kyocera Finecam S3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Kyocera Finecam S3L": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Kyocera Finecam S3R": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2054.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2054.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1544.0 - }, + }, "Kyocera Finecam S3X": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Kyocera Finecam S4": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2343.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2343.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1762.0 - }, + }, "Kyocera Finecam S5": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2629.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2629.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Kyocera Finecam S5R": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Kyocera Finecam SL300R": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Kyocera Finecam SL400R": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Leica X1": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4263.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4263.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2861.0 - }, + }, "Leica C (Typ112)": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Leica C-LUX 1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Leica C-LUX 2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Leica C-LUX 3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Leica D-LUX": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Leica D-LUX 2": { - "Sensor height (mm)": 5.81, - "Sensor width (mm)": 7.76, - "Sensor res. (width)": 3335.0, - "Sensor": "1/1.65\" (~ 7.76 x 5.81 mm)", + "Sensor height (mm)": 5.81, + "Sensor width (mm)": 7.76, + "Sensor res. (width)": 3335.0, + "Sensor": "1/1.65\" (~ 7.76 x 5.81 mm)", "Sensor res. (height)": 2489.0 - }, + }, "Leica D-LUX 3": { - "Sensor height (mm)": 5.81, - "Sensor width (mm)": 7.76, - "Sensor res. (width)": 3661.0, - "Sensor": "1/1.65\" (~ 7.76 x 5.81 mm)", + "Sensor height (mm)": 5.81, + "Sensor width (mm)": 7.76, + "Sensor res. (width)": 3661.0, + "Sensor": "1/1.65\" (~ 7.76 x 5.81 mm)", "Sensor res. (height)": 2732.0 - }, + }, "Leica D-LUX 4": { - "Sensor height (mm)": 5.89, - "Sensor width (mm)": 7.85, - "Sensor res. (width)": 3665.0, - "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", + "Sensor height (mm)": 5.89, + "Sensor width (mm)": 7.85, + "Sensor res. (width)": 3665.0, + "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Leica D-LUX 5": { - "Sensor height (mm)": 5.89, - "Sensor width (mm)": 7.85, - "Sensor res. (width)": 3665.0, - "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", + "Sensor height (mm)": 5.89, + "Sensor width (mm)": 7.85, + "Sensor res. (width)": 3665.0, + "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Leica D-Lux 6": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3678.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3678.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2745.0 - }, + }, "Leica Digilux": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Leica Digilux 1": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2286.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2286.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1706.0 - }, + }, "Leica Digilux 2": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 2552.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 2552.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Leica Digilux 3": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 3137.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 3137.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 2359.0 - }, + }, "Leica Digilux 4.3": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1793.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1793.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1338.0 - }, + }, "Leica Digilux Zoom": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Leica M Typ 240": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 6000.0, - "Sensor": "36 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 6000.0, + "Sensor": "36 x 24 mm", "Sensor res. (height)": 4000.0 - }, + }, "Leica M-E Typ 220": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 35.8, - "Sensor res. (width)": 5196.0, - "Sensor": "35.8 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 35.8, + "Sensor res. (width)": 5196.0, + "Sensor": "35.8 x 23.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Leica M-Monochrom": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 35.8, - "Sensor res. (width)": 5196.0, - "Sensor": "35.8 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 35.8, + "Sensor res. (width)": 5196.0, + "Sensor": "35.8 x 23.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Leica M-P": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 6000.0, - "Sensor": "36 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 6000.0, + "Sensor": "36 x 24 mm", "Sensor res. (height)": 4000.0 - }, + }, "Leica M8": { - "Sensor height (mm)": 18.0, - "Sensor width (mm)": 27.0, - "Sensor res. (width)": 3930.0, - "Sensor": "27 x 18 mm", + "Sensor height (mm)": 18.0, + "Sensor width (mm)": 27.0, + "Sensor res. (width)": 3930.0, + "Sensor": "27 x 18 mm", "Sensor res. (height)": 2620.0 - }, + }, "Leica M8.2": { - "Sensor height (mm)": 18.0, - "Sensor width (mm)": 27.0, - "Sensor res. (width)": 3930.0, - "Sensor": "27 x 18 mm", + "Sensor height (mm)": 18.0, + "Sensor width (mm)": 27.0, + "Sensor res. (width)": 3930.0, + "Sensor": "27 x 18 mm", "Sensor res. (height)": 2620.0 - }, + }, "Leica M9": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 35.8, - "Sensor res. (width)": 5196.0, - "Sensor": "35.8 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 35.8, + "Sensor res. (width)": 5196.0, + "Sensor": "35.8 x 23.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Leica M9 Titanium": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 35.8, - "Sensor res. (width)": 5196.0, - "Sensor": "35.8 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 35.8, + "Sensor res. (width)": 5196.0, + "Sensor": "35.8 x 23.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Leica M9-P": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 35.8, - "Sensor res. (width)": 5196.0, - "Sensor": "35.8 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 35.8, + "Sensor res. (width)": 5196.0, + "Sensor": "35.8 x 23.9 mm", "Sensor res. (height)": 3464.0 - }, + }, "Leica S (Type 007)": { - "Sensor height (mm)": 30.0, - "Sensor width (mm)": 45.0, - "Sensor res. (width)": 7500.0, - "Sensor": "45 x 30 mm", + "Sensor height (mm)": 30.0, + "Sensor width (mm)": 45.0, + "Sensor res. (width)": 7500.0, + "Sensor": "45 x 30 mm", "Sensor res. (height)": 5000.0 - }, + }, "Leica S-E": { - "Sensor height (mm)": 30.0, - "Sensor width (mm)": 45.0, - "Sensor res. (width)": 7500.0, - "Sensor": "45 x 30 mm", + "Sensor height (mm)": 30.0, + "Sensor width (mm)": 45.0, + "Sensor res. (width)": 7500.0, + "Sensor": "45 x 30 mm", "Sensor res. (height)": 5000.0 - }, + }, "Leica S2": { - "Sensor height (mm)": 30.0, - "Sensor width (mm)": 45.0, - "Sensor res. (width)": 7500.0, - "Sensor": "45 x 30 mm", + "Sensor height (mm)": 30.0, + "Sensor width (mm)": 45.0, + "Sensor res. (width)": 7500.0, + "Sensor": "45 x 30 mm", "Sensor res. (height)": 5000.0 - }, + }, "Leica T (Typ 701)": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4944.0, - "Sensor": "23.6 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4944.0, + "Sensor": "23.6 x 15.7 mm", "Sensor res. (height)": 3296.0 - }, + }, "Leica V-LUX 1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Leica V-LUX 2": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Leica V-LUX 20": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Leica V-LUX 3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Leica V-LUX 30": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Leica V-Lux 4": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Leica V-Lux 40": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Leica X (Typ 113)": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4929.0, - "Sensor": "23.6 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4929.0, + "Sensor": "23.6 x 15.7 mm", "Sensor res. (height)": 3286.0 - }, + }, "Leica X Vario": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4929.0, - "Sensor": "23.6 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4929.0, + "Sensor": "23.6 x 15.7 mm", "Sensor res. (height)": 3286.0 - }, + }, "Leica X-E": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4929.0, - "Sensor": "23.6 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4929.0, + "Sensor": "23.6 x 15.7 mm", "Sensor res. (height)": 3286.0 - }, + }, "Leica X2": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4913.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4913.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 3297.0 - }, + }, "Minolta DiMAGE 2300": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1755.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1755.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1310.0 - }, + }, "Minolta DiMAGE 2330": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1755.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1755.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1310.0 - }, + }, "Minolta DiMAGE 5": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Minolta DiMAGE 7": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 2640.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 2640.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1985.0 - }, + }, "Minolta DiMAGE 7Hi": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 2640.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 2640.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1985.0 - }, + }, "Minolta DiMAGE 7i": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 2640.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 2640.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1985.0 - }, + }, "Minolta DiMAGE A1": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 2655.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 2655.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1996.0 - }, + }, "Minolta DiMAGE E201": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1755.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1755.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1310.0 - }, + }, "Minolta DiMAGE E203": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1749.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1749.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1315.0 - }, + }, "Minolta DiMAGE E223": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Minolta DiMAGE E323": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Minolta DiMAGE EX 1500 Wide": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1365.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1365.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1026.0 - }, + }, "Minolta DiMAGE EX 1500 Zoom": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1365.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1365.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1026.0 - }, + }, "Minolta DiMAGE F100": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Minolta DiMAGE F200": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Minolta DiMAGE F300": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2640.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2640.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1985.0 - }, + }, "Minolta DiMAGE G400": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Minolta DiMAGE G500": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2680.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2680.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2015.0 - }, + }, "Minolta DiMAGE S304": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Minolta DiMAGE S404": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Minolta DiMAGE S414": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Minolta DiMAGE X": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Minolta DiMAGE X20": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1678.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1678.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1252.0 - }, + }, "Minolta DiMAGE Xi": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Minolta DiMAGE Xt": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Minolta DiMAGE Z1": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Minolta RD-3000": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1895.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1895.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1425.0 - }, + }, "Minox Classic Leica M3 2.1": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Minox Classic Leica M3 3MP": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Minox Classic Leica M3 4MP": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Minox Classic Leica M3 5MP": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Minox DC 1011": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3678.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3678.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2745.0 - }, + }, "Minox DC 1011 Carat": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3678.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3678.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2745.0 - }, + }, "Minox DC 1022": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3678.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3678.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2745.0 - }, + }, "Minox DC 1033": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Minox DC 1044": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Minox DC 1055": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Minox DC 1211": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Minox DC 1222": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Minox DC 1233": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Minox DC 1311": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 989.0 - }, + }, "Minox DC 1422": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Minox DC 2111": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Minox DC 2122": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Minox DC 2133": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1682.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1682.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1255.0 - }, + }, "Minox DC 3311": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2063.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2063.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Minox DC 4011": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Minox DC 4211": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2363.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2363.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1777.0 - }, + }, "Minox DC 5011": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Minox DC 5211": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2640.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2640.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1985.0 - }, + }, "Minox DC 5222": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2629.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2629.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Minox DC 6011": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Minox DC 6033 WP": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Minox DC 6211": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2871.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2871.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2159.0 - }, + }, "Minox DC 6311": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2909.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2909.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2187.0 - }, + }, "Minox DC 7011": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3086.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3086.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2320.0 - }, + }, "Minox DC 7022": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Minox DC 7411": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Minox DC 8011": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Minox DC 8022 WP": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Minox DC 8111": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Minox DC 8122": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Minox DC 9011 WP": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3459.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3459.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2601.0 - }, + }, "Minox DCC 14.0": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Minox DCC 5.0 White Edition": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Minox DCC 5.1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Minox DCC Leica M3 5MP Gold": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Minox DCC Rolleiflex AF 5.0": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1998.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1998.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1502.0 - }, + }, "Minox DD1": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Minox DD1 Diamond": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Minox DD100": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Minox DD200": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Minox DM 1": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Minox Mobi DV": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Minox Rolleiflex MiniDigi": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Nikon 1 AW1": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 4616.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 4616.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3077.0 - }, + }, "Nikon 1 J1": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 3893.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 3893.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 2595.0 - }, + }, "Nikon 1 J2": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 3893.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 3893.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 2595.0 - }, + }, "Nikon 1 J3": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 4616.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 4616.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3077.0 - }, + }, "Nikon 1 J4": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 5253.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 5253.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3502.0 - }, + }, "Nikon 1 S1": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 3893.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 3893.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 2595.0 - }, + }, "Nikon 1 S2": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 4616.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 4616.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3077.0 - }, + }, "Nikon 1 V1": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 3893.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 3893.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 2595.0 - }, + }, "Nikon 1 V2": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 4616.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 4616.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3077.0 - }, + }, "Nikon 1 V3": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 5253.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 5253.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3502.0 - }, + }, "Nikon Coolpix 100": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 632.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 632.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Nikon Coolpix 2000": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Nikon Coolpix 2100": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1596.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1596.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1191.0 - }, + }, "Nikon Coolpix 2200": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1596.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1596.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1191.0 - }, + }, "Nikon Coolpix 2500": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Nikon Coolpix 300": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 632.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 632.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Nikon Coolpix 3100": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Nikon Coolpix 3200": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Nikon Coolpix 3500": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Nikon Coolpix 3700": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Nikon Coolpix 4100": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Nikon Coolpix 4200": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Nikon Coolpix 4300": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Nikon Coolpix 4500": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Nikon Coolpix 4600": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Nikon Coolpix 4800": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Nikon Coolpix 5000": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 2552.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 2552.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Nikon Coolpix 5200": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Nikon Coolpix 5400": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Nikon Coolpix 5600": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Nikon Coolpix 5700": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 2552.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 2552.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Nikon Coolpix 5900": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2604.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2604.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Nikon Coolpix 600": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1032.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1032.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 776.0 - }, + }, "Nikon Coolpix 700": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Nikon Coolpix 7600": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Nikon Coolpix 775": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Nikon Coolpix 7900": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Nikon Coolpix 800": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Nikon Coolpix 8400": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3262.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3262.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix 8700": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3262.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3262.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix 880": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Nikon Coolpix 8800": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3262.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3262.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix 885": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Nikon Coolpix 900": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Nikon Coolpix 900s": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Nikon Coolpix 910": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Nikon Coolpix 950": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Nikon Coolpix 990": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Nikon Coolpix 995": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Nikon Coolpix A": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4945.0, - "Sensor": "23.6 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4945.0, + "Sensor": "23.6 x 15.6 mm", "Sensor res. (height)": 3275.0 - }, + }, "Nikon Coolpix AW100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix AW100s": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix AW110": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix AW120": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix L1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Nikon Coolpix L10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Nikon Coolpix L100": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Nikon Coolpix L101": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2910.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2910.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2188.0 - }, + }, "Nikon Coolpix L11": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Nikon Coolpix L110": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Nikon Coolpix L12": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Nikon Coolpix L120": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Nikon Coolpix L14": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Nikon Coolpix L15": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix L16": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Nikon Coolpix L18": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix L19": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix L20": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Nikon Coolpix L21": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix L22": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Nikon Coolpix L23": { - "Sensor height (mm)": 3.72, - "Sensor width (mm)": 4.96, - "Sensor res. (width)": 3708.0, - "Sensor": "1/2.9\" (~ 4.96 x 3.72 mm)", + "Sensor height (mm)": 3.72, + "Sensor width (mm)": 4.96, + "Sensor res. (width)": 3708.0, + "Sensor": "1/2.9\" (~ 4.96 x 3.72 mm)", "Sensor res. (height)": 2788.0 - }, + }, "Nikon Coolpix L24": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Nikon Coolpix L25": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 3665.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 3665.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Nikon Coolpix L26": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Nikon Coolpix L27": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Nikon Coolpix L28": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Nikon Coolpix L29": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Nikon Coolpix L30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Nikon Coolpix L310": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Nikon Coolpix L320": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Nikon Coolpix L330": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5183.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5183.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3897.0 - }, + }, "Nikon Coolpix L5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Nikon Coolpix L6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Nikon Coolpix L610": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix L620": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4906.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4906.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3689.0 - }, + }, "Nikon Coolpix L810": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix L820": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix L830": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix P1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix P100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Nikon Coolpix P2": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Nikon Coolpix P3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix P300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Nikon Coolpix P310": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix P330": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4043.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4043.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3017.0 - }, + }, "Nikon Coolpix P340": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4043.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4043.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3017.0 - }, + }, "Nikon Coolpix P4": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix P50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Nikon Coolpix P500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Nikon Coolpix P5000": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Nikon Coolpix P510": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Nikon Coolpix P5100": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4011.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4011.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Nikon Coolpix P520": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4906.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4906.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3689.0 - }, + }, "Nikon Coolpix P530": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Nikon Coolpix P60": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Nikon Coolpix P600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Nikon Coolpix P6000": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4237.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4237.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3186.0 - }, + }, "Nikon Coolpix P7000": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3678.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3678.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2745.0 - }, + }, "Nikon Coolpix P7100": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3678.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3678.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2745.0 - }, + }, "Nikon Coolpix P7700": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4043.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4043.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3017.0 - }, + }, "Nikon Coolpix P7800": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4043.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4043.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3017.0 - }, + }, "Nikon Coolpix P80": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Nikon Coolpix P90": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Nikon Coolpix S01": { - "Sensor height (mm)": 3.72, - "Sensor width (mm)": 4.96, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.9\" (~ 4.96 x 3.72 mm)", + "Sensor height (mm)": 3.72, + "Sensor width (mm)": 4.96, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.9\" (~ 4.96 x 3.72 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Nikon Coolpix S02": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 4190.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 4190.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 3150.0 - }, + }, "Nikon Coolpix S1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Nikon Coolpix S10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Nikon Coolpix S100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S1000pj": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Nikon Coolpix S1100pj": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Nikon Coolpix S1200pj": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Nikon Coolpix S2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Nikon Coolpix S200": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Nikon Coolpix S210": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix S220": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Nikon Coolpix S225": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3708.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3708.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2788.0 - }, + }, "Nikon Coolpix S230": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Nikon Coolpix S2500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4059.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4059.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3052.0 - }, + }, "Nikon Coolpix S2600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4389.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4389.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3300.0 - }, + }, "Nikon Coolpix S2700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S2750": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S2800 ": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Nikon Coolpix S3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Nikon Coolpix S30": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 3647.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 3647.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Nikon Coolpix S3000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Nikon Coolpix S31": { - "Sensor height (mm)": 3.72, - "Sensor width (mm)": 4.96, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.9\" (~ 4.96 x 3.72 mm)", + "Sensor height (mm)": 3.72, + "Sensor width (mm)": 4.96, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.9\" (~ 4.96 x 3.72 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Nikon Coolpix S3100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Nikon Coolpix S32": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 4190.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 4190.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 3150.0 - }, + }, "Nikon Coolpix S3200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S3300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S3400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Nikon Coolpix S3500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Nikon Coolpix S3600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Nikon Coolpix S4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Nikon Coolpix S4000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Nikon Coolpix S4100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Nikon Coolpix S4150": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Nikon Coolpix S4200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S4300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S4400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Nikon Coolpix S5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Nikon Coolpix S50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Nikon Coolpix S500": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Nikon Coolpix S50c": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Nikon Coolpix S51": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix S510": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Nikon Coolpix S5100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Nikon Coolpix S51c": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix S52": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3459.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3459.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2601.0 - }, + }, "Nikon Coolpix S520": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Nikon Coolpix S5200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S52c": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3459.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3459.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2601.0 - }, + }, "Nikon Coolpix S5300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S550": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Nikon Coolpix S560": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Nikon Coolpix S570": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Nikon Coolpix S6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Nikon Coolpix S60": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Nikon Coolpix S600": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Nikon Coolpix S6000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Nikon Coolpix S610": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Nikon Coolpix S6100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S610c": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Nikon Coolpix S6150": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S620": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Nikon Coolpix S6200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S630": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Nikon Coolpix S6300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S640": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Nikon Coolpix S6400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S6500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S6600 ": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S6700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Nikon Coolpix S6800": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S6900": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S70": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Nikon Coolpix S700": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4011.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4011.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Nikon Coolpix S710": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4392.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4392.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Nikon Coolpix S7c": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Nikon Coolpix S80": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Nikon Coolpix S8000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Nikon Coolpix S800c": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S8100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Nikon Coolpix S810c": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S8200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S9": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Nikon Coolpix S9050": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Nikon Coolpix S9100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Nikon Coolpix S9200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S9300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S9400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4906.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4906.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3689.0 - }, + }, "Nikon Coolpix S9500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4906.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4906.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3689.0 - }, + }, "Nikon Coolpix S9600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix S9700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Nikon Coolpix SQ": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Nikon D1": { - "Sensor height (mm)": 15.5, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 1995.0, - "Sensor": "23.7 x 15.5 mm", + "Sensor height (mm)": 15.5, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 1995.0, + "Sensor": "23.7 x 15.5 mm", "Sensor res. (height)": 1304.0 - }, + }, "Nikon D100": { - "Sensor height (mm)": 15.5, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 3029.0, - "Sensor": "23.7 x 15.5 mm", + "Sensor height (mm)": 15.5, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 3029.0, + "Sensor": "23.7 x 15.5 mm", "Sensor res. (height)": 1980.0 - }, + }, "Nikon D1H": { - "Sensor height (mm)": 15.5, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 1995.0, - "Sensor": "23.7 x 15.5 mm", + "Sensor height (mm)": 15.5, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 1995.0, + "Sensor": "23.7 x 15.5 mm", "Sensor res. (height)": 1304.0 - }, + }, "Nikon D1X": { - "Sensor height (mm)": 15.5, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 2847.0, - "Sensor": "23.7 x 15.5 mm", + "Sensor height (mm)": 15.5, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 2847.0, + "Sensor": "23.7 x 15.5 mm", "Sensor res. (height)": 1861.0 - }, + }, "Nikon D200": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 3898.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 3898.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2616.0 - }, + }, "Nikon D2H": { - "Sensor height (mm)": 15.5, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 2474.0, - "Sensor": "23.7 x 15.5 mm", + "Sensor height (mm)": 15.5, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 2474.0, + "Sensor": "23.7 x 15.5 mm", "Sensor res. (height)": 1617.0 - }, + }, "Nikon D2Hs": { - "Sensor height (mm)": 15.4, - "Sensor width (mm)": 23.2, - "Sensor res. (width)": 2488.0, - "Sensor": "23.2 x 15.4 mm", + "Sensor height (mm)": 15.4, + "Sensor width (mm)": 23.2, + "Sensor res. (width)": 2488.0, + "Sensor": "23.2 x 15.4 mm", "Sensor res. (height)": 1648.0 - }, + }, "Nikon D2X": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 4291.0, - "Sensor": "23.7 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 4291.0, + "Sensor": "23.7 x 15.7 mm", "Sensor res. (height)": 2842.0 - }, + }, "Nikon D2xs": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 4328.0, - "Sensor": "23.7 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 4328.0, + "Sensor": "23.7 x 15.7 mm", "Sensor res. (height)": 2866.0 - }, + }, "Nikon D3": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 4275.0, - "Sensor": "36 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 4275.0, + "Sensor": "36 x 23.9 mm", "Sensor res. (height)": 2831.0 - }, + }, "Nikon D300": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4281.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4281.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2873.0 - }, + }, "Nikon D3000": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 3898.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 3898.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2616.0 - }, + }, "Nikon D300s": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4281.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4281.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2873.0 - }, + }, "Nikon D3100": { - "Sensor height (mm)": 15.4, - "Sensor width (mm)": 23.1, - "Sensor res. (width)": 4616.0, - "Sensor": "23.1 x 15.4 mm", + "Sensor height (mm)": 15.4, + "Sensor width (mm)": 23.1, + "Sensor res. (width)": 4616.0, + "Sensor": "23.1 x 15.4 mm", "Sensor res. (height)": 3077.0 - }, + }, "Nikon D3200": { - "Sensor height (mm)": 15.4, - "Sensor width (mm)": 23.2, - "Sensor res. (width)": 6045.0, - "Sensor": "23.2 x 15.4 mm", + "Sensor height (mm)": 15.4, + "Sensor width (mm)": 23.2, + "Sensor res. (width)": 6045.0, + "Sensor": "23.2 x 15.4 mm", "Sensor res. (height)": 4003.0 - }, + }, "Nikon D3300": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 6045.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 6045.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 4003.0 - }, + }, "Nikon D3X": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 35.9, - "Sensor res. (width)": 6062.0, - "Sensor": "35.9 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 35.9, + "Sensor res. (width)": 6062.0, + "Sensor": "35.9 x 24 mm", "Sensor res. (height)": 4041.0 - }, + }, "Nikon D3s": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 4275.0, - "Sensor": "36 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 4275.0, + "Sensor": "36 x 23.9 mm", "Sensor res. (height)": 2831.0 - }, + }, "Nikon D4": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 4945.0, - "Sensor": "36 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 4945.0, + "Sensor": "36 x 23.9 mm", "Sensor res. (height)": 3275.0 - }, + }, "Nikon D40": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 3045.0, - "Sensor": "23.7 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 3045.0, + "Sensor": "23.7 x 15.6 mm", "Sensor res. (height)": 2003.0 - }, + }, "Nikon D40x": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 3898.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 3898.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2616.0 - }, + }, "Nikon D4s": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 4945.0, - "Sensor": "36 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 4945.0, + "Sensor": "36 x 23.9 mm", "Sensor res. (height)": 3275.0 - }, + }, "Nikon D50": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 3045.0, - "Sensor": "23.7 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 3045.0, + "Sensor": "23.7 x 15.6 mm", "Sensor res. (height)": 2003.0 - }, + }, "Nikon D5000": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4281.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4281.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2873.0 - }, + }, "Nikon D5100": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4945.0, - "Sensor": "23.6 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4945.0, + "Sensor": "23.6 x 15.6 mm", "Sensor res. (height)": 3275.0 - }, + }, "Nikon D5200": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 6032.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 6032.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3995.0 - }, + }, "Nikon D5300": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 6045.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 6045.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 4003.0 - }, + }, "Nikon D60": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 3898.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 3898.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2616.0 - }, + }, "Nikon D600": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 35.9, - "Sensor res. (width)": 6038.0, - "Sensor": "35.9 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 35.9, + "Sensor res. (width)": 6038.0, + "Sensor": "35.9 x 24 mm", "Sensor res. (height)": 4025.0 - }, + }, "Nikon D610": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 35.9, - "Sensor res. (width)": 6038.0, - "Sensor": "35.9 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 35.9, + "Sensor res. (width)": 6038.0, + "Sensor": "35.9 x 24 mm", "Sensor res. (height)": 4025.0 - }, + }, "Nikon D70": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 3045.0, - "Sensor": "23.7 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 3045.0, + "Sensor": "23.7 x 15.6 mm", "Sensor res. (height)": 2003.0 - }, + }, "Nikon D700": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 4275.0, - "Sensor": "36 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 4275.0, + "Sensor": "36 x 23.9 mm", "Sensor res. (height)": 2831.0 - }, + }, "Nikon D7000": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4945.0, - "Sensor": "23.6 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4945.0, + "Sensor": "23.6 x 15.6 mm", "Sensor res. (height)": 3275.0 - }, + }, "Nikon D70s": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 3045.0, - "Sensor": "23.7 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 3045.0, + "Sensor": "23.7 x 15.6 mm", "Sensor res. (height)": 2003.0 - }, + }, "Nikon D7100": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 6032.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 6032.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3995.0 - }, + }, "Nikon D750": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 35.9, - "Sensor res. (width)": 6038.0, - "Sensor": "35.9 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 35.9, + "Sensor res. (width)": 6038.0, + "Sensor": "35.9 x 24 mm", "Sensor res. (height)": 4025.0 - }, + }, "Nikon D80": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 3898.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 3898.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2616.0 - }, + }, "Nikon D800": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 35.9, - "Sensor res. (width)": 7379.0, - "Sensor": "35.9 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 35.9, + "Sensor res. (width)": 7379.0, + "Sensor": "35.9 x 24 mm", "Sensor res. (height)": 4919.0 - }, + }, "Nikon D800E": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 35.9, - "Sensor res. (width)": 7379.0, - "Sensor": "35.9 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 35.9, + "Sensor res. (width)": 7379.0, + "Sensor": "35.9 x 24 mm", "Sensor res. (height)": 4919.0 - }, + }, "Nikon D810": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 35.9, - "Sensor res. (width)": 7379.0, - "Sensor": "35.9 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 35.9, + "Sensor res. (width)": 7379.0, + "Sensor": "35.9 x 24 mm", "Sensor res. (height)": 4919.0 - }, + }, "Nikon D90": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4281.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4281.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2873.0 - }, + }, "Nikon Df": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 36.0, - "Sensor res. (width)": 4945.0, - "Sensor": "36 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 36.0, + "Sensor res. (width)": 4945.0, + "Sensor": "36 x 23.9 mm", "Sensor res. (height)": 3275.0 - }, + }, "Nikon E2Ns": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1365.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1365.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1026.0 - }, + }, "Nikon E2n": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1365.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1365.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1026.0 - }, + }, "Nikon E2s": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1365.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1365.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1026.0 - }, + }, "Nikon E3": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1365.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1365.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1026.0 - }, + }, "Nikon E3s": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1365.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1365.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1026.0 - }, + }, "Nokia 808 PureView": { - "Sensor height (mm)": 7.52, - "Sensor width (mm)": 10.82, - "Sensor res. (width)": 7728.0, - "Sensor": "10.82 x 7.52 mm", + "Sensor height (mm)": 7.52, + "Sensor width (mm)": 10.82, + "Sensor res. (width)": 7728.0, + "Sensor": "10.82 x 7.52 mm", "Sensor res. (height)": 5367.0 - }, + }, "Nokia Lumia 1020": { - "Sensor height (mm)": 6.0, - "Sensor width (mm)": 8.64, - "Sensor res. (width)": 7714.0, - "Sensor": "8.64 x 6 mm", + "Sensor height (mm)": 6.0, + "Sensor width (mm)": 8.64, + "Sensor res. (width)": 7714.0, + "Sensor": "8.64 x 6 mm", "Sensor res. (height)": 5357.0 - }, + }, "Olympus ": { - "Sensor height (mm)": 2.4, - "Sensor width (mm)": 3.2, - "Sensor res. (width)": 1315.0, - "Sensor": "1/4\" (~ 3.20 x 2.40 mm)", + "Sensor height (mm)": 2.4, + "Sensor width (mm)": 3.2, + "Sensor res. (width)": 1315.0, + "Sensor": "1/4\" (~ 3.20 x 2.40 mm)", "Sensor res. (height)": 989.0 - }, + }, "Olympus E-300 / EVOLT E-300": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 3262.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 3262.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus AZ-1": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Olympus AZ-1 Ferrari 2004": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Olympus AZ-2 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Olympus C-1": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1268.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1268.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 946.0 - }, + }, "Olympus C-1 Zoom": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1320.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1320.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 985.0 - }, + }, "Olympus C-100": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1320.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1320.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 985.0 - }, + }, "Olympus C-1000L": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 799.0 - }, + }, "Olympus C-120": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1682.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1682.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1255.0 - }, + }, "Olympus C-1400L": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1365.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1365.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1026.0 - }, + }, "Olympus C-1400XL": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1365.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1365.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1026.0 - }, + }, "Olympus C-150": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1637.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1637.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1222.0 - }, + }, "Olympus C-160": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2095.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2095.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1575.0 - }, + }, "Olympus C-170": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Olympus C-180": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Olympus C-2": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Olympus C-200 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Olympus C-2000 Zoom": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Olympus C-2020 Zoom": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Olympus C-2040 Zoom": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Olympus C-21": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Olympus C-2100 UZ": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Olympus C-220 Zoom": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1682.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1682.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1255.0 - }, + }, "Olympus C-2500 L": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1749.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1749.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1315.0 - }, + }, "Olympus C-300 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 1998.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 1998.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1502.0 - }, + }, "Olympus C-3000 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Olympus C-3020 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Olympus C-3030 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Olympus C-3040 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Olympus C-310 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2127.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2127.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1599.0 - }, + }, "Olympus C-315 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2671.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2671.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2008.0 - }, + }, "Olympus C-350 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Olympus C-360 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1575.0 - }, + }, "Olympus C-370 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Olympus C-40 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2343.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2343.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1762.0 - }, + }, "Olympus C-4000 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Olympus C-4040 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Olympus C-450 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Olympus C-460 Zoom del Sol": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Olympus C-470 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Olympus C-480 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2324.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2324.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1747.0 - }, + }, "Olympus C-50 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2552.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2552.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Olympus C-5000 Zoom": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 2552.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 2552.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Olympus C-5050 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2552.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2552.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Olympus C-5060 Wide Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Olympus C-55 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2655.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2655.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1996.0 - }, + }, "Olympus C-5500 Sport Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Olympus C-60 Zoom": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 2801.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 2801.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 2106.0 - }, + }, "Olympus C-70 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3137.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3137.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2359.0 - }, + }, "Olympus C-700 UZ": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Olympus C-7000 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus C-7070 Wide Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus C-720 UZ": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 1998.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 1998.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1502.0 - }, + }, "Olympus C-730 UZ": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1998.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1998.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1502.0 - }, + }, "Olympus C-740 UZ": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Olympus C-750 UZ": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Olympus C-760 UZ": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Olympus C-765 UZ": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Olympus C-770 UZ": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Olympus C-8080 Wide Zoom": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3262.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3262.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus C-820L": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1037.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1037.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 780.0 - }, + }, "Olympus C-840L": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1319.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1319.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 992.0 - }, + }, "Olympus C-860L": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1319.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1319.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 992.0 - }, + }, "Olympus C-900 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1319.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1319.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 992.0 - }, + }, "Olympus C-920 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1319.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1319.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 992.0 - }, + }, "Olympus C-960 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1319.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1319.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 992.0 - }, + }, "Olympus C-990 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Olympus D-150Z": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1268.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1268.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 946.0 - }, + }, "Olympus D-200L": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 632.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 632.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Olympus D-300L": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1032.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1032.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 776.0 - }, + }, "Olympus D-340L": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1264.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1264.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 950.0 - }, + }, "Olympus D-340R": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 950.0 - }, + }, "Olympus D-360L": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1264.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1264.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 950.0 - }, + }, "Olympus D-370": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1268.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1268.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 946.0 - }, + }, "Olympus D-380": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1596.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1596.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1191.0 - }, + }, "Olympus D-390": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1596.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1596.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1191.0 - }, + }, "Olympus D-395": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Olympus D-40 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Olympus D-400 Zoom": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 950.0 - }, + }, "Olympus D-425": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Olympus D-435": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Olympus D-450 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Olympus D-460 Zoom": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1264.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1264.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 950.0 - }, + }, "Olympus D-490 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Olympus D-500L": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1032.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1032.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 776.0 - }, + }, "Olympus D-510 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Olympus D-520 Zoom": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1596.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1596.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1191.0 - }, + }, "Olympus D-535 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Olympus D-540 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Olympus D-545 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Olympus D-560 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Olympus D-580 Zoom": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Olympus D-595 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Olympus D-600L": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1315.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1315.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 989.0 - }, + }, "Olympus D-620L": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1315.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1315.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 989.0 - }, + }, "Olympus D-630 Zoom": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Olympus E-1": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 2552.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 2552.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Olympus E-10": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 2218.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 2218.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1668.0 - }, + }, "Olympus E-100 RS": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Olympus E-20": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 2552.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 2552.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Olympus E-3": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 3665.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 3665.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Olympus E-30": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4045.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4045.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Olympus E-400": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 3647.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 3647.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus E-410 / EVOLT E-410": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 3647.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 3647.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus E-420": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 3647.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 3647.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus E-450": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 3647.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 3647.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus E-5": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4045.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4045.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Olympus E-500 / EVOLT E-500": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 3262.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 3262.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus E-510 / EVOLT E-510": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 3647.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 3647.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus E-520": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 3647.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 3647.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus E-600": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4045.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4045.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Olympus E-620": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4045.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4045.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Olympus FE-100": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Olympus FE-110": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Olympus FE-115": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Olympus FE-120": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Olympus FE-130": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Olympus FE-140": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Olympus FE-150": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Olympus FE-160": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2871.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2871.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2159.0 - }, + }, "Olympus FE-170": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Olympus FE-180": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Olympus FE-190": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Olympus FE-20": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus FE-200": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Olympus FE-210": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus FE-220": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3137.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3137.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2359.0 - }, + }, "Olympus FE-230": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus FE-240": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus FE-25": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus FE-250": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus FE-26": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4110.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4110.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3090.0 - }, + }, "Olympus FE-270": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus FE-280": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus FE-290": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus FE-300": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 3995.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 3995.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus FE-3000": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus FE-3010": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus FE-310": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus FE-340": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus FE-350": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus FE-360": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus FE-370": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus FE-4000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus FE-4010": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus FE-4020": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus FE-4030": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus FE-4040": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus FE-4050": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Olympus FE-45": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus FE-47": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus FE-48": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus FE-5000": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus FE-5010": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus FE-5020": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus FE-5030": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus FE-5035": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus FE-5040": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus FE-5050": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus IR 500": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Olympus IR-300": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Olympus Mju 1060": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Olympus Mju 5000": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4110.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4110.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3090.0 - }, + }, "Olympus Mju 7050": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus OM-D E-M1": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4656.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4656.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3501.0 - }, + }, "Olympus OM-D E-M10": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4627.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4627.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Olympus OM-D E-M5": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4627.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4627.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Olympus PEN E-P1": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4045.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4045.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Olympus PEN E-P2": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4045.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4045.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Olympus PEN E-P3": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4045.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4045.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Olympus PEN E-P5": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4627.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4627.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Olympus PEN E-PL1": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4045.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4045.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Olympus PEN E-PL1s": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4045.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4045.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Olympus PEN E-PL2": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4045.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4045.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Olympus PEN E-PL3": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4045.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4045.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Olympus PEN E-PL5": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4627.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4627.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Olympus PEN E-PL6": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4627.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4627.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Olympus PEN E-PL7": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4627.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4627.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Olympus PEN E-PM1": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4045.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4045.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Olympus PEN E-PM2": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4627.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4627.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Olympus SH-21": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus SH-25MR": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4727.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4727.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3554.0 - }, + }, "Olympus SH-50 iHS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus SP 310": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3139.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3139.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2360.0 - }, + }, "Olympus SP 320": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3139.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3139.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2360.0 - }, + }, "Olympus SP 350": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus SP 500 UZ": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2910.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2910.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2188.0 - }, + }, "Olympus SP 510 UZ": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus SP 550 UZ": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3137.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3137.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2359.0 - }, + }, "Olympus SP 560 UZ": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3362.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3362.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2528.0 - }, + }, "Olympus SP 570 UZ": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3772.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3772.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2836.0 - }, + }, "Olympus SP 590 UZ": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4110.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4110.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3090.0 - }, + }, "Olympus SP 600 UZ": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4078.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4078.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3066.0 - }, + }, "Olympus SP 700": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2867.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2867.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2156.0 - }, + }, "Olympus SP 800 UZ": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4422.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4422.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3325.0 - }, + }, "Olympus SP 810 UZ": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus SP-100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus SP-565UZ": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus SP-610UZ": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus SP-620 UZ": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus SP-720UZ": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4376.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4376.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3290.0 - }, + }, "Olympus SZ-10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus SZ-11": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus SZ-12": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus SZ-14": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Olympus SZ-15": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus SZ-16": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus SZ-20": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus SZ-30MR": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus SZ-31MR iHS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus Stylus 1": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4011.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4011.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2993.0 - }, + }, "Olympus Stylus 1000": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus Stylus 1010": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Olympus Stylus 1020": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Olympus Stylus 1030 SW": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Olympus Stylus 1040": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Olympus Stylus 1050 SW": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Olympus Stylus 1200": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 3995.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 3995.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus Stylus 300": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Olympus Stylus 400": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Olympus Stylus 410": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Olympus Stylus 500": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Olympus Stylus 5010": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus Stylus 550WP": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus Stylus 600": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Olympus Stylus 700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus Stylus 7000": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus Stylus 7010": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus Stylus 7030": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus Stylus 7040": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus Stylus 720 SW": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus Stylus 725 SW": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus Stylus 730": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus Stylus 740": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus Stylus 750": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus Stylus 760": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus Stylus 770 SW": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus Stylus 780": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus Stylus 790 SW": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus Stylus 800": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus Stylus 810": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus Stylus 820": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus Stylus 830": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus Stylus 840": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus Stylus 850 SW": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Olympus Stylus 9000": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus Stylus 9010": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus Stylus SH-1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus Stylus SP-820UZ": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus Stylus Tough 6000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus Stylus Tough 6010": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus Stylus Tough 6020": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4110.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4110.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3090.0 - }, + }, "Olympus Stylus Tough 8000": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus Stylus Tough 8010": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4110.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4110.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3090.0 - }, + }, "Olympus Stylus Tough-3000": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus Stylus Verve": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Olympus Stylus Verve S": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Olympus Stylus XZ-10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus T-10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus T-100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus T-110": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus TG-310": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus TG-320": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus TG-610": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus TG-630 iHS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus TG-810": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus TG-820 iHS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus TG-830 iHS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus TG-850 iHS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus Tough TG-1 iHS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus Tough TG-2 iHS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus Tough TG-3": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus Tough TG-620": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4110.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4110.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3090.0 - }, + }, "Olympus VG-110": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus VG-120": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus VG-130": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Olympus VG-145": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus VG-150": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4110.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4110.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3090.0 - }, + }, "Olympus VG-160": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus VG-165": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus VG-170": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Olympus VG-180": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus VG-190": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus VH-210": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Olympus VH-410": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus VH-510": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus VH-515": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus VH-520": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus VR-310": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Olympus VR-320": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus VR-330": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Olympus VR-340": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus VR-350": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus VR-360": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus VR-370": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Olympus X-15": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3362.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3362.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2528.0 - }, + }, "Olympus X-775": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3137.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3137.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2359.0 - }, + }, "Olympus X-785": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Olympus X-905": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3772.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3772.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2836.0 - }, + }, "Olympus X-920": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Olympus XZ-1": { - "Sensor height (mm)": 5.89, - "Sensor width (mm)": 7.85, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", + "Sensor height (mm)": 5.89, + "Sensor width (mm)": 7.85, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Olympus XZ-2 iHS": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4011.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4011.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2993.0 - }, + }, "Olympus mju 400 Digital Ferrari": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Olympus mju 800 black": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3322.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3322.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2498.0 - }, + }, "Olympus mju mini Digital": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Olympus mju mini Digital S": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2680.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2680.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2015.0 - }, + }, "Panasonic Lumix DMC-LZ20": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic D-snap SV-AS10": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1682.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1682.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1255.0 - }, + }, "Panasonic D-snap SV-AS3": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 2070.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 2070.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1545.0 - }, + }, "Panasonic D-snap SV-AS30": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 2070.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 2070.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1545.0 - }, + }, "Panasonic Lumix DMC-3D1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-F1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Panasonic Lumix DMC-F3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-F5": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-F7": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Panasonic Lumix DMC-FH1": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FH10": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FH2": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FH20": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Panasonic Lumix DMC-FH22": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Panasonic Lumix DMC-FH25": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FH27": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FH3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Panasonic Lumix DMC-FH4": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FH5": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FH6": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FH7": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FH8": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FP1": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FP2": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FP3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FP5": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FP7": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FP8": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FS10": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FS11": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Panasonic Lumix DMC-FS12": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FS15": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FS16": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FS18": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FS2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3133.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3133.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2356.0 - }, + }, "Panasonic Lumix DMC-FS20": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-FS22": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FS25": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FS28": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FS3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Panasonic Lumix DMC-FS30": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Panasonic Lumix DMC-FS33": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Panasonic Lumix DMC-FS35": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FS37": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4699.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4699.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3533.0 - }, + }, "Panasonic Lumix DMC-FS40": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FS42": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-FS45": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FS5": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-FS6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Panasonic Lumix DMC-FS62": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-FS7": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-FT1": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FT10": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FT2": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FT20": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FT3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FT4": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FX01": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Panasonic Lumix DMC-FX07": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Panasonic Lumix DMC-FX1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Panasonic Lumix DMC-FX10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Panasonic Lumix DMC-FX100": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 3995.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 3995.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Panasonic Lumix DMC-FX12": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Panasonic Lumix DMC-FX150": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4422.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4422.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3325.0 - }, + }, "Panasonic Lumix DMC-FX2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Panasonic Lumix DMC-FX3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Panasonic Lumix DMC-FX30": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Panasonic Lumix DMC-FX33": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Panasonic Lumix DMC-FX35": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-FX37": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-FX40": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FX48": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FX5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Panasonic Lumix DMC-FX50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Panasonic Lumix DMC-FX500": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-FX55": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Panasonic Lumix DMC-FX550": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FX580": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FX60": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FX65": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FX66": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FX68": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FX7": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Panasonic Lumix DMC-FX70": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FX700": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FX75": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FX77": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FX78": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FX8": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Panasonic Lumix DMC-FX80": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FX9": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Panasonic Lumix DMC-FX90": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FZ1": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1637.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1637.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1222.0 - }, + }, "Panasonic Lumix DMC-FZ10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Panasonic Lumix DMC-FZ100": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FZ1000": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 5492.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 5492.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3661.0 - }, + }, "Panasonic Lumix DMC-FZ15": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2391.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2391.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1798.0 - }, + }, "Panasonic Lumix DMC-FZ150": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FZ18": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Panasonic Lumix DMC-FZ2": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1637.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1637.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1222.0 - }, + }, "Panasonic Lumix DMC-FZ20": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Panasonic Lumix DMC-FZ200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FZ28": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-FZ3": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 2005.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 2005.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1496.0 - }, + }, "Panasonic Lumix DMC-FZ30": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Panasonic Lumix DMC-FZ35": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FZ38": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FZ4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Panasonic Lumix DMC-FZ40": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FZ45": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-FZ47": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FZ48": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-FZ5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Panasonic Lumix DMC-FZ50": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Panasonic Lumix DMC-FZ60": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FZ7": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Panasonic Lumix DMC-FZ70": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-FZ8": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Panasonic Lumix DMC-G1": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4011.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4011.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-G10": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4011.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4011.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-G2": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4011.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4011.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-G3": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4585.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4585.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3447.0 - }, + }, "Panasonic Lumix DMC-G5": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4620.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4620.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3474.0 - }, + }, "Panasonic Lumix DMC-G6": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4620.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4620.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3474.0 - }, + }, "Panasonic Lumix DMC-GF1": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4011.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4011.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-GF2": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4011.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4011.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-GF3": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4011.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4011.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-GF5": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4011.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4011.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-GF6": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4612.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4612.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Panasonic Lumix DMC-GH1": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4011.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4011.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-GH2": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4620.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4620.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3474.0 - }, + }, "Panasonic Lumix DMC-GH3": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4620.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4620.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3474.0 - }, + }, "Panasonic Lumix DMC-GH4": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4620.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4620.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3474.0 - }, + }, "Panasonic Lumix DMC-GM1": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4612.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4612.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Panasonic Lumix DMC-GM5": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4612.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4612.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Panasonic Lumix DMC-GX1": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4612.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4612.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Panasonic Lumix DMC-GX7": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4612.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4612.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Panasonic Lumix DMC-L1": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 3137.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 3137.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 2359.0 - }, + }, "Panasonic Lumix DMC-L10": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 3647.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 3647.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Panasonic Lumix DMC-LC1": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 2552.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 2552.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Panasonic Lumix DMC-LC20": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Panasonic Lumix DMC-LC33": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Panasonic Lumix DMC-LC40": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2286.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2286.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1706.0 - }, + }, "Panasonic Lumix DMC-LC43": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Panasonic Lumix DMC-LC5": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2286.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2286.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1706.0 - }, + }, "Panasonic Lumix DMC-LC50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Panasonic Lumix DMC-LC70": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Panasonic Lumix DMC-LC80": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Panasonic Lumix DMC-LF1": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Panasonic Lumix DMC-LS1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Panasonic Lumix DMC-LS2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Panasonic Lumix DMC-LS3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Panasonic Lumix DMC-LS5": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-LS6": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-LS60": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Panasonic Lumix DMC-LS75": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Panasonic Lumix DMC-LS80": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Panasonic Lumix DMC-LS85": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Panasonic Lumix DMC-LX1": { - "Sensor height (mm)": 5.81, - "Sensor width (mm)": 7.76, - "Sensor res. (width)": 3335.0, - "Sensor": "1/1.65\" (~ 7.76 x 5.81 mm)", + "Sensor height (mm)": 5.81, + "Sensor width (mm)": 7.76, + "Sensor res. (width)": 3335.0, + "Sensor": "1/1.65\" (~ 7.76 x 5.81 mm)", "Sensor res. (height)": 2489.0 - }, + }, "Panasonic Lumix DMC-LX100": { - "Sensor height (mm)": 13.0, - "Sensor width (mm)": 17.3, - "Sensor res. (width)": 4126.0, - "Sensor": "Four Thirds (17.3 x 13 mm)", + "Sensor height (mm)": 13.0, + "Sensor width (mm)": 17.3, + "Sensor res. (width)": 4126.0, + "Sensor": "Four Thirds (17.3 x 13 mm)", "Sensor res. (height)": 3102.0 - }, + }, "Panasonic Lumix DMC-LX2": { - "Sensor height (mm)": 5.81, - "Sensor width (mm)": 7.76, - "Sensor res. (width)": 3661.0, - "Sensor": "1/1.65\" (~ 7.76 x 5.81 mm)", + "Sensor height (mm)": 5.81, + "Sensor width (mm)": 7.76, + "Sensor res. (width)": 3661.0, + "Sensor": "1/1.65\" (~ 7.76 x 5.81 mm)", "Sensor res. (height)": 2732.0 - }, + }, "Panasonic Lumix DMC-LX3": { - "Sensor height (mm)": 5.89, - "Sensor width (mm)": 7.85, - "Sensor res. (width)": 3665.0, - "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", + "Sensor height (mm)": 5.89, + "Sensor width (mm)": 7.85, + "Sensor res. (width)": 3665.0, + "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-LX5": { - "Sensor height (mm)": 5.89, - "Sensor width (mm)": 7.85, - "Sensor res. (width)": 3665.0, - "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", + "Sensor height (mm)": 5.89, + "Sensor width (mm)": 7.85, + "Sensor res. (width)": 3665.0, + "Sensor": "1/1.63\" (~ 7.85 x 5.89 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-LX7": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3678.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3678.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2745.0 - }, + }, "Panasonic Lumix DMC-LZ1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Panasonic Lumix DMC-LZ10": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-LZ2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Panasonic Lumix DMC-LZ3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Panasonic Lumix DMC-LZ30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-LZ40": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5158.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5158.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3878.0 - }, + }, "Panasonic Lumix DMC-LZ5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Panasonic Lumix DMC-LZ6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Panasonic Lumix DMC-LZ7": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Panasonic Lumix DMC-LZ8": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Panasonic Lumix DMC-S1": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-S2": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-S3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-S5": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-SZ1": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-SZ3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-SZ5": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-SZ7": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-SZ8": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Panasonic Lumix DMC-SZ9": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-TS1": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-TS10": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-TS2": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-TS20": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-TS25": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-TS3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-TS4": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-TS5": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-TZ1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Panasonic Lumix DMC-TZ10": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-TZ18": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-TZ2": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Panasonic Lumix DMC-TZ20": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-TZ22": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-TZ25": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-TZ3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Panasonic Lumix DMC-TZ30": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-TZ31": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-TZ4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Panasonic Lumix DMC-TZ5": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3479.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3479.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Panasonic Lumix DMC-TZ50": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3479.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3479.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Panasonic Lumix DMC-TZ6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-TZ7": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-XS1": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-XS3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-ZR1": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-ZR3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-ZS1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-ZS10": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-ZS15": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-ZS20": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-ZS25": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Panasonic Lumix DMC-ZS3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Panasonic Lumix DMC-ZS30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4906.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4906.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3689.0 - }, + }, "Panasonic Lumix DMC-ZS35 / TZ55": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Panasonic Lumix DMC-ZS40 / TZ60": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4906.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4906.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3689.0 - }, + }, "Panasonic Lumix DMC-ZS5": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-ZS7": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-ZS8": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic Lumix DMC-ZX1": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Panasonic Lumix DMC-ZX3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Panasonic PV DC3000": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Pentax *ist D": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3026.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3026.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2017.0 - }, + }, "Pentax *ist DL": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3026.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3026.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2017.0 - }, + }, "Pentax *ist DL2": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3026.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3026.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2017.0 - }, + }, "Pentax *ist DS": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3026.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3026.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2017.0 - }, + }, "Pentax *ist DS2": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3026.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3026.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2017.0 - }, + }, "Pentax 645D": { - "Sensor height (mm)": 33.0, - "Sensor width (mm)": 44.0, - "Sensor res. (width)": 7294.0, - "Sensor": "44 x 33 mm", + "Sensor height (mm)": 33.0, + "Sensor width (mm)": 44.0, + "Sensor res. (width)": 7294.0, + "Sensor": "44 x 33 mm", "Sensor res. (height)": 5484.0 - }, + }, "Pentax 645Z": { - "Sensor height (mm)": 33.0, - "Sensor width (mm)": 44.0, - "Sensor res. (width)": 8269.0, - "Sensor": "44 x 33 mm", + "Sensor height (mm)": 33.0, + "Sensor width (mm)": 44.0, + "Sensor res. (width)": 8269.0, + "Sensor": "44 x 33 mm", "Sensor res. (height)": 6217.0 - }, + }, "Pentax EI-100": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1268.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1268.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 946.0 - }, + }, "Pentax EI-200": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Pentax EI-2000": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 1589.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 1589.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Pentax Efina": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Pentax K-01": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 4962.0, - "Sensor": "23.7 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 4962.0, + "Sensor": "23.7 x 15.7 mm", "Sensor res. (height)": 3286.0 - }, + }, "Pentax K-3": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 6064.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 6064.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 4016.0 - }, + }, "Pentax K-30": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 4962.0, - "Sensor": "23.7 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 4962.0, + "Sensor": "23.7 x 15.7 mm", "Sensor res. (height)": 3286.0 - }, + }, "Pentax K-5": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4944.0, - "Sensor": "23.6 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4944.0, + "Sensor": "23.6 x 15.7 mm", "Sensor res. (height)": 3296.0 - }, + }, "Pentax K-5 II": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 4962.0, - "Sensor": "23.7 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 4962.0, + "Sensor": "23.7 x 15.7 mm", "Sensor res. (height)": 3286.0 - }, + }, "Pentax K-50": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 4962.0, - "Sensor": "23.7 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 4962.0, + "Sensor": "23.7 x 15.7 mm", "Sensor res. (height)": 3286.0 - }, + }, "Pentax K-500": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 4962.0, - "Sensor": "23.7 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 4962.0, + "Sensor": "23.7 x 15.7 mm", "Sensor res. (height)": 3286.0 - }, + }, "Pentax K-7": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4680.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4680.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3120.0 - }, + }, "Pentax K-S1": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5512.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5512.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3650.0 - }, + }, "Pentax K-m": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3912.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3912.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2608.0 - }, + }, "Pentax K-r": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4299.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4299.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2885.0 - }, + }, "Pentax K-x": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4299.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4299.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2885.0 - }, + }, "Pentax K100D": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3026.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3026.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2017.0 - }, + }, "Pentax K100D Super": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3026.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3026.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2017.0 - }, + }, "Pentax K10D": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3873.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3873.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2582.0 - }, + }, "Pentax K110D": { - "Sensor height (mm)": 15.5, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 3055.0, - "Sensor": "23.7 x 15.5 mm", + "Sensor height (mm)": 15.5, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 3055.0, + "Sensor": "23.7 x 15.5 mm", "Sensor res. (height)": 1997.0 - }, + }, "Pentax K200D": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3912.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3912.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2608.0 - }, + }, "Pentax K20D": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4680.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4680.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3120.0 - }, + }, "Pentax MX-1": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4011.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4011.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2993.0 - }, + }, "Pentax Optio 230": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Pentax Optio 30": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Pentax Optio 330": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Pentax Optio 330GS": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Pentax Optio 330RS": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Pentax Optio 33L": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Pentax Optio 33LF": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Pentax Optio 33WR": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Pentax Optio 430": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2248.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2248.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1690.0 - }, + }, "Pentax Optio 430RS": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2248.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2248.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1690.0 - }, + }, "Pentax Optio 43WR": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Pentax Optio 450": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Pentax Optio 50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Pentax Optio 50L": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Pentax Optio 550": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Pentax Optio 555": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Pentax Optio 60": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2849.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2849.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Pentax Optio 750Z": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Pentax Optio A10": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Pentax Optio A20": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Pentax Optio A30": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Pentax Optio A40": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4011.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4011.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2993.0 - }, + }, "Pentax Optio E10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Pentax Optio E20": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Pentax Optio E25": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Pentax Optio E30": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Pentax Optio E40": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Pentax Optio E50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Pentax Optio E60": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Pentax Optio E70": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Pentax Optio E70L": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Pentax Optio E75": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Pentax Optio E80": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Pentax Optio E85": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Pentax Optio E90": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Pentax Optio H90": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Pentax Optio I-10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Pentax Optio L20": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Pentax Optio L50": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3362.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3362.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2528.0 - }, + }, "Pentax Optio LS1000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Pentax Optio LS1100": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Pentax Optio LS465": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.7, - "Sensor res. (width)": 4915.0, - "Sensor": "23.7 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.7, + "Sensor res. (width)": 4915.0, + "Sensor": "23.7 x 15.7 mm", "Sensor res. (height)": 3255.0 - }, + }, "Pentax Optio M10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Pentax Optio M20": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Pentax Optio M30": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Pentax Optio M40": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Pentax Optio M50": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Pentax Optio M60": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Pentax Optio M85": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Pentax Optio M90": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Pentax Optio MX": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Pentax Optio MX4": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Pentax Optio P70": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Pentax Optio P80": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Pentax Optio RS1000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Pentax Optio RS1500": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Pentax Optio RZ10": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Pentax Optio RZ18": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Pentax Optio S": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Pentax Optio S1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Pentax Optio S10": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Pentax Optio S12": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4011.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4011.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2993.0 - }, + }, "Pentax Optio S30": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Pentax Optio S4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Pentax Optio S40": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Pentax Optio S45": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Pentax Optio S4i": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Pentax Optio S50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Pentax Optio S55": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Pentax Optio S5i": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Pentax Optio S5n": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Pentax Optio S5z": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Pentax Optio S6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Pentax Optio S60": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Pentax Optio S7": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Pentax Optio SV": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Pentax Optio SVi": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Pentax Optio T10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Pentax Optio T20": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Pentax Optio T30": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Pentax Optio V10": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Pentax Optio V20": { - "Sensor height (mm)": 4.52, - "Sensor width (mm)": 6.03, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", + "Sensor height (mm)": 4.52, + "Sensor width (mm)": 6.03, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.35\" (~ 6.03 x 4.52 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Pentax Optio VS20": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Pentax Optio W10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Pentax Optio W20": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Pentax Optio W30": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Pentax Optio W60": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Pentax Optio W80": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Pentax Optio W90": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Pentax Optio WG-1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Pentax Optio WG-1 GPS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Pentax Optio WG-2": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Pentax Optio WG-2 GPS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Pentax Optio WP": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Pentax Optio WPi": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Pentax Optio WS80": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Pentax Optio X": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Pentax Optio Z10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Pentax Q": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Pentax Q-S1": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4076.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4076.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3042.0 - }, + }, "Pentax Q10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Pentax Q7": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4076.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4076.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3042.0 - }, + }, "Pentax WG-10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Pentax WG-3": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Pentax X-5": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Pentax X70": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Pentax X90": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Pentax XG-1": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Praktica DC 20": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Praktica DC 21": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Praktica DC 22": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Praktica DC 32": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Praktica DC 34": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2095.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2095.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1575.0 - }, + }, "Praktica DC 42": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2363.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2363.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1777.0 - }, + }, "Praktica DC 44": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2363.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2363.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1777.0 - }, + }, "Praktica DC 50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2627.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2627.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1975.0 - }, + }, "Praktica DC 52": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2671.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2671.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2008.0 - }, + }, "Praktica DC 60": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2867.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2867.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2156.0 - }, + }, "Praktica DC Slim 2": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Praktica DC Slim 5": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2604.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2604.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Praktica DC440": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Praktica DCZ 1.3": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Praktica DCZ 10.1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3713.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3713.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2792.0 - }, + }, "Praktica DCZ 10.2": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3677.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3677.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2765.0 - }, + }, "Praktica DCZ 10.3": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Praktica DCZ 10.4": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Praktica DCZ 12.1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Praktica DCZ 12.Z4": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Praktica DCZ 14.1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Praktica DCZ 14.2": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Praktica DCZ 2.0": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1672.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1672.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Praktica DCZ 2.1": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1676.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1676.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Praktica DCZ 2.1 S": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1682.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1682.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1255.0 - }, + }, "Praktica DCZ 2.2": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Praktica DCZ 2.2 S": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Praktica DCZ 3.0": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2044.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2044.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1537.0 - }, + }, "Praktica DCZ 3.2": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2063.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2063.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Praktica DCZ 3.2D": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2095.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2095.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1575.0 - }, + }, "Praktica DCZ 3.2S": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2095.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2095.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1575.0 - }, + }, "Praktica DCZ 3.3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Praktica DCZ 3.4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Praktica DCZ 3.5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 1998.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 1998.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1502.0 - }, + }, "Praktica DCZ 4.1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Praktica DCZ 4.2": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Praktica DCZ 4.3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Praktica DCZ 4.4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2363.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2363.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1777.0 - }, + }, "Praktica DCZ 5.1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Praktica DCZ 5.2": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Praktica DCZ 5.3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Praktica DCZ 5.4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Praktica DCZ 5.5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2629.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2629.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Praktica DCZ 5.8": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Praktica DCZ 6.1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Praktica DCZ 6.2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Praktica DCZ 6.3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Praktica DCZ 6.8": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Praktica DCZ 7.1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Praktica DCZ 7.2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3084.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3084.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2319.0 - }, + }, "Praktica DCZ 7.3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Praktica DCZ 7.4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3133.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3133.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2356.0 - }, + }, "Praktica DCZ 8.1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Praktica DCZ 8.2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Praktica DCZ 8.3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Praktica DMMC": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1631.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1631.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Praktica DMMC 4": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1631.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1631.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Praktica DVC 6.1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Praktica Digi 3": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Praktica Digi 3 LM": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Praktica Digi 30": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Praktica Digicam 3": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Praktica Dpix 1000z": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Praktica Dpix 1100z": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Praktica Dpix 1220z": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Praktica Dpix 3000": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Praktica Dpix 3200": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 2063.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 2063.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Praktica Dpix 3300": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 2063.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 2063.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Praktica Dpix 5000 WP": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 2589.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 2589.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1932.0 - }, + }, "Praktica Dpix 5100": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Praktica Dpix 510Z": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Praktica Dpix 5200": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Praktica Dpix 530Z": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Praktica Dpix 740z": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3084.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3084.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2319.0 - }, + }, "Praktica Dpix 750z": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3096.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3096.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2328.0 - }, + }, "Praktica Dpix 810z": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Praktica Dpix 820z": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Praktica Dpix 9000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3459.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3459.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2601.0 - }, + }, "Praktica Dpix 910z": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 3459.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 3459.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 2601.0 - }, + }, "Praktica Exakta DC 4200": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Praktica G2.0": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Praktica G3.2": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1998.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1998.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1502.0 - }, + }, "Praktica Luxmedia 10 TS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3708.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3708.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2788.0 - }, + }, "Praktica Luxmedia 10 X3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3665.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3665.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Praktica Luxmedia 10 XS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3713.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3713.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2792.0 - }, + }, "Praktica Luxmedia 10-03": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Praktica Luxmedia 10-23": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Praktica Luxmedia 12 HD": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 3995.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 3995.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Praktica Luxmedia 12 TS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3708.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3708.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2788.0 - }, + }, "Praktica Luxmedia 12 XS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Praktica Luxmedia 12-03": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Praktica Luxmedia 12-04": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Praktica Luxmedia 12-23": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Praktica Luxmedia 12-Z4": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Praktica Luxmedia 12-Z4TS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Praktica Luxmedia 12-Z5": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Praktica Luxmedia 14-Z50S": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Praktica Luxmedia 14-Z51": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Praktica Luxmedia 14-Z80S": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Praktica Luxmedia 16-Z12S": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Praktica Luxmedia 16-Z21C": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Praktica Luxmedia 16-Z21S": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Praktica Luxmedia 16-Z24S": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Praktica Luxmedia 16-Z51": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Praktica Luxmedia 16-Z52": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Praktica Luxmedia 18-Z36C": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4893.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4893.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3679.0 - }, + }, "Praktica Luxmedia 4008": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2363.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2363.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1777.0 - }, + }, "Praktica Luxmedia 5003": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2604.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2604.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Praktica Luxmedia 5008": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2629.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2629.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Praktica Luxmedia 5103": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2655.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2655.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1996.0 - }, + }, "Praktica Luxmedia 5203": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Praktica Luxmedia 5303": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Praktica Luxmedia 6103": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2825.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2825.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Praktica Luxmedia 6105": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2909.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2909.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2187.0 - }, + }, "Praktica Luxmedia 6203": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Praktica Luxmedia 6403": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Praktica Luxmedia 6503": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2894.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2894.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2176.0 - }, + }, "Praktica Luxmedia 7103": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Praktica Luxmedia 7105": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Praktica Luxmedia 7203": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3133.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3133.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2356.0 - }, + }, "Praktica Luxmedia 7303": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Praktica Luxmedia 7403": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3139.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3139.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2360.0 - }, + }, "Praktica Luxmedia 8003": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Praktica Luxmedia 8203": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3292.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3292.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2475.0 - }, + }, "Praktica Luxmedia 8213": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3292.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3292.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2475.0 - }, + }, "Praktica Luxmedia 8303": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Praktica Luxmedia 8403": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Praktica Luxmedia 8503": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Praktica Mini": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Praktica V2.1": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Praktica V3.2": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Ricoh CX1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3517.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3517.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2644.0 - }, + }, "Ricoh CX2": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3517.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3517.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2644.0 - }, + }, "Ricoh CX3": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Ricoh CX4": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Ricoh CX5": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Ricoh CX6": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Ricoh Caplio 400G Wide": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Ricoh Caplio 500G": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Ricoh Caplio 500G Wide": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3322.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3322.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2498.0 - }, + }, "Ricoh Caplio 500SE": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Ricoh Caplio G3": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Ricoh Caplio G3s": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2076.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2076.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1561.0 - }, + }, "Ricoh Caplio GX": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2612.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2612.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1964.0 - }, + }, "Ricoh Caplio GX100": { - "Sensor height (mm)": 5.49, - "Sensor width (mm)": 7.31, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.75\" (~ 7.31 x 5.49 mm)", + "Sensor height (mm)": 5.49, + "Sensor width (mm)": 7.31, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.75\" (~ 7.31 x 5.49 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Ricoh Caplio GX200": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Ricoh Caplio GX8": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Ricoh Caplio R1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2552.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2552.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Ricoh Caplio R1V": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Ricoh Caplio R2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Ricoh Caplio R2S": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Ricoh Caplio R3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Ricoh Caplio R30": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Ricoh Caplio R4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Ricoh Caplio R40": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Ricoh Caplio R5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Ricoh Caplio R6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Ricoh Caplio R7": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Ricoh Caplio R8": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Ricoh Caplio RR1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Ricoh Caplio RR10": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Ricoh Caplio RR120": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1717.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1717.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1281.0 - }, + }, "Ricoh Caplio RR230": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1682.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1682.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1255.0 - }, + }, "Ricoh Caplio RR30": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2076.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2076.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1561.0 - }, + }, "Ricoh Caplio RR330": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Ricoh Caplio RR530": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2552.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2552.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1919.0 - }, + }, "Ricoh Caplio RR630": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2825.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2825.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Ricoh Caplio RR660": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2862.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2862.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2152.0 - }, + }, "Ricoh Caplio RR750": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3096.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3096.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2328.0 - }, + }, "Ricoh Caplio RR770": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Ricoh Caplio RX": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Ricoh Caplio RZ1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2361.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2361.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1775.0 - }, + }, "Ricoh G600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Ricoh G700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Ricoh G700SE": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Ricoh G800": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Ricoh GR": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4929.0, - "Sensor": "23.6 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4929.0, + "Sensor": "23.6 x 15.7 mm", "Sensor res. (height)": 3286.0 - }, + }, "Ricoh GR Digital": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Ricoh GR Digital 3": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3733.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3733.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2786.0 - }, + }, "Ricoh GR Digital 4": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3733.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3733.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2786.0 - }, + }, "Ricoh GR Digital II": { - "Sensor height (mm)": 5.49, - "Sensor width (mm)": 7.31, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.75\" (~ 7.31 x 5.49 mm)", + "Sensor height (mm)": 5.49, + "Sensor width (mm)": 7.31, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.75\" (~ 7.31 x 5.49 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Ricoh GX200": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4076.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4076.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3042.0 - }, + }, "Ricoh GXR A12 50mm F2.5 Macro": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4296.0, - "Sensor": "23.6 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4296.0, + "Sensor": "23.6 x 15.7 mm", "Sensor res. (height)": 2864.0 - }, + }, "Ricoh GXR A16 24-85mm F3.5-5.5": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4929.0, - "Sensor": "23.6 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4929.0, + "Sensor": "23.6 x 15.7 mm", "Sensor res. (height)": 3286.0 - }, + }, "Ricoh GXR GR Lens A12 28mm F2.5": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4296.0, - "Sensor": "23.6 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4296.0, + "Sensor": "23.6 x 15.7 mm", "Sensor res. (height)": 2864.0 - }, + }, "Ricoh GXR Mount A12": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4296.0, - "Sensor": "23.6 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4296.0, + "Sensor": "23.6 x 15.7 mm", "Sensor res. (height)": 2864.0 - }, + }, "Ricoh GXR P10 28-300mm F3.5-5.6 VC": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Ricoh GXR S10 24-72mm F2.5-4.4 VC": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3661.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3661.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2732.0 - }, + }, "Ricoh HZ15": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Ricoh PX": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Ricoh R10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Ricoh R50": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3708.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3708.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2788.0 - }, + }, "Ricoh R8": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Ricoh RDC-200G": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1710.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1710.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1286.0 - }, + }, "Ricoh RDC-4300": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1264.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1264.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 950.0 - }, + }, "Ricoh RDC-5000": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1710.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1710.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1286.0 - }, + }, "Ricoh RDC-5300": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1710.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1710.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1286.0 - }, + }, "Ricoh RDC-6000": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Ricoh RDC-7": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Ricoh RDC-i500": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Ricoh RDC-i700": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Ricoh WG-20": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Ricoh WG-4": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Ricoh WG-M1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Compactline 100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei Compactline 101": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei Compactline 102": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei Compactline 103": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei Compactline 110": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei Compactline 130": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei Compactline 150": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei Compactline 200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Compactline 202": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Compactline 203": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Compactline 230": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Compactline 302": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Compactline 304": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Compactline 312": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Compactline 320": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Compactline 350": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Compactline 360 TS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Compactline 370 TS": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Compactline 390 SE": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Compactline 412": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Compactline 415": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Compactline 425": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Compactline 50": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Rollei Compactline 52": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Rollei Compactline 55": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Rollei Compactline 80": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei Compactline 81": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei Compactline 90": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3459.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3459.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2601.0 - }, + }, "Rollei Flexline 100": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei Flexline 100 iT": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei Flexline 140": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei Flexline 200": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Flexline 202": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Flexline 250": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Kids 100": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 632.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 632.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Rollei Powerflex 240 HD": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Rollei Powerflex 360 Full HD": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4893.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4893.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3679.0 - }, + }, "Rollei Powerflex 3D": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 2629.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 2629.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Rollei Powerflex 400": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Powerflex 440": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Powerflex 450": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Powerflex 455": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Powerflex 460": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Powerflex 470": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Powerflex 500": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Rollei Powerflex 600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Powerflex 610 HD": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Rollei Powerflex 700 Full HD": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Rollei Powerflex 800": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei Powerflex 820": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Rollei Prego da3": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Rollei Prego da4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Rollei Prego da5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Rollei Prego da6": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2825.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2825.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Rollei Prego dp4200": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2363.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2363.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1777.0 - }, + }, "Rollei Prego dp5200": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2629.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2629.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Rollei Prego dp5300": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2643.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2643.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1987.0 - }, + }, "Rollei Prego dp5500": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Rollei Prego dp6000": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2909.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2909.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2187.0 - }, + }, "Rollei Prego dp6200": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2909.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2909.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2187.0 - }, + }, "Rollei Prego dp6300": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2894.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2894.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2176.0 - }, + }, "Rollei Prego dp8300": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Rollei RCP-10325X": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei RCP-5324": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Rollei RCP-6324": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2862.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2862.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2152.0 - }, + }, "Rollei RCP-7324": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Rollei RCP-7325XS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Rollei RCP-7330X": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3086.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3086.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2320.0 - }, + }, "Rollei RCP-7430XW": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Rollei RCP-8325": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei RCP-8325X": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei RCP-8325XS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei RCP-8330X": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei RCP-8427XW": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei RCP-8527X": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei RCP-S10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei RCP-S8": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei Sportsline 50": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Rollei Sportsline 60 Camouflage": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Rollei Sportsline 62": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei Sportsline 90": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3459.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3459.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2601.0 - }, + }, "Rollei Sportsline 99": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Rollei X-8": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei X-8 Compact": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei X-8 Sports": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei XS-10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei XS-10 inTouch": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei XS-8": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei XS-8 Crystal": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei d20 motion": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1631.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1631.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Rollei d210 motion": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1678.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1678.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1252.0 - }, + }, "Rollei d23 com": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 1755.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 1755.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1310.0 - }, + }, "Rollei d33 com": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2099.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2099.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1578.0 - }, + }, "Rollei d330 motion": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Rollei d41 com": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2343.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2343.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1762.0 - }, + }, "Rollei d530 flex": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 2579.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 2579.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Rollei da10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Rollei da1325 Prego": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Rollei da5324": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2627.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2627.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1975.0 - }, + }, "Rollei da5325 Prego": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2627.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2627.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1975.0 - }, + }, "Rollei da6324": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2909.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2909.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2187.0 - }, + }, "Rollei da7325 Prego": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3103.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3103.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2333.0 - }, + }, "Rollei da8324": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Rollei dc 3100": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Rollei dcx 310": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Rollei dcx 400": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Rollei dk 3000": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2044.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2044.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1537.0 - }, + }, "Rollei dk4010": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Rollei dp 300": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Rollei dp 3210": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Rollei dp6500": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2825.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2825.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Rollei dpx 310": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Rollei dr 5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Rollei dr 5100": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2612.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2612.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1964.0 - }, + }, "Rollei ds6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Rollei dsx 410": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Rollei dt 3200": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2079.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2079.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1563.0 - }, + }, "Rollei dt 4000": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Rollei dt 4200": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Rollei dt6 Tribute": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2909.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2909.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2187.0 - }, + }, "Rollei dx63": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2909.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2909.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2187.0 - }, + }, "Samsung AQ100": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung CL5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3459.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3459.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2601.0 - }, + }, "Samsung CL65": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung CL80": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung D75": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Samsung D830": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Samsung D85": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3322.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3322.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2498.0 - }, + }, "Samsung D860": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Samsung DV100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Samsung DV150F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Samsung DV300F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Samsung Digimax 101": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 989.0 - }, + }, "Samsung Digimax 130": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1268.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1268.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 946.0 - }, + }, "Samsung Digimax 200": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Samsung Digimax 201": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1637.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1637.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1222.0 - }, + }, "Samsung Digimax 202": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Samsung Digimax 210 SE": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Samsung Digimax 220 SE": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Samsung Digimax 230": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Samsung Digimax 240": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1637.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1637.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1222.0 - }, + }, "Samsung Digimax 250": { - "Sensor height (mm)": 3.37, - "Sensor width (mm)": 4.5, - "Sensor res. (width)": 1596.0, - "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", + "Sensor height (mm)": 3.37, + "Sensor width (mm)": 4.5, + "Sensor res. (width)": 1596.0, + "Sensor": "1/3.2\" (~ 4.5 x 3.37 mm)", "Sensor res. (height)": 1191.0 - }, + }, "Samsung Digimax 301": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Samsung Digimax 330": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 2116.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 2116.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 1579.0 - }, + }, "Samsung Digimax 340": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2063.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2063.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Samsung Digimax 35 MP3": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 632.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 632.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Samsung Digimax 350SE": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Samsung Digimax 360": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2063.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2063.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Samsung Digimax 370": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Samsung Digimax 401": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Samsung Digimax 410": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Samsung Digimax 420": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Samsung Digimax 430": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Samsung Digimax 50 duo": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 632.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 632.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 475.0 - }, + }, "Samsung Digimax 530": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Samsung Digimax A400": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Samsung Digimax A402": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2363.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2363.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1777.0 - }, + }, "Samsung Digimax A5": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Samsung Digimax A50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Samsung Digimax A502": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Samsung Digimax A503": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2629.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2629.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Samsung Digimax A55W": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Samsung Digimax A6": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2825.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2825.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Samsung Digimax A7": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3095.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3095.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Samsung Digimax D103": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3701.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3701.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Samsung Digimax L50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Samsung Digimax L55W": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2612.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2612.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1964.0 - }, + }, "Samsung Digimax L60": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Samsung Digimax L70": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Samsung Digimax L85": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Samsung Digimax S1000": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3665.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3665.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Samsung Digimax S500": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Samsung Digimax S600": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2849.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2849.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Samsung Digimax S700": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Samsung Digimax S800": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Samsung Digimax U-CA 3": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Samsung Digimax U-CA 4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Samsung Digimax U-CA 401": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2277.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2277.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Samsung Digimax U-CA5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Samsung Digimax U-CA501": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Samsung Digimax U-CA505": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Samsung Digimax V3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Samsung Digimax V4": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Samsung Digimax V40": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Samsung Digimax V4000": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Samsung Digimax V5": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Samsung Digimax V50": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Samsung Digimax V6": { - "Sensor height (mm)": 5.05, - "Sensor width (mm)": 6.74, - "Sensor res. (width)": 2849.0, - "Sensor": "1/1.9\" (~ 6.74 x 5.05 mm)", + "Sensor height (mm)": 5.05, + "Sensor width (mm)": 6.74, + "Sensor res. (width)": 2849.0, + "Sensor": "1/1.9\" (~ 6.74 x 5.05 mm)", "Sensor res. (height)": 2142.0 - }, + }, "Samsung Digimax V600": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2918.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2918.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2194.0 - }, + }, "Samsung Digimax V70": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3051.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3051.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Samsung Digimax V700": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Samsung Digimax V800": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Samsung Digimax i5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Samsung Digimax i50 MP3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Samsung Digimax i6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Samsung ES10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3322.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3322.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2498.0 - }, + }, "Samsung ES15": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung ES17": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung ES20": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung ES25": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4045.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4045.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Samsung ES28": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4045.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4045.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Samsung ES30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Samsung ES50": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Samsung ES55": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung ES60": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung ES65": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung ES70": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung ES73": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4045.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4045.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3041.0 - }, + }, "Samsung ES75": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Samsung ES80": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Samsung ES90": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung ES95": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Samsung EX1": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3661.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3661.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2732.0 - }, + }, "Samsung EX2F": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4076.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4076.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3042.0 - }, + }, "Samsung GX-10": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3873.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3873.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2582.0 - }, + }, "Samsung GX-1L": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3026.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3026.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2017.0 - }, + }, "Samsung GX-1S": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3026.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3026.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2017.0 - }, + }, "Samsung GX-20": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4680.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4680.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3120.0 - }, + }, "Samsung Galaxy Camera": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4656.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4656.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3501.0 - }, + }, "Samsung Galaxy Camera 2": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4656.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4656.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3501.0 - }, + }, "Samsung Galaxy NX": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5519.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5519.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 3679.0 - }, + }, "Samsung HZ10W": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung HZ15W": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Samsung HZ25W": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4078.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4078.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3066.0 - }, + }, "Samsung HZ30W": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Samsung HZ35W": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Samsung HZ50W": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4284.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4284.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3221.0 - }, + }, "Samsung IT100": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung L100": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Samsung L110": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Samsung L200": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung L201": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung L210": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung L301": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Samsung L310W": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4253.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4253.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3198.0 - }, + }, "Samsung L700": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Samsung L73": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Samsung L730": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Samsung L74": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Samsung L74 Wide": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Samsung L77": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Samsung L80": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3342.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3342.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2513.0 - }, + }, "Samsung L830": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Samsung L83T": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Samsung M100": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Samsung MV800": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Samsung Miniket VP-MS10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2643.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2643.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1987.0 - }, + }, "Samsung Miniket VP-MS11": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2643.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2643.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1987.0 - }, + }, "Samsung Miniket VP-MS15": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2643.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2643.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1987.0 - }, + }, "Samsung NV10": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Samsung NV100 HD": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4422.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4422.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3325.0 - }, + }, "Samsung NV11": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Samsung NV15": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 3665.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 3665.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Samsung NV20": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4011.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4011.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Samsung NV24HD": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung NV3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Samsung NV30": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Samsung NV4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Samsung NV40": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3737.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3737.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2810.0 - }, + }, "Samsung NV7 OPS": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Samsung NV8": { - "Sensor height (mm)": 5.46, - "Sensor width (mm)": 7.27, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", + "Sensor height (mm)": 5.46, + "Sensor width (mm)": 7.27, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.76\" (~ 7.27 x 5.46 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Samsung NV9": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung NX mini": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 5546.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 5546.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3697.0 - }, + }, "Samsung NX1": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 6504.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 6504.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 4336.0 - }, + }, "Samsung NX10": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4680.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4680.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3120.0 - }, + }, "Samsung NX100": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4680.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4680.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3120.0 - }, + }, "Samsung NX1000": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5519.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5519.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 3679.0 - }, + }, "Samsung NX11": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4680.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4680.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3120.0 - }, + }, "Samsung NX1100": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5519.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5519.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 3679.0 - }, + }, "Samsung NX20": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5519.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5519.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 3679.0 - }, + }, "Samsung NX200": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5519.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5519.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 3679.0 - }, + }, "Samsung NX2000": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5519.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5519.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 3679.0 - }, + }, "Samsung NX210": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5519.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5519.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 3679.0 - }, + }, "Samsung NX30": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5519.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5519.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 3679.0 - }, + }, "Samsung NX300": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5519.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5519.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 3679.0 - }, + }, "Samsung NX3000": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5519.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5519.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 3679.0 - }, + }, "Samsung NX5": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4680.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4680.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3120.0 - }, + }, "Samsung PL10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3459.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3459.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2601.0 - }, + }, "Samsung PL100": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung PL120": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung PL150": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung PL160": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Samsung PL170": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Samsung PL20": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung PL200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung PL210": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Samsung PL50": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung PL51": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Samsung PL55": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung PL60": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung PL65": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Samsung PL70": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung PL80": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Samsung PL90": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung Pro815": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3262.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3262.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Samsung S1030": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Samsung S1050": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Samsung S1060": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung S1070": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung S630": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Samsung S730": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Samsung S750": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Samsung S760": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Samsung S830": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Samsung S85": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Samsung S850": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Samsung S860": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Samsung SDC-MS61": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Samsung SH100": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Samsung SL102": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung SL201": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung SL202": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung SL30": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung SL310W": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4253.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4253.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3198.0 - }, + }, "Samsung SL50": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung SL502": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung SL600": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung SL605": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Samsung SL620": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung SL630": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Samsung SL720": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung SL820": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung ST10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3479.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3479.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2616.0 - }, + }, "Samsung ST100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung ST1000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Samsung ST150F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Samsung ST200F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Samsung ST30": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 3665.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 3665.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Samsung ST45": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung ST50": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung ST500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Samsung ST5000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung ST550": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung ST5500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung ST60": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Samsung ST600": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung ST65": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung ST6500": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Samsung ST66": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Samsung ST70": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4376.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4376.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3290.0 - }, + }, "Samsung ST700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Samsung ST72": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Samsung ST76": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Samsung ST77": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Samsung ST80": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung ST88": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Samsung ST90": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Samsung ST93": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Samsung ST95": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Samsung ST96": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4406.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4406.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3313.0 - }, + }, "Samsung TL100": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung TL105": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Samsung TL110": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4376.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4376.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3290.0 - }, + }, "Samsung TL205": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung TL210": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung TL220": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung TL225": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung TL240": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung TL320": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung TL34HD": { - "Sensor height (mm)": 5.58, - "Sensor width (mm)": 7.44, - "Sensor res. (width)": 4422.0, - "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", + "Sensor height (mm)": 5.58, + "Sensor width (mm)": 7.44, + "Sensor res. (width)": 4422.0, + "Sensor": "1/1.72\" (~ 7.44 x 5.58 mm)", "Sensor res. (height)": 3325.0 - }, + }, "Samsung TL350": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Samsung TL500": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3661.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3661.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2732.0 - }, + }, "Samsung TL9": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung WB100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Samsung WB1000": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Samsung WB110": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5183.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5183.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3897.0 - }, + }, "Samsung WB1100F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Samsung WB150F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung WB2000": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Samsung WB200F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung WB210": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Samsung WB2100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4667.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4667.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3509.0 - }, + }, "Samsung WB2200F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4671.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4671.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3512.0 - }, + }, "Samsung WB250F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4346.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4346.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3268.0 - }, + }, "Samsung WB30F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Samsung WB350F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4656.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4656.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3501.0 - }, + }, "Samsung WB35F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Samsung WB500": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Samsung WB5000": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4078.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4078.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3066.0 - }, + }, "Samsung WB50F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Samsung WB510": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Samsung WB550": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Samsung WB5500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4284.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4284.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3221.0 - }, + }, "Samsung WB560": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Samsung WB600": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Samsung WB650": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Samsung WB660": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung WB690": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Samsung WB700": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Samsung WB750": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4078.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4078.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3066.0 - }, + }, "Samsung WB800F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4656.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4656.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3501.0 - }, + }, "Samsung WB850F": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Samsung WP10": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Samsung i100": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Samsung i7": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Samsung i70": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Samsung i8": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Samsung i80": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3302.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3302.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2483.0 - }, + }, "Samsung i85": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sanyo DSC S1": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Sanyo DSC S3": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Sanyo DSC S4": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Sanyo DSC S5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2671.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2671.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2008.0 - }, + }, "Sanyo VPC A5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Sanyo VPC AZ1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2343.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2343.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1762.0 - }, + }, "Sanyo VPC AZ3 EX": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Sanyo VPC E1090": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Sanyo VPC E1403": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Sanyo VPC E1500TP": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Sanyo VPC E890": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sanyo VPC HD1 EX": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2599.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2599.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1954.0 - }, + }, "Sanyo VPC J1 EX": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Sanyo VPC J2 EX": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Sanyo VPC J4 EX": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Sanyo VPC MZ1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1676.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1676.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Sanyo VPC MZ2": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1631.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1631.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Sanyo VPC S1085": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Sanyo VPC S122": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Sanyo VPC S1275": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Sanyo VPC S1414": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Sanyo VPC S885": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sanyo VPC T1495": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Sanyo VPC X1200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Sanyo VPC X1220": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Sanyo VPC X1420": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4315.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4315.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3244.0 - }, + }, "Sanyo Xacti C1": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Sanyo Xacti C4": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Sanyo Xacti C40": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Sanyo Xacti C5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2645.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2645.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1989.0 - }, + }, "Sanyo Xacti C6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Sanyo Xacti DMX-CA65": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2910.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2910.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2188.0 - }, + }, "Sanyo Xacti DMX-CA8": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3286.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3286.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2471.0 - }, + }, "Sanyo Xacti DMX-CG65": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2910.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2910.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2188.0 - }, + }, "Sanyo Xacti DMX-CG9": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3483.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3483.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2619.0 - }, + }, "Sanyo Xacti DMX-HD700": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3133.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3133.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2356.0 - }, + }, "Sanyo Xacti DMX-HD800": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Sanyo Xacti E6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2910.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2910.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2188.0 - }, + }, "Sanyo Xacti E60": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2910.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2910.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2188.0 - }, + }, "Sanyo Xacti S50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2625.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2625.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1974.0 - }, + }, "Sanyo Xacti S6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2910.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2910.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2188.0 - }, + }, "Sanyo Xacti S60": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2867.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2867.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2156.0 - }, + }, "Sanyo Xacti S70": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Sanyo Xacti VPC S1 EX": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Sanyo Xacti VPC S3 EX": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Sanyo Xacti VPC S4 EX": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Sanyo Xacti VPC-503": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Sanyo Xacti VPC-603": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Sanyo Xacti VPC-CA6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Sanyo Xacti VPC-CA9": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3463.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3463.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2604.0 - }, + }, "Sanyo Xacti VPC-CG10": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3765.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3765.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2831.0 - }, + }, "Sanyo Xacti VPC-CG6": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2910.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2910.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2188.0 - }, + }, "Sanyo Xacti VPC-E10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Sanyo Xacti VPC-E1075": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Sanyo Xacti VPC-E7": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Sanyo Xacti VPC-E760": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Sanyo Xacti VPC-E860": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sanyo Xacti VPC-E870": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Sanyo Xacti VPC-E875": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sanyo Xacti VPC-HD1A": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2671.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2671.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2008.0 - }, + }, "Sanyo Xacti VPC-HD2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3133.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3133.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2356.0 - }, + }, "Sanyo Xacti VPC-HD2000": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sanyo Xacti VPC-S1070": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Sanyo Xacti VPC-S500": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Sanyo Xacti VPC-S600": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Sanyo Xacti VPC-S650": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Sanyo Xacti VPC-S670": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Sanyo Xacti VPC-S7": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Sanyo Xacti VPC-S750": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Sanyo Xacti VPC-S760": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Sanyo Xacti VPC-S770": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3072.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3072.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Sanyo Xacti VPC-S870": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sanyo Xacti VPC-S880": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sanyo Xacti VPC-T1060": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3647.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3647.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Sanyo Xacti VPC-T700": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Sanyo Xacti VPC-T850": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sanyo Xacti VPC-W800": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Sanyo Xacti VPC-X1200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Sigma DP1": { - "Sensor height (mm)": 13.8, - "Sensor width (mm)": 20.7, - "Sensor res. (width)": 2655.0, - "Sensor": "20.7 x 13.8 mm", + "Sensor height (mm)": 13.8, + "Sensor width (mm)": 20.7, + "Sensor res. (width)": 2655.0, + "Sensor": "20.7 x 13.8 mm", "Sensor res. (height)": 1770.0 - }, + }, "Sigma DP1 Merrill": { - "Sensor height (mm)": 16.0, - "Sensor width (mm)": 24.0, - "Sensor res. (width)": 4806.0, - "Sensor": "24 x 16 mm", + "Sensor height (mm)": 16.0, + "Sensor width (mm)": 24.0, + "Sensor res. (width)": 4806.0, + "Sensor": "24 x 16 mm", "Sensor res. (height)": 3204.0 - }, + }, "Sigma DP1s": { - "Sensor height (mm)": 13.8, - "Sensor width (mm)": 20.7, - "Sensor res. (width)": 2655.0, - "Sensor": "20.7 x 13.8 mm", + "Sensor height (mm)": 13.8, + "Sensor width (mm)": 20.7, + "Sensor res. (width)": 2655.0, + "Sensor": "20.7 x 13.8 mm", "Sensor res. (height)": 1770.0 - }, + }, "Sigma DP1x": { - "Sensor height (mm)": 13.8, - "Sensor width (mm)": 20.7, - "Sensor res. (width)": 2655.0, - "Sensor": "20.7 x 13.8 mm", + "Sensor height (mm)": 13.8, + "Sensor width (mm)": 20.7, + "Sensor res. (width)": 2655.0, + "Sensor": "20.7 x 13.8 mm", "Sensor res. (height)": 1770.0 - }, + }, "Sigma DP2": { - "Sensor height (mm)": 13.8, - "Sensor width (mm)": 20.7, - "Sensor res. (width)": 2655.0, - "Sensor": "20.7 x 13.8 mm", + "Sensor height (mm)": 13.8, + "Sensor width (mm)": 20.7, + "Sensor res. (width)": 2655.0, + "Sensor": "20.7 x 13.8 mm", "Sensor res. (height)": 1770.0 - }, + }, "Sigma DP2 Merrill": { - "Sensor height (mm)": 16.0, - "Sensor width (mm)": 24.0, - "Sensor res. (width)": 4806.0, - "Sensor": "24 x 16 mm", + "Sensor height (mm)": 16.0, + "Sensor width (mm)": 24.0, + "Sensor res. (width)": 4806.0, + "Sensor": "24 x 16 mm", "Sensor res. (height)": 3204.0 - }, + }, "Sigma DP2s": { - "Sensor height (mm)": 13.8, - "Sensor width (mm)": 20.7, - "Sensor res. (width)": 2655.0, - "Sensor": "20.7 x 13.8 mm", + "Sensor height (mm)": 13.8, + "Sensor width (mm)": 20.7, + "Sensor res. (width)": 2655.0, + "Sensor": "20.7 x 13.8 mm", "Sensor res. (height)": 1770.0 - }, + }, "Sigma DP2x": { - "Sensor height (mm)": 13.8, - "Sensor width (mm)": 20.7, - "Sensor res. (width)": 2655.0, - "Sensor": "20.7 x 13.8 mm", + "Sensor height (mm)": 13.8, + "Sensor width (mm)": 20.7, + "Sensor res. (width)": 2655.0, + "Sensor": "20.7 x 13.8 mm", "Sensor res. (height)": 1770.0 - }, + }, "Sigma DP3 Merrill": { - "Sensor height (mm)": 16.0, - "Sensor width (mm)": 24.0, - "Sensor res. (width)": 4806.0, - "Sensor": "24 x 16 mm", + "Sensor height (mm)": 16.0, + "Sensor width (mm)": 24.0, + "Sensor res. (width)": 4806.0, + "Sensor": "24 x 16 mm", "Sensor res. (height)": 3204.0 - }, + }, "Sigma SD1": { - "Sensor height (mm)": 16.0, - "Sensor width (mm)": 24.0, - "Sensor res. (width)": 4806.0, - "Sensor": "24 x 16 mm", + "Sensor height (mm)": 16.0, + "Sensor width (mm)": 24.0, + "Sensor res. (width)": 4806.0, + "Sensor": "24 x 16 mm", "Sensor res. (height)": 3204.0 - }, + }, "Sigma SD1 Merrill": { - "Sensor height (mm)": 16.0, - "Sensor width (mm)": 24.0, - "Sensor res. (width)": 4806.0, - "Sensor": "24 x 16 mm", + "Sensor height (mm)": 16.0, + "Sensor width (mm)": 24.0, + "Sensor res. (width)": 4806.0, + "Sensor": "24 x 16 mm", "Sensor res. (height)": 3204.0 - }, + }, "Sigma SD10": { - "Sensor height (mm)": 13.8, - "Sensor width (mm)": 20.7, - "Sensor res. (width)": 2259.0, - "Sensor": "20.7 x 13.8 mm", + "Sensor height (mm)": 13.8, + "Sensor width (mm)": 20.7, + "Sensor res. (width)": 2259.0, + "Sensor": "20.7 x 13.8 mm", "Sensor res. (height)": 1506.0 - }, + }, "Sigma SD14": { - "Sensor height (mm)": 13.8, - "Sensor width (mm)": 20.7, - "Sensor res. (width)": 2655.0, - "Sensor": "20.7 x 13.8 mm", + "Sensor height (mm)": 13.8, + "Sensor width (mm)": 20.7, + "Sensor res. (width)": 2655.0, + "Sensor": "20.7 x 13.8 mm", "Sensor res. (height)": 1770.0 - }, + }, "Sigma SD15": { - "Sensor height (mm)": 13.8, - "Sensor width (mm)": 20.7, - "Sensor res. (width)": 2655.0, - "Sensor": "20.7 x 13.8 mm", + "Sensor height (mm)": 13.8, + "Sensor width (mm)": 20.7, + "Sensor res. (width)": 2655.0, + "Sensor": "20.7 x 13.8 mm", "Sensor res. (height)": 1770.0 - }, + }, "Sigma SD9": { - "Sensor height (mm)": 13.8, - "Sensor width (mm)": 20.7, - "Sensor res. (width)": 2259.0, - "Sensor": "20.7 x 13.8 mm", + "Sensor height (mm)": 13.8, + "Sensor width (mm)": 20.7, + "Sensor res. (width)": 2259.0, + "Sensor": "20.7 x 13.8 mm", "Sensor res. (height)": 1506.0 - }, + }, "Skydio 2": { "Sensor height (mm)": 7.564, "Sensor width (mm)": 5.476, @@ -22672,2600 +22672,2600 @@ "Sensor res. (height)": 3040 }, "Sony A77 II": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 6058.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 6058.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 4012.0 - }, + }, "Sony Alpha 7": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 35.8, - "Sensor res. (width)": 6038.0, - "Sensor": "35.8 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 35.8, + "Sensor res. (width)": 6038.0, + "Sensor": "35.8 x 23.9 mm", "Sensor res. (height)": 4025.0 - }, + }, "Sony Alpha 7R": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 35.9, - "Sensor res. (width)": 7389.0, - "Sensor": "35.9 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 35.9, + "Sensor res. (width)": 7389.0, + "Sensor": "35.9 x 24 mm", "Sensor res. (height)": 4926.0 - }, + }, "Sony Alpha 7S": { - "Sensor height (mm)": 23.8, - "Sensor width (mm)": 35.6, - "Sensor res. (width)": 4278.0, - "Sensor": "35.6 x 23.8 mm", + "Sensor height (mm)": 23.8, + "Sensor width (mm)": 35.6, + "Sensor res. (width)": 4278.0, + "Sensor": "35.6 x 23.8 mm", "Sensor res. (height)": 2852.0 - }, + }, "Sony Alpha DSLR-A100": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 3861.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 3861.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2591.0 - }, + }, "Sony Alpha DSLR-A200": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 3898.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 3898.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2616.0 - }, + }, "Sony Alpha DSLR-A230": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3912.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3912.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2608.0 - }, + }, "Sony Alpha DSLR-A290": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 4616.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 4616.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 3077.0 - }, + }, "Sony Alpha DSLR-A300": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 3898.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 3898.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 2616.0 - }, + }, "Sony Alpha DSLR-A330": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 3912.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 3912.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 2608.0 - }, + }, "Sony Alpha DSLR-A350": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4600.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4600.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 3087.0 - }, + }, "Sony Alpha DSLR-A380": { - "Sensor height (mm)": 15.8, - "Sensor width (mm)": 23.6, - "Sensor res. (width)": 4600.0, - "Sensor": "23.6 x 15.8 mm", + "Sensor height (mm)": 15.8, + "Sensor width (mm)": 23.6, + "Sensor res. (width)": 4600.0, + "Sensor": "23.6 x 15.8 mm", "Sensor res. (height)": 3087.0 - }, + }, "Sony Alpha DSLR-A390": { - "Sensor height (mm)": 15.7, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 4616.0, - "Sensor": "23.5 x 15.7 mm", + "Sensor height (mm)": 15.7, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 4616.0, + "Sensor": "23.5 x 15.7 mm", "Sensor res. (height)": 3077.0 - }, + }, "Sony Alpha DSLR-A450": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4616.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4616.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3077.0 - }, + }, "Sony Alpha DSLR-A500": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 4310.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 4310.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 2854.0 - }, + }, "Sony Alpha DSLR-A550": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4616.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4616.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3077.0 - }, + }, "Sony Alpha DSLR-A560": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 4631.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 4631.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3067.0 - }, + }, "Sony Alpha DSLR-A580": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 4945.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 4945.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3275.0 - }, + }, "Sony Alpha DSLR-A700": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 4291.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 4291.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 2842.0 - }, + }, "Sony Alpha DSLR-A850": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 35.9, - "Sensor res. (width)": 6075.0, - "Sensor": "35.9 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 35.9, + "Sensor res. (width)": 6075.0, + "Sensor": "35.9 x 24 mm", "Sensor res. (height)": 4050.0 - }, + }, "Sony Alpha DSLR-A900": { - "Sensor height (mm)": 24.0, - "Sensor width (mm)": 35.9, - "Sensor res. (width)": 6075.0, - "Sensor": "35.9 x 24 mm", + "Sensor height (mm)": 24.0, + "Sensor width (mm)": 35.9, + "Sensor res. (width)": 6075.0, + "Sensor": "35.9 x 24 mm", "Sensor res. (height)": 4050.0 - }, + }, "Sony Alpha NEX-3": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4616.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4616.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3077.0 - }, + }, "Sony Alpha NEX-3N": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 4930.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 4930.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3265.0 - }, + }, "Sony Alpha NEX-5": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4616.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4616.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3077.0 - }, + }, "Sony Alpha NEX-5N": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4914.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4914.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3276.0 - }, + }, "Sony Alpha NEX-5R": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4914.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4914.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3276.0 - }, + }, "Sony Alpha NEX-5T": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4914.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4914.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3276.0 - }, + }, "Sony Alpha NEX-6": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 4930.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 4930.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3265.0 - }, + }, "Sony Alpha NEX-7": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 6058.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 6058.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 4012.0 - }, + }, "Sony Alpha NEX-C3": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4929.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4929.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3286.0 - }, + }, "Sony Alpha NEX-F3": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.4, - "Sensor res. (width)": 4914.0, - "Sensor": "23.4 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.4, + "Sensor res. (width)": 4914.0, + "Sensor": "23.4 x 15.6 mm", "Sensor res. (height)": 3276.0 - }, + }, "Sony Alpha a3000": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5508.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5508.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3648.0 - }, + }, "Sony Alpha a5000": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5508.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5508.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3648.0 - }, + }, "Sony Alpha a5100": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 6058.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 6058.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 4012.0 - }, + }, "Sony Alpha a6000": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 6058.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 6058.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 4012.0 - }, + }, "Sony Cyber-shot DSC-RX1": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 35.8, - "Sensor res. (width)": 6038.0, - "Sensor": "35.8 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 35.8, + "Sensor res. (width)": 6038.0, + "Sensor": "35.8 x 23.9 mm", "Sensor res. (height)": 4025.0 - }, + }, "Sony CyberShot DSC HX100V": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4727.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4727.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3554.0 - }, + }, "Sony CyberShot DSC HX7V": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Sony CyberShot DSC HX9V": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4727.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4727.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3554.0 - }, + }, "Sony Cybershot DSC D700": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1365.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1365.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1026.0 - }, + }, "Sony Cybershot DSC D770": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1365.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1365.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1026.0 - }, + }, "Sony Cybershot DSC F505": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Sony Cybershot DSC F505v": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Sony Cybershot DSC F55": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Sony Cybershot DSC F55v": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Sony Cybershot DSC F707": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 2640.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 2640.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1985.0 - }, + }, "Sony Cybershot DSC F717": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 2640.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 2640.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 1985.0 - }, + }, "Sony Cybershot DSC F77": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Sony Cybershot DSC F828": { - "Sensor height (mm)": 6.6, - "Sensor width (mm)": 8.8, - "Sensor res. (width)": 3262.0, - "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", + "Sensor height (mm)": 6.6, + "Sensor width (mm)": 8.8, + "Sensor res. (width)": 3262.0, + "Sensor": "2/3\" (~ 8.8 x 6.6 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Sony Cybershot DSC F88": { - "Sensor height (mm)": 4.43, - "Sensor width (mm)": 5.9, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", + "Sensor height (mm)": 4.43, + "Sensor width (mm)": 5.9, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Sony Cybershot DSC FX77": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Sony Cybershot DSC G1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Sony Cybershot DSC G3": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Sony Cybershot DSC H1": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2643.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2643.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1987.0 - }, + }, "Sony Cybershot DSC H10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3322.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3322.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2498.0 - }, + }, "Sony Cybershot DSC H100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Sony Cybershot DSC H2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Sony Cybershot DSC H20": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Sony Cybershot DSC H200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Sony Cybershot DSC H3": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3262.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3262.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Sony Cybershot DSC H300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Sony Cybershot DSC H400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Sony Cybershot DSC H5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC H50": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Sony Cybershot DSC H55": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Sony Cybershot DSC H7": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sony Cybershot DSC H70": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Sony Cybershot DSC H9": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sony Cybershot DSC H90": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Sony Cybershot DSC HX1": { - "Sensor height (mm)": 4.43, - "Sensor width (mm)": 5.9, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", + "Sensor height (mm)": 4.43, + "Sensor width (mm)": 5.9, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Sony Cybershot DSC HX10V": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4920.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4920.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3699.0 - }, + }, "Sony Cybershot DSC HX200V": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4920.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4920.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3699.0 - }, + }, "Sony Cybershot DSC HX20V": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4920.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4920.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3699.0 - }, + }, "Sony Cybershot DSC HX30V": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5014.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5014.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3770.0 - }, + }, "Sony Cybershot DSC HX5": { - "Sensor height (mm)": 4.43, - "Sensor width (mm)": 5.9, - "Sensor res. (width)": 3755.0, - "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", + "Sensor height (mm)": 4.43, + "Sensor width (mm)": 5.9, + "Sensor res. (width)": 3755.0, + "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", "Sensor res. (height)": 2823.0 - }, + }, "Sony Cybershot DSC HX50": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5208.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5208.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3916.0 - }, + }, "Sony Cybershot DSC HX60": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5208.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5208.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3916.0 - }, + }, "Sony Cybershot DSC J10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Sony Cybershot DSC L1": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Sony Cybershot DSC M1": { - "Sensor height (mm)": 4.43, - "Sensor width (mm)": 5.9, - "Sensor res. (width)": 2629.0, - "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", + "Sensor height (mm)": 4.43, + "Sensor width (mm)": 5.9, + "Sensor res. (width)": 2629.0, + "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Sony Cybershot DSC M2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2655.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2655.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1996.0 - }, + }, "Sony Cybershot DSC N1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sony Cybershot DSC N2": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 3678.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 3678.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 2745.0 - }, + }, "Sony Cybershot DSC P1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Sony Cybershot DSC P10": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2629.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2629.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Sony Cybershot DSC P100": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2604.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2604.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Sony Cybershot DSC P12": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2629.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2629.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Sony Cybershot DSC P120": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2604.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2604.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Sony Cybershot DSC P150": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3095.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3095.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC P2": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Sony Cybershot DSC P20": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 989.0 - }, + }, "Sony Cybershot DSC P200": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Sony Cybershot DSC P3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1930.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1930.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1451.0 - }, + }, "Sony Cybershot DSC P30": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 989.0 - }, + }, "Sony Cybershot DSC P31": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Sony Cybershot DSC P32": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Sony Cybershot DSC P41": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Sony Cybershot DSC P43": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Sony Cybershot DSC P5": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Sony Cybershot DSC P50": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Sony Cybershot DSC P51": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Sony Cybershot DSC P52": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Sony Cybershot DSC P7": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2063.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2063.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Sony Cybershot DSC P71": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Sony Cybershot DSC P72": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Sony Cybershot DSC P73": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Sony Cybershot DSC P8": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Sony Cybershot DSC P9": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Sony Cybershot DSC P92": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2629.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2629.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Sony Cybershot DSC P93": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2604.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2604.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Sony Cybershot DSC R1": { - "Sensor height (mm)": 14.4, - "Sensor width (mm)": 21.5, - "Sensor res. (width)": 3861.0, - "Sensor": "21.5 x 14.4 mm", + "Sensor height (mm)": 14.4, + "Sensor width (mm)": 21.5, + "Sensor res. (width)": 3861.0, + "Sensor": "21.5 x 14.4 mm", "Sensor res. (height)": 2591.0 - }, + }, "Sony Cybershot DSC RX100": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 5505.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 5505.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3670.0 - }, + }, "Sony Cybershot DSC RX100 II": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 5505.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 5505.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3670.0 - }, + }, "Sony Cybershot DSC RX1R": { - "Sensor height (mm)": 23.9, - "Sensor width (mm)": 35.8, - "Sensor res. (width)": 6038.0, - "Sensor": "35.8 x 23.9 mm", + "Sensor height (mm)": 23.9, + "Sensor width (mm)": 35.8, + "Sensor res. (width)": 6038.0, + "Sensor": "35.8 x 23.9 mm", "Sensor res. (height)": 4025.0 - }, + }, "Sony Cybershot DSC S2000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Sony Cybershot DSC S2100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Sony Cybershot DSC S30": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 989.0 - }, + }, "Sony Cybershot DSC S3000": { - "Sensor height (mm)": 3.72, - "Sensor width (mm)": 4.96, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.9\" (~ 4.96 x 3.72 mm)", + "Sensor height (mm)": 3.72, + "Sensor width (mm)": 4.96, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.9\" (~ 4.96 x 3.72 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Sony Cybershot DSC S40": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Sony Cybershot DSC S45": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2643.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2643.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1987.0 - }, + }, "Sony Cybershot DSC S50": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1676.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1676.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1260.0 - }, + }, "Sony Cybershot DSC S5000": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Sony Cybershot DSC S60": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Sony Cybershot DSC S600": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Sony Cybershot DSC S650": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC S70": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Sony Cybershot DSC S700": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC S730": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC S75": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Sony Cybershot DSC S750": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC S780": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sony Cybershot DSC S80": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Sony Cybershot DSC S800": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sony Cybershot DSC S85": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Sony Cybershot DSC S90": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2371.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2371.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1783.0 - }, + }, "Sony Cybershot DSC S930": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Sony Cybershot DSC S950": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Sony Cybershot DSC S980": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3995.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3995.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3004.0 - }, + }, "Sony Cybershot DSC T1": { - "Sensor height (mm)": 4.43, - "Sensor width (mm)": 5.9, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", + "Sensor height (mm)": 4.43, + "Sensor width (mm)": 5.9, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Sony Cybershot DSC T10": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3137.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3137.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2359.0 - }, + }, "Sony Cybershot DSC T100": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC T11": { - "Sensor height (mm)": 4.43, - "Sensor width (mm)": 5.9, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", + "Sensor height (mm)": 4.43, + "Sensor width (mm)": 5.9, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Sony Cybershot DSC T110": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Sony Cybershot DSC T2": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sony Cybershot DSC T20": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC T200": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sony Cybershot DSC T3": { - "Sensor height (mm)": 4.43, - "Sensor width (mm)": 5.9, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", + "Sensor height (mm)": 4.43, + "Sensor width (mm)": 5.9, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Sony Cybershot DSC T30": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC T300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Sony Cybershot DSC T33": { - "Sensor height (mm)": 4.43, - "Sensor width (mm)": 5.9, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", + "Sensor height (mm)": 4.43, + "Sensor width (mm)": 5.9, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Sony Cybershot DSC T5": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Sony Cybershot DSC T50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC T500": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Sony Cybershot DSC T7": { - "Sensor height (mm)": 4.43, - "Sensor width (mm)": 5.9, - "Sensor res. (width)": 2604.0, - "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", + "Sensor height (mm)": 4.43, + "Sensor width (mm)": 5.9, + "Sensor res. (width)": 2604.0, + "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Sony Cybershot DSC T70": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sony Cybershot DSC T700": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Sony Cybershot DSC T77": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3701.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3701.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2783.0 - }, + }, "Sony Cybershot DSC T9": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Sony Cybershot DSC T90": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Sony Cybershot DSC T900": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Sony Cybershot DSC T99": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Sony Cybershot DSC TX1": { - "Sensor height (mm)": 4.43, - "Sensor width (mm)": 5.9, - "Sensor res. (width)": 3755.0, - "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", + "Sensor height (mm)": 4.43, + "Sensor width (mm)": 5.9, + "Sensor res. (width)": 3755.0, + "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", "Sensor res. (height)": 2823.0 - }, + }, "Sony Cybershot DSC TX10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Sony Cybershot DSC TX100V": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Sony Cybershot DSC TX20": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Sony Cybershot DSC TX200V": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4920.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4920.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3699.0 - }, + }, "Sony Cybershot DSC TX5": { - "Sensor height (mm)": 4.43, - "Sensor width (mm)": 5.9, - "Sensor res. (width)": 3755.0, - "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", + "Sensor height (mm)": 4.43, + "Sensor width (mm)": 5.9, + "Sensor res. (width)": 3755.0, + "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", "Sensor res. (height)": 2823.0 - }, + }, "Sony Cybershot DSC TX55": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4612.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4612.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3468.0 - }, + }, "Sony Cybershot DSC TX66": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4920.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4920.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3699.0 - }, + }, "Sony Cybershot DSC TX7": { - "Sensor height (mm)": 4.43, - "Sensor width (mm)": 5.9, - "Sensor res. (width)": 3683.0, - "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", + "Sensor height (mm)": 4.43, + "Sensor width (mm)": 5.9, + "Sensor res. (width)": 3683.0, + "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", "Sensor res. (height)": 2769.0 - }, + }, "Sony Cybershot DSC TX9": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Sony Cybershot DSC U10": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1315.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1315.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 989.0 - }, + }, "Sony Cybershot DSC U20": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Sony Cybershot DSC U30": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Sony Cybershot DSC U40": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Sony Cybershot DSC U50": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Sony Cybershot DSC U60": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Sony Cybershot DSC V1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2629.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2629.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1977.0 - }, + }, "Sony Cybershot DSC V3": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3072.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3072.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2310.0 - }, + }, "Sony Cybershot DSC W1": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2604.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2604.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Sony Cybershot DSC W100": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sony Cybershot DSC W110": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC W115": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC W12": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2635.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2635.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1981.0 - }, + }, "Sony Cybershot DSC W120": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC W125": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC W130": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sony Cybershot DSC W150": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sony Cybershot DSC W17": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3095.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3095.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC W170": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Sony Cybershot DSC W180": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 3665.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 3665.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 2756.0 - }, + }, "Sony Cybershot DSC W190": { - "Sensor height (mm)": 4.56, - "Sensor width (mm)": 6.08, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", + "Sensor height (mm)": 4.56, + "Sensor width (mm)": 6.08, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.33\" (~ 6.08 x 4.56 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Sony Cybershot DSC W200": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4027.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4027.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3005.0 - }, + }, "Sony Cybershot DSC W210": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Sony Cybershot DSC W215": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Sony Cybershot DSC W220": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Sony Cybershot DSC W230": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Sony Cybershot DSC W270": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4043.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4043.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3017.0 - }, + }, "Sony Cybershot DSC W275": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Sony Cybershot DSC W290": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Sony Cybershot DSC W30": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Sony Cybershot DSC W300": { - "Sensor height (mm)": 5.64, - "Sensor width (mm)": 7.53, - "Sensor res. (width)": 4316.0, - "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", + "Sensor height (mm)": 5.64, + "Sensor width (mm)": 7.53, + "Sensor res. (width)": 4316.0, + "Sensor": "1/1.7\" (~ 7.53 x 5.64 mm)", "Sensor res. (height)": 3221.0 - }, + }, "Sony Cybershot DSC W310": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4060.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4060.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3053.0 - }, + }, "Sony Cybershot DSC W320": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Sony Cybershot DSC W330": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Sony Cybershot DSC W35": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC W350": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Sony Cybershot DSC W360": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Sony Cybershot DSC W370": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Sony Cybershot DSC W380": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Sony Cybershot DSC W40": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Sony Cybershot DSC W5": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2604.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2604.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Sony Cybershot DSC W50": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Sony Cybershot DSC W510": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4011.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4011.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3016.0 - }, + }, "Sony Cybershot DSC W520": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Sony Cybershot DSC W530": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Sony Cybershot DSC W55": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC W550": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4330.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4330.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3256.0 - }, + }, "Sony Cybershot DSC W560": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Sony Cybershot DSC W570": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Sony Cybershot DSC W580": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Sony Cybershot DSC W610": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Sony Cybershot DSC W620": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4392.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4392.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3302.0 - }, + }, "Sony Cybershot DSC W630": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Sony Cybershot DSC W650": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4671.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4671.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3512.0 - }, + }, "Sony Cybershot DSC W670": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Sony Cybershot DSC W690": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Sony Cybershot DSC W7": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3095.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3095.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC W70": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC W710": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Sony Cybershot DSC W730": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Sony Cybershot DSC W80": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC W800": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Sony Cybershot DSC W810": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Sony Cybershot DSC W830": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5171.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5171.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3888.0 - }, + }, "Sony Cybershot DSC W85": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3095.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3095.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2327.0 - }, + }, "Sony Cybershot DSC W90": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3282.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3282.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Sony Cybershot DSC WX1": { - "Sensor height (mm)": 4.43, - "Sensor width (mm)": 5.9, - "Sensor res. (width)": 3755.0, - "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", + "Sensor height (mm)": 4.43, + "Sensor width (mm)": 5.9, + "Sensor res. (width)": 3755.0, + "Sensor": "1/2.4\" (~ 5.90 x 4.43 mm)", "Sensor res. (height)": 2823.0 - }, + }, "Sony Cybershot DSC WX10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Sony Cybershot DSC WX100": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4893.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4893.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3679.0 - }, + }, "Sony Cybershot DSC WX150": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4920.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4920.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3699.0 - }, + }, "Sony Cybershot DSC WX220": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4920.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4920.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3699.0 - }, + }, "Sony Cybershot DSC WX30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Sony Cybershot DSC WX350": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4920.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4920.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3699.0 - }, + }, "Sony Cybershot DSC WX5": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4029.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4029.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3029.0 - }, + }, "Sony Cybershot DSC WX50": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4727.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4727.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3554.0 - }, + }, "Sony Cybershot DSC WX7": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Sony Cybershot DSC WX70": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4727.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4727.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3554.0 - }, + }, "Sony Cybershot DSC WX9": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Sony Cybershot DSC-HX300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5208.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5208.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3916.0 - }, + }, "Sony Cybershot DSC-HX400": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5208.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5208.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3916.0 - }, + }, "Sony Cybershot DSC-QX10": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4920.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4920.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3699.0 - }, + }, "Sony Cybershot DSC-QX100": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 5505.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 5505.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3670.0 - }, + }, "Sony Cybershot DSC-RX10": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 5505.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 5505.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3670.0 - }, + }, "Sony Cybershot DSC-RX100 III": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 5492.0, - "Sensor": "13.2 x 8.8 mm", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 5492.0, + "Sensor": "13.2 x 8.8 mm", "Sensor res. (height)": 3661.0 - }, + }, "Sony Cybershot DSC-TF1": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4627.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4627.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3479.0 - }, + }, "Sony Cybershot DSC-TX30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4920.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4920.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3699.0 - }, + }, "Sony Cybershot DSC-WX200": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4920.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4920.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3699.0 - }, + }, "Sony Cybershot DSC-WX300": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4920.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4920.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3699.0 - }, + }, "Sony Cybershot DSC-WX60": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Sony Cybershot DSC-WX80": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 4642.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 4642.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3490.0 - }, + }, "Sony Mavica CD1000": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Sony Mavica CD200": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Sony Mavica CD250": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Sony Mavica CD300": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2031.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2031.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Sony Mavica CD350": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Sony Mavica CD400": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2277.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2277.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1712.0 - }, + }, "Sony Mavica CD500": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Sony Mavica FD-100": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Sony Mavica FD-200": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Sony Mavica FD-71": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 632.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 632.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 475.0 - }, + }, "Sony Mavica FD-73": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 632.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 632.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 475.0 - }, + }, "Sony Mavica FD-75": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 632.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 632.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 475.0 - }, + }, "Sony Mavica FD-81": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 964.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 964.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 725.0 - }, + }, "Sony Mavica FD-83": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 964.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 964.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 725.0 - }, + }, "Sony Mavica FD-85": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Sony Mavica FD-87": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Sony Mavica FD-88": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 1264.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 1264.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 950.0 - }, + }, "Sony Mavica FD-90": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Sony Mavica FD-91": { - "Sensor height (mm)": 3.6, - "Sensor width (mm)": 4.8, - "Sensor res. (width)": 964.0, - "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", + "Sensor height (mm)": 3.6, + "Sensor width (mm)": 4.8, + "Sensor res. (width)": 964.0, + "Sensor": "1/3\" (~ 4.8 x 3.6 mm)", "Sensor res. (height)": 725.0 - }, + }, "Sony Mavica FD-92": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1264.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1264.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 950.0 - }, + }, "Sony Mavica FD-95": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Sony Mavica FD-97": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1589.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1589.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1195.0 - }, + }, "Sony QX1": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 5508.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 5508.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3648.0 - }, + }, "Sony QX30": { - "Sensor height (mm)": 4.62, - "Sensor width (mm)": 6.16, - "Sensor res. (width)": 5208.0, - "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", + "Sensor height (mm)": 4.62, + "Sensor width (mm)": 6.16, + "Sensor res. (width)": 5208.0, + "Sensor": "1/2.3\" (~ 6.16 x 4.62 mm)", "Sensor res. (height)": 3916.0 - }, + }, "Sony SLT-A33": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 4631.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 4631.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3067.0 - }, + }, "Sony SLT-A35": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 4945.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 4945.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3275.0 - }, + }, "Sony SLT-A37": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 4930.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 4930.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3265.0 - }, + }, "Sony SLT-A55": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 4945.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 4945.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3275.0 - }, + }, "Sony SLT-A57": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 4930.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 4930.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 3265.0 - }, + }, "Sony SLT-A58": { - "Sensor height (mm)": 15.4, - "Sensor width (mm)": 23.2, - "Sensor res. (width)": 5508.0, - "Sensor": "23.2 x 15.4 mm", + "Sensor height (mm)": 15.4, + "Sensor width (mm)": 23.2, + "Sensor res. (width)": 5508.0, + "Sensor": "23.2 x 15.4 mm", "Sensor res. (height)": 3648.0 - }, + }, "Sony SLT-A65": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 6058.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 6058.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 4012.0 - }, + }, "Sony SLT-A77": { - "Sensor height (mm)": 15.6, - "Sensor width (mm)": 23.5, - "Sensor res. (width)": 6058.0, - "Sensor": "23.5 x 15.6 mm", + "Sensor height (mm)": 15.6, + "Sensor width (mm)": 23.5, + "Sensor res. (width)": 6058.0, + "Sensor": "23.5 x 15.6 mm", "Sensor res. (height)": 4012.0 - }, + }, "Sony SLT-A99": { - "Sensor height (mm)": 23.8, - "Sensor width (mm)": 35.8, - "Sensor res. (width)": 6038.0, - "Sensor": "35.8 x 23.8 mm", + "Sensor height (mm)": 23.8, + "Sensor width (mm)": 35.8, + "Sensor res. (width)": 6038.0, + "Sensor": "35.8 x 23.8 mm", "Sensor res. (height)": 4025.0 - }, + }, "Teracube Teracube One": { - "Sensor height (mm)": 3.54, - "Sensor width (mm)": 4.71, - "Sensor res. (width)": 4032.0, - "Sensor": "1/2.8\" (~ 4.71 x 3.54 mm)", + "Sensor height (mm)": 3.54, + "Sensor width (mm)": 4.71, + "Sensor res. (width)": 4032.0, + "Sensor": "1/2.8\" (~ 4.71 x 3.54 mm)", "Sensor res. (height)": 3024.0 - }, + }, "Toshiba PDR 2300": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Toshiba PDR 3300": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2063.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2063.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Toshiba PDR 3310": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Toshiba PDR 3320": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Toshiba PDR 4300": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2363.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2363.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1777.0 - }, + }, "Toshiba PDR 5300": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Toshiba PDR M25": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1710.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1710.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1286.0 - }, + }, "Toshiba PDR M5": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1672.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1672.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Toshiba PDR M500": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Toshiba PDR M60": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1749.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1749.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1315.0 - }, + }, "Toshiba PDR M61": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 1749.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 1749.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1315.0 - }, + }, "Toshiba PDR M65": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2108.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2108.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Toshiba PDR M70": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Toshiba PDR M700": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Toshiba PDR M71": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2108.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2108.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1585.0 - }, + }, "Toshiba PDR M81": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2363.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2363.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1777.0 - }, + }, "Toshiba PDR T10": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Toshiba PDR T20": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 1631.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 1631.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Toshiba PDR T30": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2076.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2076.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1561.0 - }, + }, "Vivitar V8025": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Vivitar ViviCam 5105s": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Vivitar ViviCam 5150s": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Vivitar ViviCam 5160s": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Vivitar ViviCam 5195": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Vivitar ViviCam 5350s": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Vivitar ViviCam 5355": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Vivitar ViviCam 5385": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Vivitar ViviCam 5386": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Vivitar ViviCam 5388": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2579.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2579.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Vivitar ViviCam 6150s": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Vivitar ViviCam 6200w": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Vivitar ViviCam 6300": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Vivitar ViviCam 6320": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Vivitar ViviCam 6326": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Vivitar ViviCam 6330": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Vivitar ViviCam 6380u": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Vivitar ViviCam 6385u": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Vivitar ViviCam 6388s": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2825.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2825.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2124.0 - }, + }, "Vivitar ViviCam 7100s": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Vivitar ViviCam 7310": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3086.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3086.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2320.0 - }, + }, "Vivitar ViviCam 7388s": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Vivitar ViviCam 7500i": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 3051.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 3051.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2294.0 - }, + }, "Vivitar ViviCam 8300s": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Vivitar ViviCam 8400": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Vivitar ViviCam 8600": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Vivitar ViviCam 8600s": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Vivitar ViviCam 8625": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3282.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3282.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2468.0 - }, + }, "Vivitar ViviCam X30": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Vivitar ViviCam X60": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3647.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3647.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2742.0 - }, + }, "Yakumo CamMaster SD 432": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2063.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2063.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1551.0 - }, + }, "Yakumo CamMaster SD 482": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Yakumo Mega Image 34": { - "Sensor height (mm)": 4.0, - "Sensor width (mm)": 5.33, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", + "Sensor height (mm)": 4.0, + "Sensor width (mm)": 5.33, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2.7\" (~ 5.33 x 4 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Yakumo Mega Image 35": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2095.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2095.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1575.0 - }, + }, "Yakumo Mega Image 37": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Yakumo Mega Image 410": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2335.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2335.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Yakumo Mega Image 45": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2306.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2306.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Yakumo Mega Image 47": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Yakumo Mega Image 47 SL": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Yakumo Mega Image 47sx": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2335.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2335.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1756.0 - }, + }, "Yakumo Mega Image 55cx": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Yakumo Mega Image 57": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Yakumo Mega Image 57x": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Yakumo Mega Image 610x": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2909.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2909.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2187.0 - }, + }, "Yakumo Mega Image 67x": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2671.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2671.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2008.0 - }, + }, "Yakumo Mega Image 811x": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 3262.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 3262.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 2453.0 - }, + }, "Yakumo Mega Image 84 D": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2306.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2306.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 1734.0 - }, + }, "Yakumo Mega Image 85D": { - "Sensor height (mm)": 4.32, - "Sensor width (mm)": 5.75, - "Sensor res. (width)": 2671.0, - "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", + "Sensor height (mm)": 4.32, + "Sensor width (mm)": 5.75, + "Sensor res. (width)": 2671.0, + "Sensor": "1/2.5\" (~ 5.75 x 4.32 mm)", "Sensor res. (height)": 2008.0 - }, + }, "Yakumo Mega Image II": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1672.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1672.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1257.0 - }, + }, "Yakumo Mega Image IV": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1631.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1631.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1226.0 - }, + }, "Yakumo Mega Image VI": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 1998.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 1998.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1502.0 - }, + }, "Yakumo Mega Image VII": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Yakumo Mega Image X": { - "Sensor height (mm)": 5.33, - "Sensor width (mm)": 7.11, - "Sensor res. (width)": 2579.0, - "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", + "Sensor height (mm)": 5.33, + "Sensor width (mm)": 7.11, + "Sensor res. (width)": 2579.0, + "Sensor": "1/1.8\" (~ 7.11 x 5.33 mm)", "Sensor res. (height)": 1939.0 - }, + }, "Yakumo Mega Image XL": { - "Sensor height (mm)": 3.17, - "Sensor width (mm)": 4.23, - "Sensor res. (width)": 2604.0, - "Sensor": "1/3.4\" (~ 4.23 x 3.17 mm)", + "Sensor height (mm)": 3.17, + "Sensor width (mm)": 4.23, + "Sensor res. (width)": 2604.0, + "Sensor": "1/3.4\" (~ 4.23 x 3.17 mm)", "Sensor res. (height)": 1958.0 - }, + }, "Yakumo Mega Image XS": { - "Sensor height (mm)": 4.8, - "Sensor width (mm)": 6.4, - "Sensor res. (width)": 2031.0, - "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", + "Sensor height (mm)": 4.8, + "Sensor width (mm)": 6.4, + "Sensor res. (width)": 2031.0, + "Sensor": "1/2\" (~ 6.4 x 4.8 mm)", "Sensor res. (height)": 1527.0 - }, + }, "Yuneec E90": { - "Sensor height (mm)": 8.8, - "Sensor width (mm)": 13.2, - "Sensor res. (width)": 5472.0, - "Sensor": "1\" (~ 13.2 x 8.8 mm)", + "Sensor height (mm)": 8.8, + "Sensor width (mm)": 13.2, + "Sensor res. (width)": 5472.0, + "Sensor": "1\" (~ 13.2 x 8.8 mm)", "Sensor res. (height)": 3648.0 - }, + }, "GITUP GIT2": { - "Sensor height (mm)": 4.662, - "Sensor width (mm)": 6.216, - "Sensor res. (width)": 4608, - "Sensor": "1/2.3", + "Sensor height (mm)": 4.662, + "Sensor width (mm)": 6.216, + "Sensor res. (width)": 4608, + "Sensor": "1/2.3", "Sensor res. (height)": 3456 } -} +} \ No newline at end of file diff --git a/opensfm/dataset.py b/opensfm/dataset.py index b653ae75f..9c75e9699 100644 --- a/opensfm/dataset.py +++ b/opensfm/dataset.py @@ -1,23 +1,15 @@ +# pyre-strict import gzip import json import logging import os import pickle from io import BytesIO -from typing import Dict, List, Tuple, Optional, IO, Any +from typing import Any, BinaryIO, Dict, List, Optional, Tuple import numpy as np -from opensfm import ( - config, - features, - geo, - io, - pygeometry, - types, - pymap, - masking, - rig, -) +from numpy.typing import NDArray +from opensfm import config, features, geo, io, masking, pygeometry, pymap, rig, types from opensfm.dataset_base import DataSetBase from PIL.PngImagePlugin import PngImageFile import hashlib @@ -40,14 +32,17 @@ class DataSet(DataSetBase): """ io_handler: io.IoFilesystemBase = io.IoFilesystemDefault() - config = None + config: Dict[str, Any] = {} image_files: Dict[str, str] = {} mask_files: Dict[str, str] = {} image_list: List[str] = [] - def __init__(self, data_path: str, io_handler=io.IoFilesystemDefault) -> None: + def __init__( + self, data_path: str, io_handler: Optional[io.IoFilesystemBase] = None + ) -> None: """Init dataset associated to a folder.""" - self.io_handler = io_handler + if io_handler is not None: + self.io_handler = io_handler self.data_path = data_path self.load_config() self.load_image_list() @@ -59,7 +54,7 @@ def _config_file(self) -> str: def load_config(self) -> None: config_file_path = self._config_file() if self.io_handler.isfile(config_file_path): - with self.io_handler.open(config_file_path) as f: + with self.io_handler.open_rt(config_file_path) as f: self.config = config.load_config_from_fileobject(f) else: self.config = config.default_config() @@ -90,9 +85,9 @@ def _image_file(self, image: str) -> str: """Path to the image file.""" return self.image_files[image] - def open_image_file(self, image: str) -> IO[Any]: + def open_image_file(self, image: str) -> BinaryIO: """Open image file and return file object.""" - return self.io_handler.open(self._image_file(image), "rb") + return self.io_handler.open_rb(self._image_file(image)) def load_image( self, @@ -100,7 +95,7 @@ def load_image( unchanged: bool = False, anydepth: bool = False, grayscale: bool = False, - ) -> np.ndarray: + ) -> NDArray: """Load image pixels as numpy array. The array is 3D, indexed by y-coord, x-coord, channel. @@ -127,15 +122,16 @@ def load_mask_list(self) -> None: else: self._set_mask_path(os.path.join(self.data_path, "masks")) - def load_mask(self, image: str) -> Optional[np.ndarray]: + def load_mask(self, image: str) -> Optional[NDArray]: """Load image mask if it exists, otherwise return None.""" if image in self.mask_files: mask_path = self.mask_files[image] mask = self.io_handler.imread(mask_path, grayscale=True) if mask is None: raise IOError( - "Unable to load mask for image {} " - "from file {}".format(image, mask_path) + "Unable to load mask for image {} " "from file {}".format( + image, mask_path + ) ) else: mask = None @@ -147,7 +143,7 @@ def _instances_path(self) -> str: def _instances_file(self, image: str) -> str: return os.path.join(self._instances_path(), image + ".png") - def load_instances(self, image: str) -> Optional[np.ndarray]: + def load_instances(self, image: str) -> Optional[NDArray]: """Load image instances file if it exists, otherwise return None.""" instances_file = self._instances_file(image) if self.io_handler.isfile(instances_file): @@ -162,14 +158,14 @@ def _segmentation_path(self) -> str: def _segmentation_file(self, image: str) -> str: return os.path.join(self._segmentation_path(), image + ".png") - def segmentation_labels(self) -> List[Any]: + def segmentation_labels(self) -> List[Dict[str, Any]]: return [] - def load_segmentation(self, image: str) -> Optional[np.ndarray]: + def load_segmentation(self, image: str) -> Optional[NDArray]: """Load image segmentation if it exists, otherwise return None.""" segmentation_file = self._segmentation_file(image) if self.io_handler.isfile(segmentation_file): - with self.io_handler.open(segmentation_file, "rb") as fp: + with self.io_handler.open_rb(segmentation_file) as fp: with PngImageFile(fp) as png_image: # TODO: We do not write a header tag in the metadata. Might be good safety check. data = np.array(png_image) @@ -301,7 +297,7 @@ def _save_features( self, filepath: str, features_data: features.FeaturesData ) -> None: self.io_handler.mkdir_p(self._feature_path()) - with self.io_handler.open(filepath, "wb") as fwb: + with self.io_handler.open_wb(filepath) as fwb: features_data.save(fwb, self.config) def features_exist(self, image: str) -> bool: @@ -315,7 +311,7 @@ def load_features(self, image: str) -> Optional[features.FeaturesData]: if self.io_handler.isfile(self._feature_file_legacy(image)) else self._feature_file(image) ) - with self.io_handler.open(features_filepath, "rb") as f: + with self.io_handler.open_rb(features_filepath) as f: return features.FeaturesData.from_file(f, self.config) def save_features(self, image: str, features_data: features.FeaturesData) -> None: @@ -327,13 +323,13 @@ def _words_file(self, image): def words_exist(self, image): return self.io_handler.isfile(self._words_file(image)) - def load_words(self, image: str) -> np.ndarray: - with self.io_handler.open(self._words_file(image), "rb") as f: + def load_words(self, image: str) -> NDArray: + with self.io_handler.open_rb(self._words_file(image)) as f: s = np.load(f) return s["words"].astype(np.int32) - def save_words(self, image: str, words: np.ndarray) -> None: - with self.io_handler.open(self._words_file(image), "wb") as f: + def save_words(self, image: str, words: NDArray) -> None: + with self.io_handler.open_wb(self._words_file(image)) as f: np.savez_compressed(f, words=words.astype(np.uint16)) def _matches_path(self) -> str: @@ -347,16 +343,20 @@ def _matches_file(self, image: str) -> str: def matches_exists(self, image: str) -> bool: return self.io_handler.isfile(self._matches_file(image)) - def load_matches(self, image: str) -> Dict[str, np.ndarray]: + def load_matches(self, image: str) -> Dict[str, NDArray]: # Prevent pickling of anything except what we strictly need # as 'pickle.load' is RCE-prone. Will raise on any class other # than the numpy ones we allow. class MatchingUnpickler(pickle.Unpickler): + # Handle both numpy <2.0 (np.core) and numpy >=2.0 (np._core) + _multiarray = ( + np.core.multiarray if hasattr(np, "core") else np._core.multiarray + ) modules_map = { - "numpy.core.multiarray._reconstruct": np.core.multiarray, - "numpy.core.multiarray.scalar": np.core.multiarray, - "numpy._core.multiarray._reconstruct": np.core.multiarray, - "numpy._core.multiarray.scalar": np.core.multiarray, + "numpy.core.multiarray._reconstruct": _multiarray, + "numpy.core.multiarray.scalar": _multiarray, + "numpy._core.multiarray._reconstruct": _multiarray, + "numpy._core.multiarray.scalar": _multiarray, "numpy.ndarray": np, "numpy.dtype": np, } @@ -370,20 +370,20 @@ def find_class(self, module, name): ) return getattr(self.modules_map[classname], name) - with self.io_handler.open(self._matches_file(image), "rb") as fin: + with self.io_handler.open_rb(self._matches_file(image)) as fin: matches = MatchingUnpickler(BytesIO(gzip.decompress(fin.read()))).load() return matches - def save_matches(self, image: str, matches: Dict[str, np.ndarray]) -> None: + def save_matches(self, image: str, matches: Dict[str, NDArray]) -> None: self.io_handler.mkdir_p(self._matches_path()) with BytesIO() as buffer: with gzip.GzipFile(fileobj=buffer, mode="w") as fzip: pickle.dump(matches, fzip) - with self.io_handler.open(self._matches_file(image), "wb") as fw: + with self.io_handler.open_wb(self._matches_file(image)) as fw: fw.write(buffer.getvalue()) - def find_matches(self, im1: str, im2: str) -> np.ndarray: + def find_matches(self, im1: str, im2: str) -> NDArray: if self.matches_exists(im1): im1_matches = self.load_matches(im1) if im2 in im1_matches: @@ -435,7 +435,7 @@ def save_reconstruction( self, reconstruction: List[types.Reconstruction], filename: Optional[str] = None, - minify=False, + minify: bool = False, ) -> None: with self.io_handler.open_wt(self._reconstruction_file(filename)) as fout: io.json_dump(io.reconstructions_to_json(reconstruction), fout, minify) @@ -575,7 +575,7 @@ def save_rig_assignments( def append_to_profile_log(self, content: str) -> None: """Append content to the profile.log file.""" path = os.path.join(self.data_path, "profile.log") - with self.io_handler.open(path, "a") as fp: + with self.io_handler.open_at(path) as fp: fp.write(content) def _report_path(self) -> str: @@ -591,7 +591,7 @@ def save_report(self, report_str: str, path: str) -> None: filepath = os.path.join(self._report_path(), path) self.io_handler.mkdir_p(os.path.dirname(filepath)) with self.io_handler.open_wt(filepath) as fout: - return fout.write(report_str) + fout.write(report_str) def _ply_file(self, filename: Optional[str]) -> str: return os.path.join(self.data_path, filename or "reconstruction.ply") @@ -641,11 +641,29 @@ def save_ground_control_points( with self.io_handler.open_wt(self._ground_control_points_file()) as fout: io.write_ground_control_points(points, fout) - def image_as_array(self, image: str) -> np.ndarray: + def _stats_file(self) -> str: + return os.path.join(self.data_path, "stats", "stats.json") + + def load_stats(self) -> Dict[str, Any]: + """Load statistics from stats/stats.json.""" + stats_file = self._stats_file() + if self.io_handler.isfile(stats_file): + with self.io_handler.open_rt(stats_file) as fin: + return io.json_load(fin) + return {} + + def save_stats(self, stats: Dict[str, Any]) -> None: + """Save statistics to stats/stats.json.""" + stats_file = self._stats_file() + self.io_handler.mkdir_p(os.path.dirname(stats_file)) + with self.io_handler.open_wt(stats_file) as fout: + io.json_dump(stats, fout) + + def image_as_array(self, image: str) -> NDArray: logger.warning("image_as_array() is deprecated. Use load_image() instead.") return self.load_image(image) - def mask_as_array(self, image: str) -> Optional[np.ndarray]: + def mask_as_array(self, image: str) -> Optional[NDArray]: logger.warning("mask_as_array() is deprecated. Use load_mask() instead.") return self.load_mask(image) @@ -707,7 +725,7 @@ def undistorted_dataset(self) -> "UndistortedDataSet": ) -class UndistortedDataSet(object): +class UndistortedDataSet: """Accessors to the undistorted data of a dataset. Data include undistorted images, masks, and segmentation as well @@ -720,18 +738,20 @@ class UndistortedDataSet(object): base: DataSetBase config: Dict[str, Any] = {} data_path: str + io_handler: io.IoFilesystemBase = io.IoFilesystemDefault() def __init__( self, base_dataset: DataSetBase, undistorted_data_path: str, - io_handler=io.IoFilesystemDefault, + io_handler: Optional[io.IoFilesystemBase] = None, ) -> None: """Init dataset associated to a folder.""" self.base = base_dataset self.config = self.base.config self.data_path = undistorted_data_path - self.io_handler = io_handler + if io_handler is not None: + self.io_handler = io_handler def load_undistorted_shot_ids(self) -> Dict[str, List[str]]: filename = os.path.join(self.data_path, "undistorted_shot_ids.json") @@ -754,11 +774,11 @@ def _undistorted_image_file(self, image: str) -> str: image = p.replace(' ', '') + hashlib.md5(image.encode('utf8')).hexdigest() + ext return os.path.join(self._undistorted_image_path(), image) - def load_undistorted_image(self, image: str) -> np.ndarray: + def load_undistorted_image(self, image: str) -> NDArray: """Load undistorted image pixels as a numpy array.""" return self.io_handler.imread(self._undistorted_image_file(image)) - def save_undistorted_image(self, image: str, array: np.ndarray) -> None: + def save_undistorted_image(self, image: str, array: NDArray) -> None: """Save undistorted image pixels.""" self.io_handler.mkdir_p(self._undistorted_image_path()) self.io_handler.imwrite(self._undistorted_image_file(image), array) @@ -778,13 +798,13 @@ def undistorted_mask_exists(self, image: str) -> bool: """Check if the undistorted mask file exists.""" return self.io_handler.isfile(self._undistorted_mask_file(image)) - def load_undistorted_mask(self, image: str) -> np.ndarray: + def load_undistorted_mask(self, image: str) -> NDArray: """Load undistorted mask pixels as a numpy array.""" return self.io_handler.imread( self._undistorted_mask_file(image), grayscale=True ) - def save_undistorted_mask(self, image: str, array: np.ndarray) -> None: + def save_undistorted_mask(self, image: str, array: NDArray) -> None: """Save the undistorted image mask.""" self.io_handler.mkdir_p(self._undistorted_mask_path()) self.io_handler.imwrite(self._undistorted_mask_file(image), array) @@ -800,10 +820,10 @@ def undistorted_segmentation_exists(self, image: str) -> bool: """Check if the undistorted segmentation file exists.""" return self.io_handler.isfile(self._undistorted_segmentation_file(image)) - def load_undistorted_segmentation(self, image: str) -> np.ndarray: + def load_undistorted_segmentation(self, image: str) -> NDArray: """Load an undistorted image segmentation.""" segmentation_file = self._undistorted_segmentation_file(image) - with self.io_handler.open(segmentation_file, "rb") as fp: + with self.io_handler.open_rb(segmentation_file) as fp: with PngImageFile(fp) as png_image: # TODO: We do not write a header tag in the metadata. Might be good safety check. data = np.array(png_image) @@ -820,12 +840,12 @@ def load_undistorted_segmentation(self, image: str) -> np.ndarray: else: raise IndexError - def save_undistorted_segmentation(self, image: str, array: np.ndarray) -> None: + def save_undistorted_segmentation(self, image: str, array: NDArray) -> None: """Save the undistorted image segmentation.""" self.io_handler.mkdir_p(self._undistorted_segmentation_path()) self.io_handler.imwrite(self._undistorted_segmentation_file(image), array) - def load_undistorted_segmentation_mask(self, image: str) -> Optional[np.ndarray]: + def load_undistorted_segmentation_mask(self, image: str) -> Optional[NDArray]: """Build a mask from the undistorted segmentation. The mask is non-zero only for pixels with segmentation @@ -844,7 +864,7 @@ def load_undistorted_segmentation_mask(self, image: str) -> Optional[np.ndarray] return masking.mask_from_segmentation(segmentation, ignore_values) - def load_undistorted_combined_mask(self, image: str) -> Optional[np.ndarray]: + def load_undistorted_combined_mask(self, image: str) -> Optional[NDArray]: """Combine undistorted binary mask with segmentation mask. Return a mask that is non-zero only where the binary @@ -870,20 +890,20 @@ def point_cloud_file(self, filename: str = "merged.ply") -> str: def load_point_cloud( self, filename: str = "merged.ply" - ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: - with self.io_handler.open(self.point_cloud_file(filename), "r") as fp: + ) -> Tuple[NDArray, NDArray, NDArray, NDArray]: + with self.io_handler.open_rt(self.point_cloud_file(filename)) as fp: return io.point_cloud_from_ply(fp) def save_point_cloud( self, - points: np.ndarray, - normals: np.ndarray, - colors: np.ndarray, - labels: np.ndarray, + points: NDArray, + normals: NDArray, + colors: NDArray, + labels: NDArray, filename: str = "merged.ply", ) -> None: self.io_handler.mkdir_p(self._depthmap_path()) - with self.io_handler.open(self.point_cloud_file(filename), "w") as fp: + with self.io_handler.open_wt(self.point_cloud_file(filename)) as fp: io.point_cloud_to_ply(points, normals, colors, labels, fp) def raw_depthmap_exists(self, image: str) -> bool: @@ -892,23 +912,23 @@ def raw_depthmap_exists(self, image: str) -> bool: def save_raw_depthmap( self, image: str, - depth: np.ndarray, - plane: np.ndarray, - score: np.ndarray, - nghbr: np.ndarray, - nghbrs: np.ndarray, + depth: NDArray, + plane: NDArray, + score: NDArray, + nghbr: NDArray, + nghbrs: List[str], ) -> None: self.io_handler.mkdir_p(self._depthmap_path()) filepath = self.depthmap_file(image, "raw.npz") - with self.io_handler.open(filepath, "wb") as f: + with self.io_handler.open_wb(filepath) as f: np.savez_compressed( f, depth=depth, plane=plane, score=score, nghbr=nghbr, nghbrs=nghbrs ) def load_raw_depthmap( self, image: str - ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]: - with self.io_handler.open(self.depthmap_file(image, "raw.npz"), "rb") as f: + ) -> Tuple[NDArray, NDArray, NDArray, NDArray, List[str]]: + with self.io_handler.open_rb(self.depthmap_file(image, "raw.npz")) as f: o = np.load(f) return o["depth"], o["plane"], o["score"], o["nghbr"], o["nghbrs"] @@ -916,17 +936,15 @@ def clean_depthmap_exists(self, image: str) -> bool: return self.io_handler.isfile(self.depthmap_file(image, "clean.npz")) def save_clean_depthmap( - self, image: str, depth: np.ndarray, plane: np.ndarray, score: np.ndarray + self, image: str, depth: NDArray, plane: NDArray, score: NDArray ) -> None: self.io_handler.mkdir_p(self._depthmap_path()) filepath = self.depthmap_file(image, "clean.npz") - with self.io_handler.open(filepath, "wb") as f: + with self.io_handler.open_wb(filepath) as f: np.savez_compressed(f, depth=depth, plane=plane, score=score) - def load_clean_depthmap( - self, image: str - ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: - with self.io_handler.open(self.depthmap_file(image, "clean.npz"), "rb") as f: + def load_clean_depthmap(self, image: str) -> Tuple[NDArray, NDArray, NDArray]: + with self.io_handler.open_rb(self.depthmap_file(image, "clean.npz")) as f: o = np.load(f) return o["depth"], o["plane"], o["score"] @@ -936,14 +954,14 @@ def pruned_depthmap_exists(self, image: str) -> bool: def save_pruned_depthmap( self, image: str, - points: np.ndarray, - normals: np.ndarray, - colors: np.ndarray, - labels: np.ndarray, + points: NDArray, + normals: NDArray, + colors: NDArray, + labels: NDArray, ) -> None: self.io_handler.mkdir_p(self._depthmap_path()) filepath = self.depthmap_file(image, "pruned.npz") - with self.io_handler.open(filepath, "wb") as f: + with self.io_handler.open_wb(filepath) as f: np.savez_compressed( f, points=points, @@ -954,8 +972,8 @@ def save_pruned_depthmap( def load_pruned_depthmap( self, image: str - ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: - with self.io_handler.open(self.depthmap_file(image, "pruned.npz"), "rb") as f: + ) -> Tuple[NDArray, NDArray, NDArray, NDArray]: + with self.io_handler.open_rb(self.depthmap_file(image, "pruned.npz")) as f: o = np.load(f) return ( o["points"], @@ -995,8 +1013,12 @@ def save_undistorted_reconstruction( def invent_reference_from_gps_and_gcp( data: DataSetBase, images: Optional[List[str]] = None ) -> geo.TopocentricConverter: - lat, lon, alt = 0.0, 0.0, 0.0 - wlat, wlon, walt = 0.0, 0.0, 0.0 + """Invent the reference from the weighted average of lat/lon measurements. + Most of the time the altitude provided in the metadata is inaccurate, thus + the reference altitude is set equal to 0 regardless of the altitude measurements. + """ + lat, lon = 0.0, 0.0 + wlat, wlon = 0.0, 0.0 if images is None: images = data.images() for image in images: @@ -1007,27 +1029,18 @@ def invent_reference_from_gps_and_gcp( lon += w * d["gps"]["longitude"] wlat += w wlon += w - if "altitude" in d["gps"]: - alt += w * d["gps"]["altitude"] - walt += w if not wlat and not wlon: for gcp in data.load_ground_control_points(): if gcp.lla: lat += gcp.lla["latitude"] lon += gcp.lla["longitude"] - wlat += 1 - wlon += 1 - - if gcp.has_altitude: - alt += gcp.lla["altitude"] - walt += 1 + wlat += 1.0 + wlon += 1.0 if wlat: lat /= wlat if wlon: lon /= wlon - if walt: - alt /= walt - return geo.TopocentricConverter(lat, lon, 0) # Set altitude manually. + return geo.TopocentricConverter(lat, lon, 0) diff --git a/opensfm/dataset_base.py b/opensfm/dataset_base.py index 9f3c64068..f01fadf87 100644 --- a/opensfm/dataset_base.py +++ b/opensfm/dataset_base.py @@ -1,16 +1,10 @@ +# pyre-strict import logging from abc import ABC, abstractmethod -from typing import Dict, List, Tuple, Optional, IO, Any - -import numpy as np -from opensfm import ( - features, - geo, - io, - pygeometry, - types, - pymap, -) +from typing import Any, BinaryIO, Dict, List, Optional, Tuple + +from numpy.typing import NDArray +from opensfm import features, geo, io, pygeometry, pymap, types logger: logging.Logger = logging.getLogger(__name__) @@ -37,7 +31,7 @@ def images(self) -> List[str]: pass @abstractmethod - def open_image_file(self, image: str) -> IO[Any]: + def open_image_file(self, image: str) -> BinaryIO: pass @abstractmethod @@ -47,7 +41,7 @@ def load_image( unchanged: bool = False, anydepth: bool = False, grayscale: bool = False, - ) -> np.ndarray: + ) -> NDArray: pass @abstractmethod @@ -55,19 +49,19 @@ def image_size(self, image: str) -> Tuple[int, int]: pass @abstractmethod - def load_mask(self, image: str) -> Optional[np.ndarray]: + def load_mask(self, image: str) -> Optional[NDArray]: pass @abstractmethod - def load_instances(self, image: str) -> Optional[np.ndarray]: + def load_instances(self, image: str) -> Optional[NDArray]: pass @abstractmethod - def segmentation_labels(self) -> List[Any]: + def segmentation_labels(self) -> List[Dict[str, Any]]: pass @abstractmethod - def load_segmentation(self, image: str) -> Optional[np.ndarray]: + def load_segmentation(self, image: str) -> Optional[NDArray]: pass @abstractmethod @@ -111,11 +105,11 @@ def words_exist(self, image: str) -> bool: pass @abstractmethod - def load_words(self, image: str) -> np.ndarray: + def load_words(self, image: str) -> NDArray: pass @abstractmethod - def save_words(self, image: str, words: np.ndarray) -> None: + def save_words(self, image: str, words: NDArray) -> None: pass @abstractmethod @@ -123,11 +117,11 @@ def matches_exists(self, image: str) -> bool: pass @abstractmethod - def load_matches(self, image: str) -> Dict[str, np.ndarray]: + def load_matches(self, image: str) -> Dict[str, NDArray]: pass @abstractmethod - def save_matches(self, image: str, matches: Dict[str, np.ndarray]) -> None: + def save_matches(self, image: str, matches: Dict[str, NDArray]) -> None: pass @abstractmethod @@ -153,7 +147,7 @@ def save_reconstruction( self, reconstruction: List[types.Reconstruction], filename: Optional[str] = None, - minify=False, + minify: bool = False, ) -> None: pass @@ -245,5 +239,13 @@ def save_ground_control_points( ) -> None: pass + @abstractmethod + def load_stats(self) -> Dict[str, Any]: + pass + + @abstractmethod + def save_stats(self, stats: Dict[str, Any]) -> None: + pass + def clean_up(self) -> None: pass diff --git a/opensfm/dense.py b/opensfm/dense.py index 49cd18365..0f4b69ec0 100644 --- a/opensfm/dense.py +++ b/opensfm/dense.py @@ -1,25 +1,22 @@ +# pyre-strict import logging -import typing as t +from typing import Any, Callable, Dict, Iterable, List, Tuple, Union import cv2 import numpy as np -from opensfm import io -from opensfm import log -from opensfm import pydense -from opensfm import pymap -from opensfm import tracking -from opensfm import types +from numpy.typing import NDArray +from opensfm import io, log, pydense, pymap, tracking, types from opensfm.context import parallel_map from opensfm.dataset import UndistortedDataSet -logger = logging.getLogger(__name__) +logger: logging.Logger = logging.getLogger(__name__) def compute_depthmaps( data: UndistortedDataSet, graph: pymap.TracksManager, reconstruction: types.Reconstruction, -): +) -> None: """Compute and refine depthmaps for all shots. Args: @@ -33,7 +30,7 @@ def compute_depthmaps( num_neighbors = config["depthmap_num_neighbors"] neighbors = {} - common_tracks = common_tracks_double_dict(graph) + common_tracks = common_tracks_double_dict(graph, processes) for shot in reconstruction.shots.values(): neighbors[shot.id] = find_neighboring_images( shot, common_tracks, reconstruction, num_neighbors @@ -65,7 +62,9 @@ def compute_depthmaps( data.save_point_cloud(*point_cloud, filename="merged.ply") -def compute_depthmap_catched(arguments): +def compute_depthmap_catched( + arguments: Tuple[UndistortedDataSet, List[pymap.Shot], float, float, pymap.Shot], +) -> None: try: compute_depthmap(arguments) except Exception as e: @@ -73,7 +72,9 @@ def compute_depthmap_catched(arguments): logger.exception(e) -def clean_depthmap_catched(arguments): +def clean_depthmap_catched( + arguments: Tuple[UndistortedDataSet, List[pymap.Shot], pymap.Shot], +) -> None: try: clean_depthmap(arguments) except Exception as e: @@ -81,7 +82,9 @@ def clean_depthmap_catched(arguments): logger.exception(e) -def prune_depthmap_catched(arguments): +def prune_depthmap_catched( + arguments: Tuple[UndistortedDataSet, List[pymap.Shot], pymap.Shot], +) -> None: try: prune_depthmap(arguments) except Exception as e: @@ -89,15 +92,17 @@ def prune_depthmap_catched(arguments): logger.exception(e) -def compute_depthmap(arguments): +def compute_depthmap( + arguments: Tuple[UndistortedDataSet, List[pymap.Shot], float, float, pymap.Shot], +) -> None: """Compute depthmap for a single shot.""" log.setup() data: UndistortedDataSet = arguments[0] - neighbors = arguments[1] - min_depth = arguments[2] - max_depth = arguments[3] - shot = arguments[4] + neighbors: List[pymap.Shot] = arguments[1] + min_depth: float = arguments[2] + max_depth: float = arguments[3] + shot: pymap.Shot = arguments[4] method = data.config["depthmap_method"] @@ -132,6 +137,7 @@ def compute_depthmap(arguments): neighbor_ids = [i.id for i in neighbors[1:]] data.save_raw_depthmap(shot.id, depth, plane, score, nghbr, neighbor_ids) + image = None if data.config["depthmap_save_debug_files"]: image = data.load_undistorted_image(shot.id) image = scale_down_image(image, depth.shape[1], depth.shape[0]) @@ -144,8 +150,9 @@ def compute_depthmap(arguments): plt.figure() plt.suptitle("Shot: " + shot.id + ", neighbors: " + ", ".join(neighbor_ids)) - plt.subplot(2, 3, 1) - plt.imshow(image) + if image is not None: + plt.subplot(2, 3, 1) + plt.imshow(image) plt.subplot(2, 3, 2) plt.imshow(color_plane_normals(plane)) plt.subplot(2, 3, 3) @@ -160,7 +167,9 @@ def compute_depthmap(arguments): plt.show() -def clean_depthmap(arguments): +def clean_depthmap( + arguments: Tuple[UndistortedDataSet, List[pymap.Shot], pymap.Shot], +) -> None: """Clean depthmap by checking consistency with neighbors.""" log.setup() @@ -204,7 +213,9 @@ def clean_depthmap(arguments): plt.show() -def prune_depthmap(arguments): +def prune_depthmap( + arguments: Tuple[UndistortedDataSet, List[pymap.Shot], pymap.Shot], +) -> None: """Prune depthmap to remove redundant points.""" log.setup() @@ -229,7 +240,10 @@ def prune_depthmap(arguments): data.save_point_cloud(points, normals, colors, labels, "pruned.npz.ply") -def aggregate_depthmaps(shot_ids, depthmap_provider): +def aggregate_depthmaps( + shot_ids: Iterable[str], + depthmap_provider: Callable[[str], Tuple[NDArray, NDArray, NDArray, NDArray]], +) -> Tuple[NDArray, NDArray, NDArray, NDArray]: """Aggregate depthmaps by concatenation.""" points = [] @@ -253,19 +267,20 @@ def aggregate_depthmaps(shot_ids, depthmap_provider): def merge_depthmaps( data: UndistortedDataSet, reconstruction: types.Reconstruction -) -> t.Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: +) -> Tuple[NDArray, NDArray, NDArray, NDArray]: """Merge depthmaps into a single point cloud.""" shot_ids = [s for s in reconstruction.shots if data.pruned_depthmap_exists(s)] - def depthmap_provider(shot_id): + def depthmap_provider(shot_id: str) -> Tuple[NDArray, NDArray, NDArray, NDArray]: return data.load_pruned_depthmap(shot_id) return merge_depthmaps_from_provider(shot_ids, depthmap_provider) def merge_depthmaps_from_provider( - shot_ids: t.Iterable[str], depthmap_provider: t.Callable -) -> t.Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + shot_ids: Iterable[str], + depthmap_provider: Callable[[str], Tuple[NDArray, NDArray, NDArray, NDArray]], +) -> Tuple[NDArray, NDArray, NDArray, NDArray]: """Merge depthmaps into a single point cloud.""" logger.info("Merging depthmaps") @@ -276,7 +291,9 @@ def merge_depthmaps_from_provider( return aggregate_depthmaps(shot_ids, depthmap_provider) -def add_views_to_depth_estimator(data: UndistortedDataSet, neighbors, de): +def add_views_to_depth_estimator( + data: UndistortedDataSet, neighbors: List[pymap.Shot], de: pydense.DepthmapEstimator +) -> None: """Add neighboring views to the DepthmapEstimator.""" num_neighbors = data.config["depthmap_num_matching_views"] for shot in neighbors[: num_neighbors + 1]: @@ -295,7 +312,9 @@ def add_views_to_depth_estimator(data: UndistortedDataSet, neighbors, de): de.add_view(K, R, t, image, mask) -def add_views_to_depth_cleaner(data: UndistortedDataSet, neighbors, dc): +def add_views_to_depth_cleaner( + data: UndistortedDataSet, neighbors: List[pymap.Shot], dc: pydense.DepthmapCleaner +) -> None: for shot in neighbors: if not data.raw_depthmap_exists(shot.id): continue @@ -307,7 +326,7 @@ def add_views_to_depth_cleaner(data: UndistortedDataSet, neighbors, dc): dc.add_view(K, R, t, depth) -def load_combined_mask(data: UndistortedDataSet, shot): +def load_combined_mask(data: UndistortedDataSet, shot: pymap.Shot) -> NDArray: """Load the undistorted mask. If no mask exists return an array of ones. @@ -320,7 +339,7 @@ def load_combined_mask(data: UndistortedDataSet, shot): return mask -def load_segmentation_labels(data: UndistortedDataSet, shot): +def load_segmentation_labels(data: UndistortedDataSet, shot: pymap.Shot) -> NDArray: """Load the undistorted segmentation labels. If no segmentation exists return an array of zeros. @@ -332,7 +351,9 @@ def load_segmentation_labels(data: UndistortedDataSet, shot): return np.zeros(size, dtype=np.uint8) -def add_views_to_depth_pruner(data: UndistortedDataSet, neighbors, dp): +def add_views_to_depth_pruner( + data: UndistortedDataSet, neighbors: List[pymap.Shot], dp: pydense.DepthmapPruner +) -> None: for shot in neighbors: if not data.clean_depthmap_exists(shot.id): continue @@ -349,7 +370,12 @@ def add_views_to_depth_pruner(data: UndistortedDataSet, neighbors, dp): dp.add_view(K, R, t, depth, plane, image, labels) -def compute_depth_range(tracks_manager, reconstruction, shot, config): +def compute_depth_range( + tracks_manager: pymap.TracksManager, + reconstruction: types.Reconstruction, + shot: pymap.Shot, + config: Dict[str, Any], +) -> Tuple[float, float]: """Compute min and max depth based on reconstruction points.""" depths = [] for track in tracks_manager.get_shot_observations(shot.id): @@ -368,13 +394,14 @@ def compute_depth_range(tracks_manager, reconstruction, shot, config): def common_tracks_double_dict( tracks_manager: pymap.TracksManager, -) -> t.Dict[str, t.Dict[str, t.List[str]]]: + processes: int, +) -> Dict[str, Dict[str, List[str]]]: """List of track ids observed by each image pair. Return a dict, ``res``, such that ``res[im1][im2]`` is the list of common tracks between ``im1`` and ``im2``. """ - common_tracks_per_pair = tracking.all_common_tracks_without_features(tracks_manager) + common_tracks_per_pair = tracking.all_common_tracks_without_features(tracks_manager, processes=processes) res = {image: {} for image in tracks_manager.get_shot_ids()} for (im1, im2), v in common_tracks_per_pair.items(): res[im1][im2] = v @@ -384,10 +411,10 @@ def common_tracks_double_dict( def find_neighboring_images( shot: pymap.Shot, - common_tracks: t.Dict[str, t.Dict[str, t.List[str]]], + common_tracks: Dict[str, Dict[str, List[str]]], reconstruction: types.Reconstruction, num_neighbors: int, -): +) -> List[pymap.Shot]: """Find neighboring images based on common tracks.""" theta_min = np.pi / 60 theta_max = np.pi / 6 @@ -412,7 +439,11 @@ def find_neighboring_images( return [shot] + [n for n, s in ns[:num_neighbors]] -def angle_between_points(origin, p1, p2): +def angle_between_points( + origin: Union[NDArray, List[float]], + p1: Union[NDArray, List[float]], + p2: Union[NDArray, List[float]], +) -> float: a0 = p1[0] - origin[0] a1 = p1[1] - origin[1] a2 = p1[2] - origin[2] @@ -425,28 +456,26 @@ def angle_between_points(origin, p1, p2): return np.arccos(dot / np.sqrt(la * lb)) -def distance_between_shots(shot, other): +def distance_between_shots(shot: pymap.Shot, other: pymap.Shot) -> float: o1 = shot.pose.get_origin() o2 = other.pose.get_origin() d = o2 - o1 - return np.sqrt(np.sum(d ** 2)) + return np.sqrt(np.sum(d**2)) -def scale_image( - image: np.ndarray, width: int, height: int, interpolation: int -) -> np.ndarray: +def scale_image(image: NDArray, width: int, height: int, interpolation: int) -> NDArray: return cv2.resize(image, (width, height), interpolation=interpolation) def scale_down_image( - image: np.ndarray, width: int, height: int, interpolation=cv2.INTER_AREA -) -> np.ndarray: + image: NDArray, width: int, height: int, interpolation: int = cv2.INTER_AREA +) -> NDArray: width = min(width, image.shape[1]) height = min(height, image.shape[0]) return scale_image(image, width, height, interpolation) -def depthmap_to_ply(shot, depth, image): +def depthmap_to_ply(shot: pymap.Shot, depth: NDArray, image: NDArray) -> str: """Export depthmap points as a PLY string""" height, width = depth.shape K = shot.camera.get_K_in_pixel_coordinates(width, height) @@ -466,7 +495,7 @@ def depthmap_to_ply(shot, depth, image): return io.points_to_ply_string(vertices) -def color_plane_normals(plane): +def color_plane_normals(plane: NDArray) -> NDArray: norm = np.linalg.norm(plane, axis=2) normal = plane / norm[..., np.newaxis] normal[..., 1] *= -1 # Reverse Y because it points down diff --git a/opensfm/exif.py b/opensfm/exif.py index de1adc42b..cc888d716 100644 --- a/opensfm/exif.py +++ b/opensfm/exif.py @@ -1,35 +1,42 @@ +# pyre-strict + import datetime import logging from codecs import encode, decode from bs4 import BeautifulSoup -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple, BinaryIO, Callable, Union import exifread +import math import numpy as np import xmltodict as x2d -from opensfm import pygeometry +from opensfm import geometry, pygeometry + from opensfm.dataset_base import DataSetBase from opensfm.geo import ecef_from_lla -from opensfm.pygeometry import Camera -from opensfm.sensors import sensor_data, camera_calibration +from opensfm.sensors import camera_calibration, sensor_data +from opensfm.geo import TopocentricConverter logger: logging.Logger = logging.getLogger(__name__) +inch_in_mm: float = 25.4 +cm_in_mm: float = 10 +um_in_mm: float = 0.001 +default_projection: str = "perspective" +maximum_altitude: float = 1e4 -inch_in_mm = 25.4 -cm_in_mm = 10 -um_in_mm = 0.001 -default_projection = "perspective" -maximum_altitude = 1e4 - -def eval_frac(value) -> Optional[float]: +def eval_frac( + value: exifread.utils.Ratio, +) -> Optional[float]: try: return float(value.num) / float(value.den) except ZeroDivisionError: return None -def gps_to_decimal(values, reference) -> Optional[float]: +def gps_to_decimal( + values: List[exifread.utils.Ratio], reference: str +) -> Optional[float]: sign = 1 if reference in "NE" else -1 degrees = eval_frac(values[0]) minutes = eval_frac(values[1]) @@ -39,7 +46,7 @@ def gps_to_decimal(values, reference) -> Optional[float]: return None -def get_tag_as_float(tags, key, index: int = 0) -> Optional[float]: +def get_tag_as_float(tags: Dict[str, Any], key: str, index: int = 0) -> Optional[float]: if key in tags: val = tags[key].values[index] if isinstance(val, exifread.utils.Ratio): @@ -56,17 +63,53 @@ def get_tag_as_float(tags, key, index: int = 0) -> Optional[float]: return None +def focal35_to_focal_ratio( + focal35_or_ratio: float, width: int, height: int, inverse=False +) -> float: + """ + Convert focal length in 35mm film equivalent to focal ratio (and vice versa). + We follow https://en.wikipedia.org/wiki/35_mm_equivalent_focal_length + """ + image_ratio = float(max(width, height)) / min(width, height) + is_32 = math.fabs(image_ratio - 3.0 / 2.0) + is_43 = math.fabs(image_ratio - 4.0 / 3.0) + if is_32 < is_43: + # 3:2 aspect ratio : use 36mm for 35mm film + film_width = 36.0 + if inverse: + return focal35_or_ratio * film_width + else: + return focal35_or_ratio / film_width + else: + # 4:3 aspect ratio : use 34mm for 35mm film + film_width = 34 + if inverse: + return focal35_or_ratio * film_width + else: + return focal35_or_ratio / film_width + + def compute_focal( - focal_35: Optional[float], focal: Optional[float], sensor_width, sensor_string + pixel_width: int, + pixel_height: int, + focal_35: Optional[float], + focal: Optional[float], + sensor_width: Optional[float], + sensor_string: Optional[str], ) -> Tuple[float, float]: if focal_35 is not None and focal_35 > 0: - focal_ratio = focal_35 / 36.0 # 35mm film produces 36x24mm pictures. + focal_ratio = focal35_to_focal_ratio( + focal_35, pixel_width, pixel_height) else: if not sensor_width: - sensor_width = sensor_data().get(sensor_string, None) + sensor_width = ( + sensor_data().get(sensor_string, None) if sensor_string else None + ) if sensor_width and focal: focal_ratio = focal / sensor_width - focal_35 = 36.0 * focal_ratio + focal_35 = focal35_to_focal_ratio( + focal, pixel_width, pixel_height, inverse=True + ) else: focal_35 = 0.0 focal_ratio = 0.0 @@ -80,7 +123,7 @@ def sensor_string(make: str, model: str) -> str: return (make.strip() + " " + model.strip()).strip().lower() -def camera_id(exif) -> str: +def camera_id(exif: Dict[str, Any]) -> str: return camera_id_( exif["make"], exif["model"], @@ -91,7 +134,9 @@ def camera_id(exif) -> str: ) -def camera_id_(make, model, width, height, projection_type, focal) -> str: +def camera_id_( + make: str, model: str, width: int, height: int, projection_type: str, focal: float +) -> str: if make != "unknown": # remove duplicate 'make' information in 'model' model = model.replace(make, "") @@ -109,14 +154,17 @@ def camera_id_(make, model, width, height, projection_type, focal) -> str: def extract_exif_from_file( - fileobj, image_size_loader, use_exif_size, name=None + fileobj: BinaryIO, + image_size_loader: Callable[[], Tuple[int, int]], + use_exif_size: bool, + name: Optional[str] = None, ) -> Dict[str, Any]: exif_data = EXIF(fileobj, image_size_loader, use_exif_size, name=name) d = exif_data.extract_exif() return d -def unescape_string(s) -> str: +def unescape_string(s: str) -> str: return decode(encode(s, "latin-1", "backslashreplace"), "unicode-escape") @@ -136,14 +184,14 @@ def parse_xmp_string(xmp_str: str): return None -def get_xmp(fileobj) -> List[str]: +def get_xmp(fileobj: BinaryIO) -> List[Dict[str, Any]]: """Extracts XMP metadata from and image fileobj""" img_str = str(fileobj.read()) xmp_start = img_str.find(" List[str]: return [] -def get_gpano_from_xmp(xmp) -> Dict[str, Any]: +def get_gpano_from_xmp(xmp: List[Dict[str, Any]]) -> Dict[str, Any]: for i in xmp: for k in i: if "GPano" in k: @@ -175,15 +223,21 @@ def get_pix4d_from_xmp(xmp): class EXIF: def __init__( - self, fileobj, image_size_loader, use_exif_size=True, name=None + self, + fileobj: BinaryIO, + image_size_loader: Callable[[], Tuple[int, int]], + use_exif_size: bool = True, + name: Optional[str] = None, ) -> None: - self.image_size_loader = image_size_loader - self.use_exif_size = use_exif_size - self.fileobj = fileobj - self.tags = exifread.process_file(fileobj, details=False) + self.image_size_loader: Callable[[], + Tuple[int, int]] = image_size_loader + self.use_exif_size: bool = use_exif_size + self.fileobj: BinaryIO = fileobj + self.tags: Dict[str, Any] = exifread.process_file( + fileobj, details=False) fileobj.seek(0) - self.xmp = get_xmp(fileobj) - self.fileobj_name = self.fileobj.name if name is None else name + self.xmp: List[Dict[str, Any]] = get_xmp(fileobj) + self.fileobj_name: str = self.fileobj.name if name is None else name def extract_image_size(self) -> Tuple[int, int]: if ( @@ -208,14 +262,15 @@ def extract_image_size(self) -> Tuple[int, int]: height, width = self.image_size_loader() return width, height - def _decode_make_model(self, value) -> str: + def _decode_make_model(self, value: Union[str, bytes]) -> str: """Python 2/3 compatible decoding of make/model field.""" - if hasattr(value, "decode"): + if type(value) is bytes: try: return value.decode("utf-8") except UnicodeDecodeError: return "unknown" else: + assert type(value) is str return value def extract_make(self) -> str: @@ -254,7 +309,10 @@ def extract_projection_type(self) -> str: def extract_focal(self) -> Tuple[float, float]: make, model = self.extract_make(), self.extract_model() + width, height = self.extract_image_size() focal_35, focal_ratio = compute_focal( + width, + height, get_tag_as_float(self.tags, "EXIF FocalLengthIn35mmFilm"), get_tag_as_float(self.tags, "EXIF FocalLength"), self.extract_sensor_width(), @@ -273,18 +331,20 @@ def extract_sensor_width(self) -> Optional[float]: mm_per_unit = self.get_mm_per_unit(resolution_unit) if not mm_per_unit: return None - pixels_per_unit = get_tag_as_float(self.tags, "EXIF FocalPlaneXResolution") + pixels_per_unit = get_tag_as_float( + self.tags, "EXIF FocalPlaneXResolution") if pixels_per_unit is None: return None if pixels_per_unit <= 0.0: - pixels_per_unit = get_tag_as_float(self.tags, "EXIF FocalPlaneYResolution") + pixels_per_unit = get_tag_as_float( + self.tags, "EXIF FocalPlaneYResolution") if pixels_per_unit is None or pixels_per_unit <= 0.0: return None units_per_pixel = 1 / pixels_per_unit width_in_pixels = self.extract_image_size()[0] return width_in_pixels * units_per_pixel * mm_per_unit - def get_mm_per_unit(self, resolution_unit) -> Optional[float]: + def get_mm_per_unit(self, resolution_unit: int) -> Optional[float]: """Length of a resolution unit in millimeters. Uses the values from the EXIF specs in @@ -293,6 +353,7 @@ def get_mm_per_unit(self, resolution_unit) -> Optional[float]: Args: resolution_unit: the resolution unit value given in the EXIF """ + global inch_in_mm if resolution_unit == 2: # inch return inch_in_mm elif resolution_unit == 3: # cm @@ -303,7 +364,8 @@ def get_mm_per_unit(self, resolution_unit) -> Optional[float]: return um_in_mm else: logger.warning( - "Unknown EXIF resolution unit value: {}".format(resolution_unit) + "Unknown EXIF resolution unit value: {}".format( + resolution_unit) ) return None @@ -311,7 +373,7 @@ def extract_orientation(self) -> int: orientation = 1 if "Image Orientation" in self.tags: value = self.tags.get("Image Orientation").values[0] - if type(value) == int and value != 0: + if type(value) is int and value != 0: orientation = value return orientation @@ -474,13 +536,22 @@ def extract_capture_time(self) -> float: return (d - datetime.datetime(1970, 1, 1)).total_seconds() logger.info( - 'Image file "{0:s}" has no valid time stamp'.format(self.fileobj_name) + 'Image file "{0:s}" has no valid time stamp'.format( + self.fileobj_name) ) return 0.0 - def extract_opk(self, geo) -> Optional[Dict[str, Any]]: + def extract_opk(self, geo: Dict[str, Any]) -> Optional[Dict[str, Any]]: opk = None + # Can't convert from YPR to OPK without geo location + if "latitude" not in geo and "longitude" not in geo and "altitude" not in geo: + return opk + + tc = TopocentricConverter( + geo["latitude"], geo["longitude"], geo["altitude"] + ) + if self.has_xmp() and geo and "latitude" in geo and "longitude" in geo: ypr = np.array([None, None, None]) @@ -493,31 +564,41 @@ def extract_opk(self, geo) -> Optional[Dict[str, Any]]: # Pitch: 90 --> camera is looking forward # Roll: 0 (assuming gimbal) - if ( - "@Camera:Yaw" in self.xmp[0] - and "@Camera:Pitch" in self.xmp[0] - and "@Camera:Roll" in self.xmp[0] - ): - ypr = np.array( - [ - float(self.xmp[0]["@Camera:Yaw"]), - float(self.xmp[0]["@Camera:Pitch"]), - float(self.xmp[0]["@Camera:Roll"]), - ] - ) - elif ( - "@drone-dji:GimbalYawDegree" in self.xmp[0] - and "@drone-dji:GimbalPitchDegree" in self.xmp[0] - and "@drone-dji:GimbalRollDegree" in self.xmp[0] - ): - ypr = np.array( - [ - float(self.xmp[0]["@drone-dji:GimbalYawDegree"]), - float(self.xmp[0]["@drone-dji:GimbalPitchDegree"]), - float(self.xmp[0]["@drone-dji:GimbalRollDegree"]), - ] - ) - ypr[1] += 90 # DJI's values need to be offset + # Prioritize Gimbal, then Flight, then Camera + tag_sets = [ + ( + "@drone-dji:GimbalYawDegree", + "@drone-dji:GimbalPitchDegree", + "@drone-dji:GimbalRollDegree", + True, + ), + ( + "@drone-dji:FlightYawDegree", + "@drone-dji:FlightPitchDegree", + "@drone-dji:FlightRollDegree", + True, + ), + ("@Camera:Yaw", "@Camera:Pitch", "@Camera:Roll", False), + ] + + for yaw_key, pitch_key, roll_key, offset_pitch in tag_sets: + if ( + yaw_key in self.xmp[0] + and pitch_key in self.xmp[0] + and roll_key in self.xmp[0] + ): + ypr = np.array( + [ + float(self.xmp[0][yaw_key]), + float(self.xmp[0][pitch_key]), + float(self.xmp[0][roll_key]), + ] + ) + if offset_pitch: + ypr[1] += 90 + + break + except ValueError: logger.debug( 'Invalid yaw/pitch/roll tag in image file "{0:s}"'.format( @@ -525,7 +606,7 @@ def extract_opk(self, geo) -> Optional[Dict[str, Any]]: ) ) - if np.all(ypr) is not None: + if not any(v is None for v in ypr): ypr = np.radians(ypr) # Convert YPR --> OPK @@ -540,18 +621,28 @@ def extract_opk(self, geo) -> Optional[Dict[str, Any]]: [ [ np.cos(y) * np.cos(p), - np.cos(y) * np.sin(p) * np.sin(r) - np.sin(y) * np.cos(r), - np.cos(y) * np.sin(p) * np.cos(r) + np.sin(y) * np.sin(r), + np.cos(y) * np.sin(p) * np.sin(r) - + np.sin(y) * np.cos(r), + np.cos(y) * np.sin(p) * np.cos(r) + + np.sin(y) * np.sin(r), ], [ np.sin(y) * np.cos(p), - np.sin(y) * np.sin(p) * np.sin(r) + np.cos(y) * np.cos(r), - np.sin(y) * np.sin(p) * np.cos(r) - np.cos(y) * np.sin(r), + np.sin(y) * np.sin(p) * np.sin(r) + + np.cos(y) * np.cos(r), + np.sin(y) * np.sin(p) * np.cos(r) - + np.cos(y) * np.sin(r), ], - [-np.sin(p), np.cos(p) * np.sin(r), np.cos(p) * np.cos(r)], + [-np.sin(p), np.cos(p) * np.sin(r), + np.cos(p) * np.cos(r)], ] ) + # Flip X and Z for 180 degree roll + if math.isclose(abs(ypr[2]), math.pi, abs_tol=1e-3): + cnb[:, 0] *= -1 + cnb[:, 2] *= -1 + # Convert between image and body coordinates # Top of image pixels point to flying direction # and camera is looking down. @@ -561,17 +652,16 @@ def extract_opk(self, geo) -> Optional[Dict[str, Any]]: # (Swap X/Y, flip Z) cbb = np.array([[0, 1, 0], [1, 0, 0], [0, 0, -1]]) - delta = 1e-7 - + delta = 1e-10 p1 = np.array( - ecef_from_lla( + tc.to_topocentric( geo["latitude"] + delta, geo["longitude"], geo.get("altitude", 0), ) ) p2 = np.array( - ecef_from_lla( + tc.to_topocentric( geo["latitude"] - delta, geo["longitude"], geo.get("altitude", 0), @@ -589,7 +679,6 @@ def extract_opk(self, geo) -> Optional[Dict[str, Any]]: znp = np.array([0, 0, -1]).T ynp = np.cross(znp, xnp) - cen = np.array([xnp, ynp, znp]).T # OPK rotation matrix @@ -633,7 +722,13 @@ def hard_coded_calibration(exif) -> Optional[Dict[str, Any]]: return None # Disable hard coded calibrations focal = exif["focal_ratio"] - fmm35 = int(round(focal * 36.0)) + fmm35 = int( + round( + focal35_to_focal_ratio( + focal, int(exif["width"]), int(exif["height"]), inverse=True + ) + ) + ) make = exif["make"].strip().lower() model = exif["model"].strip().lower() raw_calibrations = camera_calibration()[0] @@ -653,7 +748,7 @@ def hard_coded_calibration(exif) -> Optional[Dict[str, Any]]: return None -def focal_ratio_calibration(exif) -> Optional[Dict[str, Any]]: +def focal_ratio_calibration(exif: Dict[str, Any]) -> Optional[Dict[str, Any]]: if exif.get("focal_ratio"): return { "focal": exif["focal_ratio"], @@ -665,7 +760,7 @@ def focal_ratio_calibration(exif) -> Optional[Dict[str, Any]]: } -def focal_xy_calibration(exif) -> Optional[Dict[str, Any]]: +def focal_xy_calibration(exif: Dict[str, Any]) -> Optional[Dict[str, Any]]: focal = exif.get("focal_x", exif.get("focal_ratio")) if focal: return { @@ -710,7 +805,9 @@ def default_calibration(data: DataSetBase) -> Dict[str, Any]: } -def calibration_from_metadata(metadata, data: DataSetBase) -> Dict[str, Any]: +def calibration_from_metadata( + metadata: Dict[str, Any], data: DataSetBase +) -> Dict[str, Any]: """Finds the best calibration in one of the calibration sources.""" pt = metadata.get("projection_type", default_projection).lower() if ( @@ -738,8 +835,12 @@ def calibration_from_metadata(metadata, data: DataSetBase) -> Dict[str, Any]: def camera_from_exif_metadata( - metadata, data: DataSetBase, calibration_func=calibration_from_metadata -) -> Camera: + metadata: Dict[str, Any], + data: DataSetBase, + calibration_func: Callable[ + [Dict[str, Any], DataSetBase], Dict[str, Any] + ] = calibration_from_metadata, +) -> pygeometry.Camera: """ Create a camera object from exif metadata and the calibration function that turns metadata into usable calibration parameters. diff --git a/opensfm/feature_loader.py b/opensfm/feature_loader.py index eac0cb4cc..79d2d0949 100644 --- a/opensfm/feature_loader.py +++ b/opensfm/feature_loader.py @@ -1,3 +1,4 @@ +# pyre-strict from opensfm import feature_loading diff --git a/opensfm/feature_loading.py b/opensfm/feature_loading.py index c085f9f2b..2b9948b0e 100644 --- a/opensfm/feature_loading.py +++ b/opensfm/feature_loading.py @@ -1,9 +1,13 @@ +# pyre-strict import logging from functools import lru_cache -from typing import Optional, Tuple, Any +from typing import Optional, Tuple + +import cv2 import numpy as np -from opensfm import pygeometry, features as ft, masking +from numpy.typing import NDArray +from opensfm import features as ft, masking, pygeometry from opensfm.dataset_base import DataSetBase @@ -15,7 +19,7 @@ ) -class FeatureLoader(object): +class FeatureLoader: def clear_cache(self) -> None: self.load_mask.cache_clear() self.load_points_colors_segmentations_instances.cache_clear() @@ -25,7 +29,7 @@ def clear_cache(self) -> None: self.load_words.cache_clear() @lru_cache(1000) - def load_mask(self, data: DataSetBase, image: str) -> Optional[np.ndarray]: + def load_mask(self, data: DataSetBase, image: str) -> Optional[NDArray]: all_features_data = self._load_all_data_unmasked(data, image) if not all_features_data: return None @@ -87,7 +91,7 @@ def load_bearings( image: str, masked: bool, camera: pygeometry.Camera, - ) -> Optional[np.ndarray]: + ) -> Optional[NDArray]: if masked: features_data = self._load_all_data_masked(data, image) else: @@ -132,6 +136,7 @@ def _add_segmentation_in_descriptor( return features desc_augmented = np.concatenate( + # pyre-fixme[6]: For 1st argument expected `Union[_SupportsArray[dtype[ty... ( features.descriptors, (np.array([segmentation]).T).astype(np.float32), @@ -172,7 +177,7 @@ def load_features_index( image: str, masked: bool, segmentation_in_descriptor: bool, - ) -> Optional[Tuple[ft.FeaturesData, Any]]: + ) -> Optional[Tuple[ft.FeaturesData, cv2.flann_Index]]: features_data = self.load_all_data( data, image, masked, segmentation_in_descriptor ) @@ -187,7 +192,7 @@ def load_features_index( ) @lru_cache(200) - def load_words(self, data: DataSetBase, image: str, masked: bool) -> np.ndarray: + def load_words(self, data: DataSetBase, image: str, masked: bool) -> NDArray: words = data.load_words(image) if masked: mask = self.load_mask(data, image) diff --git a/opensfm/features.py b/opensfm/features.py index eb8ce6b0d..fec0e0175 100644 --- a/opensfm/features.py +++ b/opensfm/features.py @@ -1,11 +1,13 @@ +# pyre-strict """Tools to extract features.""" import logging import time -from typing import Tuple, Dict, Any, List, Optional +from typing import Any, BinaryIO, Dict, List, Optional, Tuple, Union import cv2 import numpy as np +from numpy.typing import NDArray from opensfm import context, pyfeatures @@ -13,16 +15,16 @@ class SemanticData: - segmentation: np.ndarray - instances: Optional[np.ndarray] + segmentation: NDArray + instances: Optional[NDArray] labels: List[Dict[str, Any]] def __init__( self, - segmentation: np.ndarray, - instances: Optional[np.ndarray], + segmentation: NDArray, + instances: Optional[NDArray], labels: List[Dict[str, Any]], - ): + ) -> None: self.segmentation = segmentation self.instances = instances self.labels = labels @@ -30,7 +32,7 @@ def __init__( def has_instances(self) -> bool: return self.instances is not None - def mask(self, mask: np.ndarray) -> "SemanticData": + def mask(self, mask: NDArray) -> "SemanticData": try: segmentation = self.segmentation[mask] instances = self.instances @@ -46,27 +48,30 @@ def mask(self, mask: np.ndarray) -> "SemanticData": class FeaturesData: - points: np.ndarray - descriptors: Optional[np.ndarray] - colors: np.ndarray + points: NDArray + descriptors: Optional[NDArray] + colors: NDArray semantic: Optional[SemanticData] + depths: Optional[NDArray] # New field. This field is not serialized yet FEATURES_VERSION: int = 3 FEATURES_HEADER: str = "OPENSFM_FEATURES_VERSION" def __init__( self, - points: np.ndarray, - descriptors: Optional[np.ndarray], - colors: np.ndarray, + points: NDArray, + descriptors: Optional[NDArray], + colors: NDArray, semantic: Optional[SemanticData], - ): + depths: Optional[NDArray] = None, + ) -> None: self.points = points self.descriptors = descriptors self.colors = colors self.semantic = semantic + self.depths = depths - def get_segmentation(self) -> Optional[np.ndarray]: + def get_segmentation(self) -> Optional[NDArray]: semantic = self.semantic if not semantic: return None @@ -80,7 +85,7 @@ def has_instances(self) -> bool: return False return semantic.instances is not None - def mask(self, mask: np.ndarray) -> "FeaturesData": + def mask(self, mask: NDArray) -> "FeaturesData": if self.semantic: masked_semantic = self.semantic.mask(mask) else: @@ -90,11 +95,12 @@ def mask(self, mask: np.ndarray) -> "FeaturesData": self.descriptors[mask] if self.descriptors is not None else None, self.colors[mask], masked_semantic, + self.depths[mask] if self.depths is not None else None, ) - def save(self, fileobject: Any, config: Dict[str, Any]): + def save(self, fileobject: Union[str, BinaryIO], config: Dict[str, Any]) -> None: """Save features from file (path like or file object like)""" - feature_type = config["feature_type"] + feature_type = config["feature_type"].upper() if ( ( feature_type == "AKAZE" @@ -119,7 +125,7 @@ def save(self, fileobject: Any, config: Dict[str, Any]): colors=self.colors, segmentations=semantic.segmentation.astype(np.uint8), instances=instances.astype(np.int16) if instances is not None else [], - segmentation_labels=np.array(semantic.labels).astype(np.str), + segmentation_labels=np.array(semantic.labels).astype(str), OPENSFM_FEATURES_VERSION=self.FEATURES_VERSION, ) else: @@ -135,7 +141,9 @@ def save(self, fileobject: Any, config: Dict[str, Any]): ) @classmethod - def from_file(cls, fileobject: Any, config: Dict[str, Any]) -> "FeaturesData": + def from_file( + cls, fileobject: Union[str, BinaryIO], config: Dict[str, Any] + ) -> "FeaturesData": """Load features from file (path like or file object like)""" s = np.load(fileobject, allow_pickle=False) version = cls._features_file_version(s) @@ -151,7 +159,7 @@ def _features_file_version(cls, obj: Dict[str, Any]) -> int: @classmethod def _from_file_v0( - cls, data: Dict[str, np.ndarray], config: Dict[str, Any] + cls, data: Dict[str, NDArray], config: Dict[str, Any] ) -> "FeaturesData": """Base version of features file @@ -168,7 +176,7 @@ def _from_file_v0( @classmethod def _from_file_v1( - cls, data: Dict[str, np.ndarray], config: Dict[str, Any] + cls, data: Dict[str, NDArray], config: Dict[str, Any] ) -> "FeaturesData": """Version 1 of features file @@ -268,7 +276,7 @@ def _from_file_v3( ) -def resized_image(image: np.ndarray, max_size: int) -> np.ndarray: +def resized_image(image: NDArray, max_size: int) -> NDArray: """Resize image to feature_process_size.""" h, w = image.shape[:2] size = max(w, h) @@ -279,7 +287,7 @@ def resized_image(image: np.ndarray, max_size: int) -> np.ndarray: return image -def root_feature(desc: np.ndarray, l2_normalization: bool = False) -> np.ndarray: +def root_feature(desc: NDArray, l2_normalization: bool = False) -> NDArray: if l2_normalization: s2 = np.linalg.norm(desc, axis=1) desc = (desc.T / s2).T @@ -289,8 +297,8 @@ def root_feature(desc: np.ndarray, l2_normalization: bool = False) -> np.ndarray def root_feature_surf( - desc: np.ndarray, l2_normalization: bool = False, partial: bool = False -) -> np.ndarray: + desc: NDArray, l2_normalization: bool = False, partial: bool = False +) -> NDArray: """ Experimental square root mapping of surf-like feature, only work for 64-dim surf now """ @@ -312,8 +320,8 @@ def root_feature_surf( def normalized_image_coordinates( - pixel_coords: np.ndarray, width: int, height: int -) -> np.ndarray: + pixel_coords: NDArray, width: int, height: int +) -> NDArray: size = max(width, height) p = np.empty((len(pixel_coords), 2)) p[:, 0] = (pixel_coords[:, 0] + 0.5 - width / 2.0) / size @@ -322,8 +330,8 @@ def normalized_image_coordinates( def denormalized_image_coordinates( - norm_coords: np.ndarray, width: int, height: int -) -> np.ndarray: + norm_coords: NDArray, width: int, height: int +) -> NDArray: size = max(width, height) p = np.empty((len(norm_coords), 2)) p[:, 0] = norm_coords[:, 0] * size - 0.5 + width / 2.0 @@ -332,15 +340,19 @@ def denormalized_image_coordinates( def normalize_features( - points: np.ndarray, desc: np.ndarray, colors: np.ndarray, width: int, height: int -) -> Tuple[np.ndarray, np.ndarray, np.ndarray,]: + points: NDArray, desc: NDArray, colors: NDArray, width: int, height: int +) -> Tuple[ + NDArray, + NDArray, + NDArray, +]: """Normalize feature coordinates and size.""" points[:, :2] = normalized_image_coordinates(points[:, :2], width, height) points[:, 2:3] /= max(width, height) return points, desc, colors -def _in_mask(point: np.ndarray, width: int, height: int, mask: np.ndarray) -> bool: +def _in_mask(point: NDArray, width: int, height: int, mask: NDArray) -> bool: """Check if a point is inside a binary mask.""" u = mask.shape[1] * (point[0] + 0.5) / width v = mask.shape[0] * (point[1] + 0.5) / height @@ -348,8 +360,8 @@ def _in_mask(point: np.ndarray, width: int, height: int, mask: np.ndarray) -> bo def extract_features_sift( - image: np.ndarray, config: Dict[str, Any], features_count: int -) -> Tuple[np.ndarray, np.ndarray]: + image: NDArray, config: Dict[str, Any], features_count: int +) -> Tuple[NDArray, NDArray]: sift_edge_threshold = config["sift_edge_threshold"] sift_peak_threshold = float(config["sift_peak_threshold"]) nfeatures = int(features_count * 3 / 2) @@ -386,12 +398,17 @@ def extract_features_sift( detector = cv2.SIFT_create( nfeatures=nfeatures, edgeThreshold=sift_edge_threshold, contrastThreshold=sift_peak_threshold ) + descriptor = detector elif context.OPENCV3: detector = cv2.xfeatures2d.SIFT_create( nfeatures=nfeatures, edgeThreshold=sift_edge_threshold, contrastThreshold=sift_peak_threshold ) + descriptor = detector else: - detector.setDouble("contrastThreshold", sift_peak_threshold) + detector = cv2.FeatureDetector_create("SIFT") + descriptor = cv2.DescriptorExtractor_create("SIFT") + detector.setDouble("edgeThreshold", sift_edge_threshold) + points = detector.detect(image) logger.debug("Found {0} points in {1}s".format(len(points), time.time() - t)) if len(points) < features_count and sift_peak_threshold > 0.0001: @@ -400,6 +417,7 @@ def extract_features_sift( else: logger.debug("done") break + points, desc = descriptor.compute(image, points) if desc is not None: @@ -427,8 +445,8 @@ def extract_features_popsift( return points, desc def extract_features_surf( - image: np.ndarray, config: Dict[str, Any], features_count: int -) -> Tuple[np.ndarray, np.ndarray]: + image: NDArray, config: Dict[str, Any], features_count: int +) -> Tuple[NDArray, NDArray]: surf_hessian_threshold = config["surf_hessian_threshold"] if context.OPENCV3: try: @@ -492,8 +510,8 @@ def akaze_descriptor_type(name: str) -> pyfeatures.AkazeDescriptorType: def extract_features_akaze( - image: np.ndarray, config: Dict[str, Any], features_count: int -) -> Tuple[np.ndarray, np.ndarray]: + image: NDArray, config: Dict[str, Any], features_count: int +) -> Tuple[NDArray, NDArray]: options = pyfeatures.AKAZEOptions() options.omax = config["akaze_omax"] akaze_descriptor_name = config["akaze_descriptor"] @@ -521,8 +539,8 @@ def extract_features_akaze( def extract_features_hahog( - image: np.ndarray, config: Dict[str, Any], features_count: int -) -> Tuple[np.ndarray, np.ndarray]: + image: NDArray, config: Dict[str, Any], features_count: int +) -> Tuple[NDArray, NDArray]: t = time.time() points, desc = pyfeatures.hahog( @@ -539,6 +557,7 @@ def extract_features_hahog( uchar_scaling = 512 if config["hahog_normalize_to_uchar"]: + # pyre-fixme[16]: `int` has no attribute `clip`. desc = (uchar_scaling * desc).clip(0, 255).round() logger.debug("Found {0} points in {1}s".format(len(points), time.time() - t)) @@ -564,8 +583,8 @@ def extract_features_dspsift( def extract_features_orb( - image: np.ndarray, config: Dict[str, Any], features_count: int -) -> Tuple[np.ndarray, np.ndarray]: + image: NDArray, config: Dict[str, Any], features_count: int +) -> Tuple[NDArray, NDArray]: if context.OPENCV3: detector = cv2.ORB_create(nfeatures=features_count) descriptor = detector @@ -590,8 +609,8 @@ def extract_features_orb( def extract_features( - image: np.ndarray, config: Dict[str, Any], is_panorama: bool -) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + image: NDArray, config: Dict[str, Any], is_panorama: bool +) -> Tuple[NDArray, NDArray, NDArray]: """Detect features in a color or gray-scale image. The type of feature detected is determined by the ``feature_type`` @@ -623,10 +642,14 @@ def extract_features( else config["feature_min_frames"] ) - assert len(image.shape) == 3 or len(image.shape) == 2 + assert image.ndim == 2 or image.ndim == 3 and image.shape[2] in [1, 3] + assert image.shape[0] > 2 and image.shape[1] > 2 + assert np.issubdtype(image.dtype, np.uint8) + image = resized_image(image, extraction_size) - if len(image.shape) == 2: # convert (h, w) to (h, w, 1) + if image.ndim == 2: # convert (h, w) to (h, w, 1) image = np.expand_dims(image, axis=2) + # convert color to gray-scale if necessary if image.shape[2] == 3: image_gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) @@ -667,7 +690,7 @@ def extract_features( return normalize_features(points, desc, colors, image.shape[1], image.shape[0]) -def build_flann_index(descriptors: np.ndarray, config: Dict[str, Any]) -> Any: +def build_flann_index(descriptors: NDArray, config: Dict[str, Any]) -> cv2.flann_Index: # FLANN_INDEX_LINEAR = 0 FLANN_INDEX_KDTREE = 1 FLANN_INDEX_KMEANS = 2 diff --git a/opensfm/features_processing.py b/opensfm/features_processing.py index db2242877..19244b03d 100644 --- a/opensfm/features_processing.py +++ b/opensfm/features_processing.py @@ -1,13 +1,16 @@ +# pyre-strict import itertools import logging import math import queue +import sys import threading from timeit import default_timer as timer -from typing import Optional, List, Dict, Any, Tuple +from typing import Any, cast, Dict, List, Optional, Tuple, Union import numpy as np -from opensfm import bow, features, io, log, pygeometry, upright, masking +from numpy.typing import NDArray +from opensfm import bow, features, io, log, masking, pygeometry, upright from opensfm.context import parallel_map from opensfm.dataset_base import DataSetBase @@ -15,19 +18,46 @@ logger: logging.Logger = logging.getLogger(__name__) +if sys.version_info >= (3, 9): + ProcessQueue = queue.Queue[ + Optional[ + Tuple[ + str, + NDArray, + Optional[NDArray], + Optional[NDArray], + DataSetBase, + bool, + ] + ] + ] +else: + ProcessQueue = queue.Queue + +ProducerArgs = Tuple[ + ProcessQueue, + DataSetBase, + List[str], + "Counter", + int, + bool, +] +ConsumerArgs = ProcessQueue + + def run_features_processing(data: DataSetBase, images: List[str], force: bool) -> None: """Main entry point for running features extraction on a list of images.""" default_queue_size = 10 max_queue_size = 200 - mem_available = log.memory_available() + mem_ceiling = data.config.get("mem_ceiling") or log.memory_available() + processes = data.config["processes"] - if mem_available: - # Use 90% of available memory - ratio_use = 0.9 - mem_available *= ratio_use + if mem_ceiling: + ratio_use = data.config.get("mem_ratio") or 0.9 + mem_available = mem_ceiling * ratio_use logger.info( - f"Planning to use {mem_available} MB of RAM for both processing queue and parallel processing." + f"Planning to use {mem_available} MB of RAM (out of {mem_ceiling} MB RAM available) for both processing queue and parallel processing. Memory ratio is {ratio_use}." ) # 50% for the queue / 50% for parallel processing @@ -46,8 +76,8 @@ def run_features_processing(data: DataSetBase, images: List[str], force: bool) - f"Expecting to queue at most {expected_images} images while parallel processing of {processes} images." ) - process_queue = queue.Queue(expected_images) - arguments: List[Tuple[str, Any]] = [] + process_queue: ProcessQueue = queue.Queue(expected_images) + arguments: List[Tuple[str, Union[ProducerArgs, ConsumerArgs]]] = [] if processes == 1: for image in images: @@ -75,7 +105,7 @@ def run_features_processing(data: DataSetBase, images: List[str], force: bool) - ) ) for _ in range(processes): - arguments.append(("consumer", (process_queue))) + arguments.append(("consumer", process_queue)) parallel_map(process, arguments, processes, 1) @@ -102,7 +132,7 @@ def average_processing_size(data: DataSetBase) -> float: def is_high_res_panorama( - data: DataSetBase, image_key: str, image_array: np.ndarray + data: DataSetBase, image_key: str, image_array: NDArray ) -> bool: """Detect if image is a panorama.""" exif = data.load_exif(image_key) @@ -118,15 +148,15 @@ def is_high_res_panorama( return w == 2 * h or exif_pano -class Counter(object): +class Counter: """Lock-less counter from https://julien.danjou.info/atomic-lock-free-counters-in-python/ that relies on the CPython impl. of itertools.count() that is thread-safe. Used, as for some reason, joblib doesn't like a good old threading.Lock (everything is stuck) """ - def __init__(self) ->None: + def __init__(self) -> None: self.number_of_read = 0 - self.counter = itertools.count() + self.counter: itertools.count[int] = itertools.count() self.read_lock = threading.Lock() def increment(self) -> None: @@ -139,18 +169,18 @@ def value(self) -> int: return value -def process(args: Tuple[str, Any]) -> None: +def process(args: Tuple[str, Union[ProducerArgs, ConsumerArgs]]) -> None: process_type, real_args = args if process_type == "producer": - queue, data, images, counter, expected, force = real_args + queue, data, images, counter, expected, force = cast(ProducerArgs, real_args) read_images(queue, data, images, counter, expected, force) if process_type == "consumer": - queue = real_args + queue = cast(ConsumerArgs, real_args) run_detection(queue) def read_images( - queue: queue.Queue, + queue: ProcessQueue, data: DataSetBase, images: List[str], counter: Counter, @@ -166,7 +196,8 @@ def read_images( instances_array = data.load_instances(image) else: segmentation_array, instances_array = None, None - args = image, image_array, segmentation_array, instances_array, data, force + args = (image, image_array, segmentation_array, instances_array, data, force) + queue.put(args, block=True, timeout=full_queue_timeout) counter.increment() if counter.value() == expected: @@ -174,7 +205,7 @@ def read_images( queue.put(None) -def run_detection(queue: queue.Queue): +def run_detection(queue: ProcessQueue) -> None: while True: args = queue.get() if args is None: @@ -188,12 +219,12 @@ def run_detection(queue: queue.Queue): def bake_segmentation( - image: np.ndarray, - points: np.ndarray, - segmentation: Optional[np.ndarray], - instances: Optional[np.ndarray], + image: NDArray, + points: NDArray, + segmentation: Optional[NDArray], + instances: Optional[NDArray], exif: Dict[str, Any], -) -> Tuple[Optional[np.ndarray], Optional[np.ndarray]]: +) -> Tuple[Optional[NDArray], Optional[NDArray]]: exif_height, exif_width, exif_orientation = ( exif["height"], exif["width"], @@ -214,19 +245,21 @@ def bake_segmentation( points[:, :2], width, height, - exif["orientation"], + exif_orientation, new_width=new_width, new_height=new_height, ).astype(int) + # pyre-fixme[6]: For 2nd argument expected `None` but got + # `ndarray[typing.Any, typing.Any]`. panoptic_data[i] = p_data[ps[:, 1], ps[:, 0]] return tuple(panoptic_data) def detect( image: str, - image_array: np.ndarray, - segmentation_array: Optional[np.ndarray], - instances_array: Optional[np.ndarray], + image_array: NDArray, + segmentation_array: Optional[NDArray], + instances_array: Optional[NDArray], data: DataSetBase, force: bool = False, ) -> None: diff --git a/opensfm/geo.py b/opensfm/geo.py index a0324f9b9..c900dc9cc 100644 --- a/opensfm/geo.py +++ b/opensfm/geo.py @@ -1,12 +1,30 @@ +# pyre-strict +from typing import List, overload, Sequence, Tuple, Union + import numpy as np -from numpy import ndarray -from typing import Tuple +import pyproj +from numpy.typing import NDArray + +Scalars = Union[float, NDArray] WGS84_a = 6378137.0 WGS84_b = 6356752.314245 -def ecef_from_lla(lat, lon, alt: float) -> Tuple[float, ...]: +@overload +def ecef_from_lla(lat: float, lon: float, + alt: float) -> Tuple[float, float, float]: ... + + +@overload +def ecef_from_lla( + lat: NDArray, lon: NDArray, alt: NDArray +) -> Tuple[NDArray, NDArray, NDArray]: ... + + +def ecef_from_lla( + lat: Scalars, lon: Scalars, alt: Scalars +) -> Tuple[Scalars, Scalars, Scalars]: """ Compute ECEF XYZ from latitude, longitude and altitude. @@ -19,8 +37,8 @@ def ecef_from_lla(lat, lon, alt: float) -> Tuple[float, ...]: >>> np.allclose(lla_from_ecef(x,y,z), [lat, lon, alt]) True """ - a2 = WGS84_a ** 2 - b2 = WGS84_b ** 2 + a2 = WGS84_a**2 + b2 = WGS84_b**2 lat = np.radians(lat) lon = np.radians(lon) L = 1.0 / np.sqrt(a2 * np.cos(lat) ** 2 + b2 * np.sin(lat) ** 2) @@ -30,7 +48,20 @@ def ecef_from_lla(lat, lon, alt: float) -> Tuple[float, ...]: return x, y, z -def lla_from_ecef(x, y, z): +@overload +def lla_from_ecef(x: float, y: float, + z: float) -> Tuple[float, float, float]: ... + + +@overload +def lla_from_ecef( + x: NDArray, y: NDArray, z: NDArray +) -> Tuple[NDArray, NDArray, NDArray]: ... + + +def lla_from_ecef( + x: Scalars, y: Scalars, z: Scalars +) -> Tuple[Scalars, Scalars, Scalars]: """ Compute latitude, longitude and altitude from ECEF XYZ. @@ -39,20 +70,21 @@ def lla_from_ecef(x, y, z): """ a = WGS84_a b = WGS84_b - ea = np.sqrt((a ** 2 - b ** 2) / a ** 2) - eb = np.sqrt((a ** 2 - b ** 2) / b ** 2) - p = np.sqrt(x ** 2 + y ** 2) + ea = np.sqrt((a**2 - b**2) / a**2) + eb = np.sqrt((a**2 - b**2) / b**2) + # pyre-ignore[6]: pyre ignores that x,y are all either scalars or arrays + p = np.sqrt(x**2 + y**2) theta = np.arctan2(z * a, p * b) lon = np.arctan2(y, x) lat = np.arctan2( - z + eb ** 2 * b * np.sin(theta) ** 3, p - ea ** 2 * a * np.cos(theta) ** 3 + z + eb**2 * b * np.sin(theta) ** 3, p - ea**2 * a * np.cos(theta) ** 3 ) - N = a / np.sqrt(1 - ea ** 2 * np.sin(lat) ** 2) + N = a / np.sqrt(1 - ea**2 * np.sin(lat) ** 2) alt = p / np.cos(lat) - N return np.degrees(lat), np.degrees(lon), alt -def ecef_from_topocentric_transform(lat, lon, alt: float) -> ndarray: +def ecef_from_topocentric_transform(lat: float, lon: float, alt: float) -> NDArray: """ Transformation from a topocentric frame at reference position to ECEF. @@ -79,7 +111,9 @@ def ecef_from_topocentric_transform(lat, lon, alt: float) -> ndarray: ) -def ecef_from_topocentric_transform_finite_diff(lat, lon, alt: float) -> ndarray: +def ecef_from_topocentric_transform_finite_diff( + lat: float, lon: float, alt: float +) -> NDArray: """ Transformation from a topocentric frame at reference position to ECEF. @@ -126,7 +160,36 @@ def ecef_from_topocentric_transform_finite_diff(lat, lon, alt: float) -> ndarray ) -def topocentric_from_lla(lat, lon, alt: float, reflat, reflon, refalt: float): +@overload +def topocentric_from_lla( + lat: float, + lon: float, + alt: float, + reflat: float, + reflon: float, + refalt: float, +) -> Tuple[float, float, float]: ... + + +@overload +def topocentric_from_lla( + lat: NDArray, + lon: NDArray, + alt: NDArray, + reflat: float, + reflon: float, + refalt: float, +) -> Tuple[NDArray, NDArray, NDArray]: ... + + +def topocentric_from_lla( + lat: Scalars, + lon: Scalars, + alt: Scalars, + reflat: float, + reflon: float, + refalt: float, +) -> Tuple[Scalars, Scalars, Scalars]: """ Transform from lat, lon, alt to topocentric XYZ. @@ -140,6 +203,7 @@ def topocentric_from_lla(lat, lon, alt: float, reflat, reflon, refalt: float): True """ T = np.linalg.inv(ecef_from_topocentric_transform(reflat, reflon, refalt)) + # pyre-ignore[6]: pyre gets confused with Scalar vs float vs NDarray x, y, z = ecef_from_lla(lat, lon, alt) tx = T[0, 0] * x + T[0, 1] * y + T[0, 2] * z + T[0, 3] ty = T[1, 0] * x + T[1, 1] * y + T[1, 2] * z + T[1, 3] @@ -147,7 +211,36 @@ def topocentric_from_lla(lat, lon, alt: float, reflat, reflon, refalt: float): return tx, ty, tz -def lla_from_topocentric(x, y, z, reflat, reflon, refalt: float): +@overload +def lla_from_topocentric( + x: float, + y: float, + z: float, + reflat: float, + reflon: float, + refalt: float, +) -> Tuple[float, float, float]: ... + + +@overload +def lla_from_topocentric( + x: NDArray, + y: NDArray, + z: NDArray, + reflat: float, + reflon: float, + refalt: float, +) -> Tuple[NDArray, NDArray, NDArray]: ... + + +def lla_from_topocentric( + x: Scalars, + y: Scalars, + z: Scalars, + reflat: float, + reflon: float, + refalt: float, +) -> Tuple[Scalars, Scalars, Scalars]: """ Transform from topocentric XYZ to lat, lon, alt. """ @@ -158,39 +251,172 @@ def lla_from_topocentric(x, y, z, reflat, reflon, refalt: float): return lla_from_ecef(ex, ey, ez) -def gps_distance(latlon_1, latlon_2): - """ - Distance between two (lat,lon) pairs. - - >>> p1 = (42.1, -11.1) - >>> p2 = (42.2, -11.3) - >>> 19000 < gps_distance(p1, p2) < 20000 - True - """ - x1, y1, z1 = ecef_from_lla(latlon_1[0], latlon_1[1], 0.0) - x2, y2, z2 = ecef_from_lla(latlon_2[0], latlon_2[1], 0.0) +@overload +def gps_distance(latlon_1: Sequence[float], + latlon_2: Sequence[float]) -> float: ... - dis = np.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2 + (z1 - z2) ** 2) - return dis +@overload +def gps_distance( + latlon_1: Sequence[NDArray], latlon_2: Sequence[NDArray] +) -> NDArray: ... +@overload +def gps_distance(latlon_1: NDArray, latlon_2: NDArray) -> NDArray: ... -class TopocentricConverter(object): +class TopocentricConverter: """Convert to and from a topocentric reference frame.""" - def __init__(self, reflat, reflon, refalt): + def __init__(self, reflat: float, reflon: float, refalt: float) -> None: """Init the converter given the reference origin.""" self.lat = reflat self.lon = reflon self.alt = refalt - def to_topocentric(self, lat, lon, alt): + @overload + def to_topocentric( + self, lat: float, lon: float, alt: float + ) -> Tuple[float, float, float]: ... + + @overload + def to_topocentric( + self, lat: NDArray, lon: NDArray, alt: NDArray + ) -> Tuple[NDArray, NDArray, NDArray]: ... + + def to_topocentric( + self, lat: Scalars, lon: Scalars, alt: Scalars + ) -> Tuple[Scalars, Scalars, Scalars]: """Convert lat, lon, alt to topocentric x, y, z.""" + # pyre-ignore[6]: pyre gets confused with Scalar vs float vs NDarray return topocentric_from_lla(lat, lon, alt, self.lat, self.lon, self.alt) - def to_lla(self, x, y, z): + @overload + def to_lla(self, x: float, y: float, + z: float) -> Tuple[float, float, float]: ... + + @overload + def to_lla( + self, x: NDArray, y: NDArray, z: NDArray + ) -> Tuple[NDArray, NDArray, NDArray]: ... + + def to_lla( + self, x: Scalars, y: Scalars, z: Scalars + ) -> Tuple[Scalars, Scalars, Scalars]: """Convert topocentric x, y, z to lat, lon, alt.""" + # pyre-ignore[6]: pyre gets confused with Scalar vs float vs NDarray return lla_from_topocentric(x, y, z, self.lat, self.lon, self.alt) - def __eq__(self, o): + def __eq__(self, o: "TopocentricConverter") -> bool: return np.allclose([self.lat, self.lon, self.alt], (o.lat, o.lon, o.alt)) + + +def construct_proj_transformer(proj_str: str, inverse: bool = False) -> pyproj.Transformer: + """ + Construct a pyproj Transformer object, converting between the given projection antod WGS84 (EPSG:4326). + If inverse is True, the transformation is from WGS84 to the given projection. + """ + crs_4326 = pyproj.CRS.from_epsg(4326) + if inverse: + return pyproj.Transformer.from_proj(crs_4326, pyproj.CRS(proj_str)) + else: + return pyproj.Transformer.from_proj(pyproj.CRS(proj_str), crs_4326) + + +def transform_to_proj( + point: Sequence[float], reference: TopocentricConverter, projection: pyproj.Transformer +) -> List[float]: + """ + Transform a point defined wrt. the local topocentric frame to a projection + defined by the given Transformer. We assume the Transformer goes from + WGS84 to the desired projection. + """ + assert projection.source_crs.to_epsg( + ) == 4326, "Transformer source CRS must be WGS84 (EPSG:4326)" + + lat, lon, altitude = reference.to_lla(point[0], point[1], point[2]) + easting, northing = projection.transform(lat, lon) + return [easting, northing, altitude] + + +def get_proj_transform_matrix( + reference: TopocentricConverter, projection: pyproj.Transformer +) -> NDArray: + """Get the linear transform from reconstruction coords to geocoords.""" + p = [[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]] + q = [transform_to_proj(point, reference, projection) for point in p] + + transformation = np.array( + [ + [q[0][0] - q[3][0], q[1][0] - q[3][0], q[2][0] - q[3][0], q[3][0]], + [q[0][1] - q[3][1], q[1][1] - q[3][1], q[2][1] - q[3][1], q[3][1]], + [q[0][2] - q[3][2], q[1][2] - q[3][2], q[2][2] - q[3][2], q[3][2]], + [0, 0, 0, 1], + ] + ) + return transformation + + +def transform_reconstruction_with_matrix( + reconstruction: "types.Reconstruction", transformation: NDArray +) -> None: + """Apply a rigid transformation to a reconstruction in-place.""" + A, b = transformation[:3, :3], transformation[:3, 3] + A1 = np.linalg.inv(A) + + for shot in reconstruction.shots.values(): + R = shot.pose.get_rotation_matrix() + shot.pose.set_rotation_matrix(np.dot(R, A1)) + shot.pose.set_origin(np.dot(A, shot.pose.get_origin()) + b) + + for point in reconstruction.points.values(): + point.coordinates = list(np.dot(A, point.coordinates) + b) + + +def transform_reconstruction_with_proj( + reconstruction: "types.Reconstruction", transformation: pyproj.Transformer +) -> None: + """Apply a proj Transformer to a reconstruction in-place.""" + eps = 1e-3 + for shot in reconstruction.shots.values(): + origin = shot.pose.get_origin() + + # Jacobian for rotation update + p0 = np.array(transform_to_proj( + origin, reconstruction.reference, transformation)) + px = np.array(transform_to_proj( + origin + [eps, 0, 0], reconstruction.reference, transformation)) + py = np.array(transform_to_proj( + origin + [0, eps, 0], reconstruction.reference, transformation)) + pz = np.array(transform_to_proj( + origin + [0, 0, eps], reconstruction.reference, transformation)) + J = np.column_stack( + ((px - p0) / eps, (py - p0) / eps, (pz - p0) / eps)) + + shot.pose.set_origin(p0) + shot.pose.set_rotation_matrix( + shot.pose.get_rotation_matrix() @ np.linalg.inv(J)) + + for point in reconstruction.points.values(): + point.coordinates = transform_to_proj( + point.coordinates, reconstruction.reference, transformation + ) + + +def gps_distance( + latlon_1: Union[Sequence[Scalars], NDArray], + latlon_2: Union[Sequence[Scalars], NDArray], +) -> Scalars: + """ + Distance between two (lat,lon) pairs. + + >>> p1 = (42.1, -11.1) + >>> p2 = (42.2, -11.3) + >>> 19000 < gps_distance(p1, p2) < 20000 + True + """ + x1, y1, z1 = ecef_from_lla(latlon_1[0], latlon_1[1], 0.0) + x2, y2, z2 = ecef_from_lla(latlon_2[0], latlon_2[1], 0.0) + + dis = np.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2 + (z1 - z2) ** 2) + + return dis diff --git a/opensfm/geometry.py b/opensfm/geometry.py index 654a17469..8daf50750 100644 --- a/opensfm/geometry.py +++ b/opensfm/geometry.py @@ -1,15 +1,17 @@ +# pyre-strict from typing import Tuple import cv2 import numpy as np +from numpy.typing import NDArray from opensfm import transformations -def rotation_from_angle_axis(angle_axis: np.ndarray) -> np.ndarray: +def rotation_from_angle_axis(angle_axis: NDArray) -> NDArray: return cv2.Rodrigues(np.asarray(angle_axis))[0] -def rotation_from_ptr(pan: float, tilt: float, roll: float) -> np.ndarray: +def rotation_from_ptr(pan: float, tilt: float, roll: float) -> NDArray: """World-to-camera rotation matrix from pan, tilt and roll.""" R1 = rotation_from_angle_axis(np.array([0.0, 0.0, roll])) R2 = rotation_from_angle_axis(np.array([tilt + np.pi / 2, 0.0, 0.0])) @@ -18,7 +20,7 @@ def rotation_from_ptr(pan: float, tilt: float, roll: float) -> np.ndarray: def ptr_from_rotation( - rotation_matrix: np.ndarray, + rotation_matrix: NDArray, ) -> Tuple[float, float, float]: """Pan tilt and roll from camera rotation matrix""" pan = pan_from_rotation(rotation_matrix) @@ -27,18 +29,18 @@ def ptr_from_rotation( return pan, tilt, roll -def pan_from_rotation(rotation_matrix: np.ndarray) -> float: +def pan_from_rotation(rotation_matrix: NDArray) -> float: Rt_ez = np.dot(rotation_matrix.T, [0, 0, 1]) return np.arctan2(Rt_ez[0], Rt_ez[1]) -def tilt_from_rotation(rotation_matrix: np.ndarray) -> float: +def tilt_from_rotation(rotation_matrix: NDArray) -> float: Rt_ez = np.dot(rotation_matrix.T, [0, 0, 1]) l = np.linalg.norm(Rt_ez[:2]) return np.arctan2(-Rt_ez[2], l) -def roll_from_rotation(rotation_matrix: np.ndarray) -> float: +def roll_from_rotation(rotation_matrix: NDArray) -> float: Rt_ex = np.dot(rotation_matrix.T, [1, 0, 0]) Rt_ez = np.dot(rotation_matrix.T, [0, 0, 1]) a = np.cross(Rt_ez, [0, 0, 1]) @@ -47,7 +49,7 @@ def roll_from_rotation(rotation_matrix: np.ndarray) -> float: return np.arcsin(np.dot(Rt_ez, b)) -def rotation_from_ptr_v2(pan: float, tilt: float, roll: float) -> np.ndarray: +def rotation_from_ptr_v2(pan: float, tilt: float, roll: float) -> NDArray: """Camera rotation matrix from pan, tilt and roll. This is the implementation used in the Single Image Calibration code. @@ -56,7 +58,7 @@ def rotation_from_ptr_v2(pan: float, tilt: float, roll: float) -> np.ndarray: return transformations.euler_matrix(pan, tilt, roll, "szxz")[:3, :3] -def ptr_from_rotation_v2(rotation_matrix: np.ndarray) -> Tuple[float, float, float]: +def ptr_from_rotation_v2(rotation_matrix: NDArray) -> Tuple[float, float, float]: """Pan tilt and roll from camera rotation matrix. This is the implementation used in the Single Image Calibration code. @@ -67,7 +69,7 @@ def ptr_from_rotation_v2(rotation_matrix: np.ndarray) -> Tuple[float, float, flo return pan, tilt - np.pi / 2, roll -def rotation_from_opk(omega: float, phi: float, kappa: float) -> np.ndarray: +def rotation_from_opk(omega: float, phi: float, kappa: float) -> NDArray: """World-to-camera rotation matrix from pan, tilt and roll.""" # Omega (ω), the rotation around the Χ axis. (East) @@ -86,7 +88,7 @@ def rotation_from_opk(omega: float, phi: float, kappa: float) -> np.ndarray: def opk_from_rotation( - rotation_matrix: np.ndarray, + rotation_matrix: NDArray, ) -> Tuple[float, float, float]: """Omega, phi, kappa from camera rotation matrix""" Rc = np.array([[1, 0, 0], [0, -1, 0], [0, 0, -1]]) @@ -97,13 +99,13 @@ def opk_from_rotation( return omega, phi, kappa -def omega_from_rotation(rotation_matrix: np.ndarray) -> float: +def omega_from_rotation(rotation_matrix: NDArray) -> float: return np.arctan2(-rotation_matrix[1, 2], rotation_matrix[2, 2]) -def phi_from_rotation(rotation_matrix: np.ndarray) -> float: +def phi_from_rotation(rotation_matrix: NDArray) -> float: return np.arcsin(rotation_matrix[0, 2]) -def kappa_from_rotation(rotation_matrix: np.ndarray) -> float: +def kappa_from_rotation(rotation_matrix: NDArray) -> float: return np.arctan2(-rotation_matrix[0, 1], rotation_matrix[0, 0]) diff --git a/opensfm/geotag_from_gpx.py b/opensfm/geotag_from_gpx.py index a206f907d..a69ab62f2 100644 --- a/opensfm/geotag_from_gpx.py +++ b/opensfm/geotag_from_gpx.py @@ -1,5 +1,7 @@ #!/usr/bin/python3 +# pyre-unsafe + import datetime import math import os @@ -57,7 +59,7 @@ def utc_to_localtime(utc_time): return utc_time - utc_offset_timedelta -def get_lat_lon_time(gpx_file, gpx_time: str = "utc"): +def get_lat_lon_time(gpx_file: str, gpx_time: str = "utc"): """ Read location and time stamps from a track in a GPX file. @@ -193,8 +195,8 @@ def segment_sphere_intersection(A, B, C, r): CA = np.array(A) - np.array(C) a = AB.dot(AB) b = 2 * AB.dot(CA) - c = CA.dot(CA) - r ** 2 - d = max(0, b ** 2 - 4 * a * c) + c = CA.dot(CA) - r**2 + d = max(0, b**2 - 4 * a * c) return (-b + np.sqrt(d)) / (2 * a) diff --git a/opensfm/io.py b/opensfm/io.py index ca9896fb7..3af8112e2 100644 --- a/opensfm/io.py +++ b/opensfm/io.py @@ -1,15 +1,27 @@ +# pyre-strict import json import logging import os import shutil from abc import ABC, abstractmethod from pathlib import Path -from typing import Union, Dict, Any, Iterable, List, IO, Tuple, TextIO, Optional +from typing import ( + Any, + BinaryIO, + Dict, + IO, + Iterable, + List, + Optional, + TextIO, + Tuple, + Union, +) import cv2 import numpy as np import pyproj -from numpy import ndarray +from numpy.typing import NDArray from opensfm import context, features, geo, pygeometry, pymap, types from PIL import Image @@ -20,6 +32,8 @@ import warnings warnings.filterwarnings("ignore", category=rasterio.errors.NotGeoreferencedWarning) +JSONType = Any # pyre-ignore[33] + logger: logging.Logger = logging.getLogger(__name__) logging.getLogger("rasterio").setLevel(logging.WARNING) @@ -344,7 +358,7 @@ def cameras_from_json(obj: Dict[str, Any]) -> Dict[str, pygeometry.Camera]: return cameras -def camera_to_json(camera) -> Dict[str, Any]: +def camera_to_json(camera: pygeometry.Camera) -> Dict[str, Any]: """ Write camera to a json object """ @@ -548,6 +562,10 @@ def pymap_metadata_to_json(metadata: pymap.ShotMeasurements) -> Dict[str, Any]: obj["compass"] = {"accuracy": metadata.compass_accuracy.value} if metadata.sequence_key.has_value: obj["skey"] = metadata.sequence_key.value + if metadata.opk_angles.has_value: + obj["opk_angles"] = list(metadata.opk_angles.value) + if metadata.opk_accuracy.has_value: + obj["opk_accuracy"] = metadata.opk_accuracy.value return obj @@ -571,6 +589,10 @@ def json_to_pymap_metadata(obj: Dict[str, Any]) -> pymap.ShotMeasurements: metadata.compass_angle.value = compass["angle"] if "accuracy" in compass: metadata.compass_accuracy.value = compass["accuracy"] + if obj.get("opk_angles") is not None: + metadata.opk_angles.value = obj.get("opk_angles") + if obj.get("opk_accuracy") is not None: + metadata.opk_accuracy.value = obj.get("opk_accuracy") return metadata @@ -606,7 +628,8 @@ def reconstruction_to_json(reconstruction: types.Reconstruction) -> Dict[str, An if len(reconstruction.rig_instances): obj["rig_instances"] = {} for rig_instance in reconstruction.rig_instances.values(): - obj["rig_instances"][rig_instance.id] = rig_instance_to_json(rig_instance) + obj["rig_instances"][rig_instance.id] = rig_instance_to_json( + rig_instance) # Extract shots for shot in reconstruction.shots.values(): @@ -663,7 +686,7 @@ def bias_to_json(bias: pygeometry.Similarity) -> Dict[str, Any]: def rig_cameras_to_json( - rig_cameras: Dict[str, pymap.RigCamera] + rig_cameras: Dict[str, pymap.RigCamera], ) -> Dict[str, Dict[str, Any]]: """ Write rig cameras to a json object @@ -701,7 +724,8 @@ def camera_from_vector( elif projection_type == "fisheye62": fx, fy, cx, cy, k1, k2, k3, k4, k5, k6, p1, p2 = parameters camera = pygeometry.Camera.create_fisheye62( - fx, fy / fx, np.array([cx, cy]), np.array([k1, k2, k3, k4, k5, k6, p1, p2]) + fx, fy / fx, np.array([cx, cy]), np.array([k1, + k2, k3, k4, k5, k6, p1, p2]) ) elif projection_type == "fisheye624": fx, fy, cx, cy, k1, k2, k3, k4, k5, k6, p1, p2, s0, s1, s2, s3 = parameters @@ -831,7 +855,7 @@ def camera_to_vector(camera: pygeometry.Camera) -> List[float]: def _read_gcp_list_lines( lines: Iterable[str], - projection, + projection: Optional[pyproj.Transformer], exifs: Dict[str, Dict[str, Any]], ) -> List[pymap.GroundControlPoint]: points = {} @@ -867,6 +891,7 @@ def _read_gcp_list_lines( point.lla = {"latitude": lat, "longitude": lon, "altitude": alt} point.has_altitude = has_altitude + point.coordinates = [easting, northing, alt] points[key] = point @@ -902,31 +927,35 @@ def _parse_utm_projection_string(line: str) -> str: return s.format(zone_number, zone_hemisphere) -def _parse_projection(line: str) -> Optional[pyproj.Transformer]: - """Build a proj4 from the GCP format line.""" - crs_4326 = pyproj.CRS.from_epsg(4326) - if line.strip() == "WGS84": +def _parse_projection_string(line: str) -> Optional[str]: + """Parse the projection string from the GCP format line.""" + line = line.strip() + if line in ["WGS84", "EPSG:4326"]: return None elif line.upper().startswith("WGS84 UTM"): - return pyproj.Transformer.from_proj( - pyproj.CRS(_parse_utm_projection_string(line)), crs_4326 - ) - elif "+proj" in line: - return pyproj.Transformer.from_proj(pyproj.CRS(line), crs_4326) - elif line.upper().startswith("EPSG:"): - return pyproj.Transformer.from_proj( - pyproj.CRS.from_epsg(int(line.split(":")[1])), crs_4326 - ) + return _parse_utm_projection_string(line) + elif "+proj" in line or line.upper().startswith("EPSG:"): + return line else: raise ValueError("Un-supported geo system definition: {}".format(line)) +def read_gcp_projection_string(fileobj: IO[str]) -> Optional[str]: + """Read the projection string from a gcp_list.txt file.""" + for line in fileobj: + if _valid_gcp_line(line): + return _parse_projection_string(line) + return None + + def _valid_gcp_line(line: str) -> bool: stripped = line.strip() return stripped != "" and stripped[0] != "#" -def read_gcp_list(fileobj, exif: Dict[str, Any]) -> List[pymap.GroundControlPoint]: +def read_gcp_list( + fileobj: IO[str], exif: Dict[str, Any] +) -> List[pymap.GroundControlPoint]: """Read a ground control points from a gcp_list.txt file. It requires the points to be in the WGS84 lat, lon, alt format. @@ -934,12 +963,14 @@ def read_gcp_list(fileobj, exif: Dict[str, Any]) -> List[pymap.GroundControlPoin """ all_lines = fileobj.readlines() lines = iter(filter(_valid_gcp_line, all_lines)) - projection = _parse_projection(next(lines)) + projection_string = _parse_projection_string(next(lines)) + projection = geo.construct_proj_transformer( + projection_string) if projection_string else None points = _read_gcp_list_lines(lines, projection, exif) return points -def read_ground_control_points(fileobj: IO) -> List[pymap.GroundControlPoint]: +def read_ground_control_points(fileobj: IO[str]) -> List[pymap.GroundControlPoint]: """Read ground control points from json file""" obj = json_load(fileobj) @@ -952,6 +983,9 @@ def read_ground_control_points(fileobj: IO) -> List[pymap.GroundControlPoint]: point.lla = lla point.has_altitude = "altitude" in point.lla + if "coordinates" in point_dict: + point.coordinates = point_dict["coordinates"] + observations = [] observing_images = set() for o_dict in point_dict["observations"]: @@ -974,7 +1008,7 @@ def read_ground_control_points(fileobj: IO) -> List[pymap.GroundControlPoint]: def write_ground_control_points( gcp: List[pymap.GroundControlPoint], - fileobj: IO, + fileobj: IO[str], ) -> None: """Write ground control points to json file.""" obj = {"points": []} @@ -990,6 +1024,9 @@ def write_ground_control_points( if point.has_altitude: point_obj["position"]["altitude"] = point.lla["altitude"] + if hasattr(point, "coordinates"): + point_obj["coordinates"] = list(point.coordinates) + point_obj["observations"] = [] for observation in point.observations: point_obj["observations"].append( @@ -1012,20 +1049,21 @@ def json_dump_kwargs(minify: bool = False) -> Dict[str, Any]: return {"indent": indent, "ensure_ascii": False, "separators": separators} -def json_dump(data, fout: IO[str], minify: bool = False) -> None: +def json_dump(data: JSONType, fout: IO[str], minify: bool = False) -> None: kwargs = json_dump_kwargs(minify) return json.dump(data, fout, **kwargs) -def json_dumps(data, minify: bool = False) -> str: +def json_dumps(data: JSONType, minify: bool = False) -> str: kwargs = json_dump_kwargs(minify) return json.dumps(data, **kwargs) -def json_load(fp: Union[IO[str], IO[bytes]]) -> Any: + +def json_load(fp: Union[IO[str], IO[bytes]]) -> JSONType: return json.load(fp) -def json_loads(text: Union[str, bytes]) -> Any: +def json_loads(text: Union[str, bytes]) -> JSONType: return json.loads(text) @@ -1046,9 +1084,9 @@ def ply_header( "property float nx", "property float ny", "property float nz", - "property uchar diffuse_red", - "property uchar diffuse_green", - "property uchar diffuse_blue", + "property uchar red", + "property uchar green", + "property uchar blue", ] else: header = [ @@ -1058,9 +1096,9 @@ def ply_header( "property float x", "property float y", "property float z", - "property uchar diffuse_red", - "property uchar diffuse_green", - "property uchar diffuse_blue", + "property uchar red", + "property uchar green", + "property uchar blue", ] if point_num_views: @@ -1096,7 +1134,8 @@ def reconstruction_to_ply( if point_num_views and tracks_manager: obs_count = point.number_of_observations() if obs_count == 0: - obs_count = len(tracks_manager.get_track_observations(point.id)) + obs_count = len( + tracks_manager.get_track_observations(point.id)) s += " {}".format(obs_count) vertices.append(s) @@ -1106,11 +1145,16 @@ def reconstruction_to_ply( o = shot.pose.get_origin() R = shot.pose.get_rotation_matrix() for axis in range(3): - c = 255 * np.eye(3)[axis] + c = np.eye(3)[axis] * 255 for depth in np.linspace(0, 2, 10): p = o + depth * R[axis] s = "{} {} {} {} {} {}".format( - p[0], p[1], p[2], int(c[0]), int(c[1]), int(c[2]) + p[0], + p[1], + p[2], + int(c[0]), + int(c[1]), + int(c[2]), ) if point_num_views: s += " 0" @@ -1120,7 +1164,7 @@ def reconstruction_to_ply( def point_cloud_from_ply( fp: TextIO, -) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: +) -> Tuple[NDArray, NDArray, NDArray, NDArray]: """Load point cloud from a PLY file.""" all_lines = fp.read().splitlines() start = all_lines.index("end_header") + 1 @@ -1144,10 +1188,10 @@ def point_cloud_from_ply( def point_cloud_to_ply( - points: np.ndarray, - normals: np.ndarray, - colors: np.ndarray, - labels: np.ndarray, + points: NDArray, + normals: NDArray, + colors: NDArray, + labels: NDArray, fp: TextIO, ) -> None: fp.write("ply\n") @@ -1159,9 +1203,9 @@ def point_cloud_to_ply( fp.write("property float nx\n") fp.write("property float ny\n") fp.write("property float nz\n") - fp.write("property uchar diffuse_red\n") - fp.write("property uchar diffuse_green\n") - fp.write("property uchar diffuse_blue\n") + fp.write("property uchar red\n") + fp.write("property uchar green\n") + fp.write("property uchar blue\n") fp.write("property uchar class\n") fp.write("end_header\n") @@ -1190,19 +1234,19 @@ def mkdir_p(path: str) -> None: os.makedirs(path, exist_ok=True) -def open_wt(path: str) -> IO[Any]: +def open_wt(path: str) -> TextIO: """Open a file in text mode for writing utf-8.""" return open(path, "w", encoding="utf-8") -def open_rt(path: str) -> IO[Any]: +def open_rt(path: str) -> TextIO: """Open a file in text mode for reading utf-8.""" return open(path, "r", encoding="utf-8") def imread( path: str, grayscale: bool = False, unchanged: bool = False, anydepth: bool = False -) -> ndarray: +) -> NDArray: _, ext = os.path.splitext(path) if ext.lower() == ".tiff" or ext.lower() == ".tif": return imread_rasterio(path, grayscale, unchanged, anydepth) @@ -1214,8 +1258,11 @@ def imread( def imread_from_fileobject( - fb, grayscale: bool = False, unchanged: bool = False, anydepth: bool = False -) -> np.ndarray: + fb: IO[bytes], + grayscale: bool = False, + unchanged: bool = False, + anydepth: bool = False, +) -> NDArray: """Load image as an array ignoring EXIF orientation.""" if context.OPENCV3: if grayscale: @@ -1260,10 +1307,6 @@ def imread_from_fileobject( return image - @classmethod - def imwrite(cls, path: str, image: np.ndarray) -> None: - with cls.open(path, "wb") as fwb: - imwrite(fwb, image, path) def imread_rasterio(path, grayscale=False, unchanged=False, anydepth=False): """Load image as an array ignoring EXIF orientation.""" @@ -1322,10 +1365,13 @@ def imwrite(path, image: np.ndarray) -> None: return imwrite_from_fileobject(fwb, image, ext) -def imwrite_from_fileobject(fwb, image: np.ndarray, ext: str) -> None: +def imwrite_from_fileobject( + fwb: Union[IO[str], IO[bytes]], image: NDArray, ext: str +) -> None: """Write an image to a file object""" if len(image.shape) == 3: - image[:, :, :3] = image[:, :, [2, 1, 0]] # Turn RGB to BGR (or RGBA to BGRA) + # Turn RGB to BGR (or RGBA to BGRA) + image[:, :, :3] = image[:, :, [2, 1, 0]] _, im_buffer = cv2.imencode(ext, image) fwb.write(im_buffer) @@ -1354,7 +1400,7 @@ def imwrite_rasterio(path, image: np.ndarray): def image_size_from_fileobject( - fb: Union[IO[bytes], bytes, Path, str, TextIO] + fb: Union[IO[bytes], bytes, Path, str, TextIO], ) -> Tuple[int, int]: """Height and width of an image.""" if isinstance(fb, TextIO): @@ -1376,70 +1422,73 @@ def image_size(path: str) -> Tuple[int, int]: class IoFilesystemBase(ABC): @classmethod @abstractmethod - def exists(cls, path: str): - pass + def exists(cls, path: str) -> bool: ... @classmethod - def ls(cls, path: str): - pass + @abstractmethod + def ls(cls, path: str) -> List[str]: ... @classmethod @abstractmethod - def isfile(cls, path: str): - pass + def isfile(cls, path: str) -> bool: ... @classmethod @abstractmethod - def isdir(cls, path: str): - pass + def isdir(cls, path: str) -> bool: ... @classmethod - def rm_if_exist(cls, filename: str): - pass + @abstractmethod + def rm_if_exist(cls, filename: str) -> None: ... @classmethod - def symlink(cls, src_path: str, dst_path: str, **kwargs): - pass + @abstractmethod + def symlink(cls, src_path: str, dst_path: str, **kwargs: Any) -> None: ... @classmethod @abstractmethod - def open(cls, *args, **kwargs) -> IO[Any]: - pass + def open_wb(cls, path: str) -> BinaryIO: ... @classmethod @abstractmethod - def open_wt(cls, path: str): - pass + def open_rb(cls, path: str) -> BinaryIO: ... @classmethod @abstractmethod - def open_rt(cls, path: str): - pass + def open_wt(cls, path: str) -> TextIO: ... @classmethod @abstractmethod - def mkdir_p(cls, path: str): - pass + def open_rt(cls, path: str) -> TextIO: ... @classmethod @abstractmethod - def imwrite(cls, path: str, image): - pass + def open_at(cls, path: str) -> TextIO: ... + + @classmethod + @abstractmethod + def mkdir_p(cls, path: str) -> None: ... @classmethod @abstractmethod - def imread(cls, path: str, grayscale=False, unchanged=False, anydepth=False): - pass + def imwrite(cls, path: str, image: NDArray) -> None: ... + + @classmethod + @abstractmethod + def imread( + cls, + path: str, + grayscale: bool = False, + unchanged: bool = False, + anydepth: bool = False, + ) -> NDArray: ... @classmethod @abstractmethod - def image_size(cls, path: str): - pass + def image_size(cls, path: str) -> Tuple[int, int]: ... @classmethod @abstractmethod - def timestamp(cls, path: str): - pass + def timestamp(cls, path: str) -> float: ... class IoFilesystemDefault(IoFilesystemBase): @@ -1447,8 +1496,7 @@ def __init__(self) -> None: self.type = "default" @classmethod - def exists(cls, path: str) -> str: - # pyre-fixme[7]: Expected `str` but got `bool`. + def exists(cls, path: str) -> bool: return os.path.exists(path) @classmethod @@ -1456,13 +1504,11 @@ def ls(cls, path: str) -> List[str]: return os.listdir(path) @classmethod - def isfile(cls, path: str) -> str: - # pyre-fixme[7]: Expected `str` but got `bool`. + def isfile(cls, path: str) -> bool: return os.path.isfile(path) @classmethod - def isdir(cls, path: str) -> str: - # pyre-fixme[7]: Expected `str` but got `bool`. + def isdir(cls, path: str) -> bool: return os.path.isdir(path) @classmethod @@ -1484,18 +1530,30 @@ def symlink(cls, src_path: str, dst_path: str, **kwargs): @classmethod def open(cls, *args, **kwargs) -> IO[Any]: return open(*args, **kwargs) + + @classmethod + def open_wb(cls, path: str) -> BinaryIO: + return open(path, "wb") + + @classmethod + def open_rb(cls, path: str) -> BinaryIO: + return open(path, "rb") + + @classmethod + def open_wt(cls, path: str) -> TextIO: + return open(path, "wt") @classmethod - def open_wt(cls, path: str): - return cls.open(path, "w", encoding="utf-8") + def open_rt(cls, path: str) -> TextIO: + return open(path, "rt") @classmethod - def open_rt(cls, path: str): - return cls.open(path, "r", encoding="utf-8") + def open_at(cls, path: str) -> TextIO: + return open(path, "at") @classmethod - def mkdir_p(cls, path: str): - return os.makedirs(path, exist_ok=True) + def mkdir_p(cls, path: str) -> None: + os.makedirs(path, exist_ok=True) @classmethod def imread(cls, @@ -1531,6 +1589,5 @@ def image_size(cls, path: str) -> Tuple[int, int]: with rasterio.open(path, "r") as r: return r.height, r.width @classmethod - def timestamp(cls, path: str) -> str: - # pyre-fixme[7]: Expected `str` but got `float`. + def timestamp(cls, path: str) -> float: return os.path.getmtime(path) diff --git a/opensfm/large/metadataset.py b/opensfm/large/metadataset.py index 3166579b2..645f9deb7 100644 --- a/opensfm/large/metadataset.py +++ b/opensfm/large/metadataset.py @@ -1,26 +1,28 @@ +# pyre-strict +import glob import os import os.path import shutil import sys -import glob +from typing import Any, Dict, Generator, List, Tuple import numpy as np -from opensfm import config -from opensfm import io +from numpy.typing import NDArray +from opensfm import config, io from opensfm.dataset import DataSet class MetaDataSet: - def __init__(self, data_path): + def __init__(self, data_path: str) -> None: """ Create meta dataset instance for large scale reconstruction. :param data_path: Path to directory containing meta dataset """ - self.data_path = os.path.abspath(data_path) + self.data_path: str = os.path.abspath(data_path) config_file = os.path.join(self.data_path, "config.yaml") - self.config = config.load_config(config_file) + self.config: Dict[str, Any] = config.load_config(config_file) self._image_list_file_name = "image_list_with_gps.tsv" self._clusters_file_name = "clusters.npz" @@ -32,51 +34,51 @@ def __init__(self, data_path): io.mkdir_p(self._submodels_path()) - def _submodels_path(self): + def _submodels_path(self) -> str: return os.path.join(self.data_path, self.config["submodels_relpath"]) - def _submodel_path(self, i): + def _submodel_path(self, i: int) -> str: """Path to submodel i folder.""" template = self.config["submodel_relpath_template"] return os.path.join(self.data_path, template % i) - def _submodel_images_path(self, i): + def _submodel_images_path(self, i: int) -> str: """Path to submodel i images folder.""" template = self.config["submodel_images_relpath_template"] return os.path.join(self.data_path, template % i) - def _image_groups_path(self): + def _image_groups_path(self) -> str: return os.path.join(self.data_path, "image_groups.txt") - def _image_list_path(self): + def _image_list_path(self) -> str: return os.path.join(self._submodels_path(), self._image_list_file_name) - def _clusters_path(self): + def _clusters_path(self) -> str: return os.path.join(self._submodels_path(), self._clusters_file_name) - def _clusters_with_neighbors_path(self): + def _clusters_with_neighbors_path(self) -> str: return os.path.join( self._submodels_path(), self._clusters_with_neighbors_file_name ) - def _clusters_with_neighbors_geojson_path(self): + def _clusters_with_neighbors_geojson_path(self) -> str: return os.path.join( self._submodels_path(), self._clusters_with_neighbors_geojson_file_name ) - def _clusters_geojson_path(self): + def _clusters_geojson_path(self) -> str: return os.path.join(self._submodels_path(), self._clusters_geojson_file_name) - def _create_symlink(self, base_path, file_path): + def _create_symlink(self, base_path: str, file_path: str) -> None: src = os.path.join(self.data_path, file_path) dst = os.path.join(base_path, file_path) if not os.path.exists(src): return - + # Symlinks on Windows require admin privileges, # so we use hard links instead - if sys.platform == 'win32': + if sys.platform == "win32": if os.path.isdir(dst): shutil.rmtree(dst) elif os.path.isfile(dst): @@ -87,7 +89,7 @@ def _create_symlink(self, base_path, file_path): subfolders = len(file_path.split(os.path.sep)) - 1 - if sys.platform == 'win32': + if sys.platform == "win32": if os.path.isdir(src): # Create directory in destination, then make hard links # to files @@ -100,61 +102,65 @@ def _create_symlink(self, base_path, file_path): # Just make hard link os.link(src, dst) else: - os.symlink(os.path.join(*[".."] * subfolders, os.path.relpath(src, base_path)), dst) + os.symlink( + os.path.join(*[".."] * subfolders, os.path.relpath(src, base_path)), dst + ) - def image_groups_exists(self): + def image_groups_exists(self) -> bool: return os.path.isfile(self._image_groups_path()) - def load_image_groups(self): + def load_image_groups(self) -> Generator[List[str], None, None]: with open(self._image_groups_path()) as fin: for line in fin: yield line.split() - def image_list_exists(self): + def image_list_exists(self) -> bool: return os.path.isfile(self._image_list_path()) - def create_image_list(self, ills): + def create_image_list(self, ills: List[Tuple[str, float, float]]) -> None: with io.open_wt(self._image_list_path()) as csvfile: for image, lat, lon in ills: - csvfile.write(u"{}\t{}\t{}\n".format(image, lat, lon)) + csvfile.write("{}\t{}\t{}\n".format(image, lat, lon)) - def images_with_gps(self): + def images_with_gps(self) -> Generator[Tuple[str, float, float], None, None]: with io.open_rt(self._image_list_path()) as csvfile: for line in csvfile: - image, lat, lon = line.split(u"\t") + image, lat, lon = line.split("\t") yield image, float(lat), float(lon) - def save_clusters(self, images, positions, labels, centers): + def save_clusters( + self, images: NDArray, positions: NDArray, labels: NDArray, centers: NDArray + ) -> None: filepath = self._clusters_path() np.savez_compressed( filepath, images=images, positions=positions, labels=labels, centers=centers ) - def load_clusters(self): + def load_clusters(self) -> Tuple[NDArray, NDArray, NDArray, NDArray]: c = np.load(self._clusters_path()) images = c["images"].ravel() labels = c["labels"].ravel() return images, c["positions"], labels, c["centers"] - def save_clusters_with_neighbors(self, clusters): + def save_clusters_with_neighbors(self, clusters: List[List[int]]) -> None: filepath = self._clusters_with_neighbors_path() np.savez_compressed(filepath, clusters=np.array(clusters, dtype=object)) - def load_clusters_with_neighbors(self): + def load_clusters_with_neighbors(self) -> NDArray: c = np.load(self._clusters_with_neighbors_path(), allow_pickle=True) return c["clusters"] - def save_cluster_with_neighbors_geojson(self, geojson): + def save_cluster_with_neighbors_geojson(self, geojson: Dict[str, Any]) -> None: filepath = self._clusters_with_neighbors_geojson_path() with io.open_wt(filepath) as fout: io.json_dump(geojson, fout) - def save_clusters_geojson(self, geojson): + def save_clusters_geojson(self, geojson: Dict[str, Any]) -> None: filepath = self._clusters_geojson_path() with io.open_wt(filepath) as fout: io.json_dump(geojson, fout) - def remove_submodels(self): + def remove_submodels(self) -> None: sm = self._submodels_path() paths = [ os.path.join(sm, o) @@ -164,7 +170,7 @@ def remove_submodels(self): for path in paths: shutil.rmtree(path) - def create_submodels(self, clusters): + def create_submodels(self, clusters: NDArray) -> None: data = DataSet(self.data_path) for i, cluster in enumerate(clusters): # create sub model dirs @@ -180,7 +186,7 @@ def create_submodels(self, clusters): src = data.image_files[image] dst = os.path.join(submodel_images_path, image) if not os.path.isfile(dst): - if sys.platform == 'win32': + if sys.platform == "win32": os.link(src, dst) else: os.symlink(os.path.relpath(src, submodel_images_path), dst) @@ -214,8 +220,7 @@ def create_submodels(self, clusters): for filepath in filepaths: self._create_symlink(submodel_path, filepath) - - def get_submodel_paths(self): + def get_submodel_paths(self) -> List[str]: submodel_paths = [] for i in range(999999): submodel_path = self._submodel_path(i) diff --git a/opensfm/large/tools.py b/opensfm/large/tools.py index d3ababb0c..e88956387 100644 --- a/opensfm/large/tools.py +++ b/opensfm/large/tools.py @@ -1,12 +1,12 @@ +# pyre-strict +from __future__ import annotations + import itertools import logging -from collections import namedtuple -from functools import lru_cache -from typing import Dict, List, Tuple +from typing import Callable, Dict, List, NamedTuple, Tuple import cv2 import networkx as nx -from networkx.classes.reportviews import EdgeView import numpy as np import scipy.spatial as spatial import vmem @@ -14,8 +14,8 @@ import sys from collections import namedtuple from networkx.algorithms import bipartite -from opensfm.large.lru_cache import lru_cache - +from networkx.classes.reportviews import EdgeView +from numpy.typing import NDArray from opensfm import ( align, context, @@ -23,20 +23,24 @@ geo, multiview, pybundle, + pymap, reconstruction, types, - pymap, ) +from opensfm.large.metadataset import MetaDataSet +from opensfm.large.lru_cache import lru_cache logger: logging.Logger = logging.getLogger(__name__) -PartialReconstruction = namedtuple("PartialReconstruction", ["submodel_path", "index"]) +class PartialReconstruction(NamedTuple): + submodel_path: str + idx: int def kmeans( - samples, nclusters, max_iter: int = 100, attempts: int = 20 -) -> Tuple[np.ndarray, np.ndarray]: + samples: NDArray, nclusters: int, max_iter: int = 100, attempts: int = 20 +) -> Tuple[NDArray, NDArray]: criteria = (cv2.TERM_CRITERIA_MAX_ITER, max_iter, 1.0) flags = cv2.KMEANS_PP_CENTERS @@ -47,8 +51,8 @@ def kmeans( def add_cluster_neighbors( - positions, labels, centers, max_distance -) -> List[List[np.ndarray]]: + positions: NDArray, labels: NDArray, centers: NDArray, max_distance: float +) -> List[List[NDArray]]: reflla = np.mean(positions, 0) reference = geo.TopocentricConverter(reflla[0], reflla[1], 0) @@ -74,7 +78,9 @@ def add_cluster_neighbors( return clusters -def connected_reconstructions(reconstruction_shots) -> EdgeView: +def connected_reconstructions( + reconstruction_shots: Dict[PartialReconstruction, Dict[str, pymap.Shot]], +) -> EdgeView[None]: g = nx.Graph() for r in reconstruction_shots: g.add_node(r, bipartite=0) @@ -87,7 +93,7 @@ def connected_reconstructions(reconstruction_shots) -> EdgeView: return p.edges() -def scale_matrix(covariance: np.ndarray) -> np.ndarray: +def scale_matrix(covariance: NDArray) -> NDArray: try: L = np.linalg.cholesky(covariance) except Exception: @@ -101,9 +107,7 @@ def scale_matrix(covariance: np.ndarray) -> np.ndarray: return np.linalg.inv(L) -def invert_similarity( - s: float, A: np.ndarray, b: np.ndarray -) -> Tuple[float, np.ndarray, float]: +def invert_similarity(s: float, A: NDArray, b: NDArray) -> Tuple[float, NDArray, float]: s_inv = 1 / s A_inv = A.T b_inv = -s_inv * A_inv.dot(b) @@ -111,11 +115,15 @@ def invert_similarity( return s_inv, A_inv, b_inv -def partial_reconstruction_name(key) -> str: - return str(key.submodel_path) + "_index" + str(key.index) +def partial_reconstruction_name(key: PartialReconstruction) -> str: + return str(key.submodel_path) + "_index" + str(key.idx) -def add_camera_constraints_soft(ra, reconstruction_shots, reconstruction_name) -> None: +def add_camera_constraints_soft( + ra: pybundle.ReconstructionAlignment, + reconstruction_shots: Dict[PartialReconstruction, Dict[str, pymap.Shot]], + reconstruction_name: Callable[[PartialReconstruction], str], +) -> None: added_shots = set() for key in reconstruction_shots: shots = reconstruction_shots[key] @@ -154,7 +162,10 @@ def add_camera_constraints_soft(ra, reconstruction_shots, reconstruction_name) - def add_camera_constraints_hard( - ra, reconstruction_shots, reconstruction_name, add_common_camera_constraint + ra: pybundle.ReconstructionAlignment, + reconstruction_shots: Dict[PartialReconstruction, Dict[str, pymap.Shot]], + reconstruction_name: Callable[[PartialReconstruction], str], + add_common_camera_constraint: bool, ) -> None: for key in reconstruction_shots: shots = reconstruction_shots[key] @@ -197,7 +208,7 @@ def add_camera_constraints_hard( @lru_cache(use_memory_up_to=vmem.virtual_memory().available * 0.9) def load_reconstruction( - path, index + path: str, index: int ) -> Tuple[str, Tuple[types.Reconstruction, pymap.TracksManager]]: d1 = dataset.DataSet(path) r1 = d1.load_reconstruction()[index] @@ -205,15 +216,18 @@ def load_reconstruction( return (path + ("_%s" % index)), (r1, g1) -def add_point_constraints(ra, reconstruction_shots, reconstruction_name) -> None: +def add_point_constraints( + ra: pybundle.ReconstructionAlignment, + reconstruction_shots: Dict[PartialReconstruction, Dict[str, pymap.Shot]], + reconstruction_name: Callable[[PartialReconstruction], str], +) -> None: connections = connected_reconstructions(reconstruction_shots) for connection in connections: - i1, (r1, g1) = load_reconstruction( - connection[0].submodel_path, connection[0].index + connection[0].submodel_path, connection[0].idx ) i2, (r2, g2) = load_reconstruction( - connection[1].submodel_path, connection[1].index + connection[1].submodel_path, connection[1].idx ) rec_name1 = reconstruction_name(connection[0]) @@ -245,7 +259,9 @@ def add_point_constraints(ra, reconstruction_shots, reconstruction_name) -> None ) -def load_reconstruction_shots(meta_data) -> Dict[str, pymap.Shot]: +def load_reconstruction_shots( + meta_data: MetaDataSet, +) -> Dict[PartialReconstruction, Dict[str, pymap.Shot]]: reconstruction_shots = {} for submodel_path in meta_data.get_submodel_paths(): data = dataset.DataSet(submodel_path) @@ -261,11 +277,11 @@ def load_reconstruction_shots(meta_data) -> Dict[str, pymap.Shot]: def align_reconstructions( - reconstruction_shots, - reconstruction_name, - use_points_constraints, + reconstruction_shots: Dict[PartialReconstruction, Dict[str, pymap.Shot]], + reconstruction_name: Callable[[PartialReconstruction], str], + use_points_constraints: bool, camera_constraint_type: str = "soft_camera_constraint", -) -> Dict[str, Tuple[float, np.ndarray, float]]: +) -> Dict[PartialReconstruction, Tuple[float, NDArray, NDArray]]: ra = pybundle.ReconstructionAlignment() if camera_constraint_type == "soft_camera_constraint": @@ -291,7 +307,9 @@ def align_reconstructions( return transformations -def apply_transformations(transformations) -> None: +def apply_transformations( + transformations: Dict[PartialReconstruction, Tuple[float, NDArray, NDArray]], +) -> None: submodels = itertools.groupby( sorted(transformations.keys(), key=lambda key: key.submodel_path), lambda key: key.submodel_path, @@ -303,7 +321,7 @@ def apply_transformations(transformations) -> None: reconstruction = data.load_reconstruction() for key in keys: - partial_reconstruction = reconstruction[key.index] + partial_reconstruction = reconstruction[key.idx] s, A, b = transformations[key] align.apply_similarity(partial_reconstruction, s, A, b) diff --git a/opensfm/log.py b/opensfm/log.py index f4bbab659..a1cd8989e 100644 --- a/opensfm/log.py +++ b/opensfm/log.py @@ -1,3 +1,4 @@ +# pyre-strict import logging import os from typing import Optional diff --git a/opensfm/masking.py b/opensfm/masking.py index 7a03640ce..61449ddea 100644 --- a/opensfm/masking.py +++ b/opensfm/masking.py @@ -1,17 +1,17 @@ +# pyre-strict import logging -from typing import List, Tuple, Optional +from typing import List, Optional, Tuple import cv2 import numpy as np +from numpy.typing import NDArray from opensfm import upright from opensfm.dataset_base import DataSetBase logger: logging.Logger = logging.getLogger(__name__) -def mask_from_segmentation( - segmentation: np.ndarray, ignore_values: List[int] -) -> np.ndarray: +def mask_from_segmentation(segmentation: NDArray, ignore_values: List[int]) -> NDArray: """Binary mask that is 0 for pixels with segmentation value to ignore.""" mask = np.ones(segmentation.shape, dtype=np.uint8) for value in ignore_values: @@ -20,8 +20,8 @@ def mask_from_segmentation( def combine_masks( - mask1: Optional[np.ndarray], mask2: Optional[np.ndarray] -) -> Optional[np.ndarray]: + mask1: Optional[NDArray], mask2: Optional[NDArray] +) -> Optional[NDArray]: """Combine two masks as mask1 AND mask2. Ignore any missing mask argument. @@ -40,9 +40,9 @@ def combine_masks( def _resize_masks_to_match( - im1: np.ndarray, - im2: np.ndarray, -) -> Tuple[np.ndarray, np.ndarray]: + im1: NDArray, + im2: NDArray, +) -> Tuple[NDArray, NDArray]: h, w = max(im1.shape, im2.shape) if im1.shape != (h, w): im1 = cv2.resize(im1, (w, h), interpolation=cv2.INTER_NEAREST) @@ -54,9 +54,9 @@ def _resize_masks_to_match( def load_features_mask( data: DataSetBase, image: str, - points: np.ndarray, - mask_image: Optional[np.ndarray] = None, -) -> np.ndarray: + points: NDArray, + mask_image: Optional[NDArray] = None, +) -> NDArray: """Load a feature-wise mask. This is a binary array true for features that lie inside the @@ -101,7 +101,7 @@ def load_features_mask( return np.array(mask, dtype=bool) -def _load_segmentation_mask(data: DataSetBase, image: str) -> Optional[np.ndarray]: +def _load_segmentation_mask(data: DataSetBase, image: str) -> Optional[NDArray]: """Build a mask from segmentation ignore values. The mask is non-zero only for pixels with segmentation @@ -118,7 +118,7 @@ def _load_segmentation_mask(data: DataSetBase, image: str) -> Optional[np.ndarra return mask_from_segmentation(segmentation, ignore_values) -def _load_combined_mask(data: DataSetBase, image: str) -> Optional[np.ndarray]: +def _load_combined_mask(data: DataSetBase, image: str) -> Optional[NDArray]: """Combine binary mask with segmentation mask. Return a mask that is non-zero only where the binary diff --git a/opensfm/matching.py b/opensfm/matching.py index cb69c6278..32f5ce551 100644 --- a/opensfm/matching.py +++ b/opensfm/matching.py @@ -1,9 +1,11 @@ +# pyre-strict import logging from timeit import default_timer as timer -from typing import Sized, Optional, Dict, Any, Tuple, List, Generator +from typing import Any, Dict, Generator, List, Optional, Sized, Tuple import cv2 import numpy as np +from numpy.typing import NDArray from opensfm import ( context, feature_loader, @@ -185,8 +187,8 @@ def match_unwrap_args( DataSetBase, Dict[str, Any], Optional[Dict[str, pygeometry.Pose]], - ] -) -> Tuple[str, str, np.ndarray]: + ], +) -> Tuple[str, str, NDArray]: """Wrapper for parallel processing of pair matching. Compute all pair matchings of a given image and save them. @@ -218,7 +220,7 @@ def match_descriptors( camera2: pygeometry.Camera, data: DataSetBase, config_override: Dict[str, Any], -) -> np.ndarray: +) -> NDArray: """Perform descriptor matching for a pair of images.""" # Override parameters overriden_config = data.config.copy() @@ -240,8 +242,7 @@ def match_descriptors( symmetric = "symmetric" if overriden_config["symmetric_matching"] else "one-way" logger.debug( - "Matching {} and {}. Matcher: {} ({}) " - "T-desc: {:1.3f} Matches: {}".format( + "Matching {} and {}. Matcher: {} ({}) " "T-desc: {:1.3f} Matches: {}".format( im1, im2, matcher_type, @@ -261,7 +262,7 @@ def _match_descriptors_guided_impl( relative_pose: pygeometry.Pose, data: DataSetBase, overriden_config: Dict[str, Any], -) -> Tuple[np.ndarray, np.ndarray, np.ndarray, str]: +) -> Tuple[NDArray, NDArray, NDArray, str]: """Perform descriptor guided matching for a pair of images, using their relative pose. It also apply static objects removal.""" guided_matcher_override = "BRUTEFORCE" matcher_type = overriden_config["matcher_type"].upper() @@ -341,7 +342,7 @@ def _match_descriptors_impl( camera2: pygeometry.Camera, data: DataSetBase, overriden_config: Dict[str, Any], -) -> Tuple[np.ndarray, np.ndarray, np.ndarray, str]: +) -> Tuple[NDArray, NDArray, NDArray, str]: """Perform descriptor matching for a pair of images. It also apply static objects removal.""" dummy = np.array([]) matcher_type = overriden_config["matcher_type"].upper() @@ -464,7 +465,8 @@ def match_robust( camera2: pygeometry.Camera, data: DataSetBase, config_override: Dict[str, Any], -) -> np.ndarray: + input_is_masked: bool = True, +) -> NDArray: """Perform robust geometry matching on a set of matched descriptors indexes.""" # Override parameters overriden_config = data.config.copy() @@ -475,10 +477,16 @@ def match_robust( "matching_use_segmentation" ] # unused but keep using the same cache features_data1 = feature_loader.instance.load_all_data( - data, im1, masked=True, segmentation_in_descriptor=segmentation_in_descriptor + data, + im1, + masked=input_is_masked, + segmentation_in_descriptor=segmentation_in_descriptor, ) features_data2 = feature_loader.instance.load_all_data( - data, im2, masked=True, segmentation_in_descriptor=segmentation_in_descriptor + data, + im2, + masked=input_is_masked, + segmentation_in_descriptor=segmentation_in_descriptor, ) if ( features_data1 is None @@ -508,8 +516,10 @@ def match_robust( rmatches_unfiltered = [] m1 = feature_loader.instance.load_mask(data, im1) m2 = feature_loader.instance.load_mask(data, im2) - if m1 is not None and m2 is not None: + if m1 is not None and m2 is not None and input_is_masked: rmatches_unfiltered = unfilter_matches(rmatches, m1, m2) + else: + rmatches_unfiltered = rmatches robust_matching_min_match = overriden_config["robust_matching_min_match"] logger.debug( @@ -532,14 +542,14 @@ def match_robust( def _match_robust_impl( im1: str, im2: str, - p1: np.ndarray, - p2: np.ndarray, - matches: np.ndarray, + p1: NDArray, + p2: NDArray, + matches: NDArray, camera1: pygeometry.Camera, camera2: pygeometry.Camera, data: DataSetBase, overriden_config: Dict[str, Any], -) -> np.ndarray: +) -> NDArray: """Perform robust geometry matching on a set of matched descriptors indexes.""" # robust matching rmatches = robust_match(p1, p2, camera1, camera2, matches, overriden_config) @@ -555,7 +565,7 @@ def match( data: DataSetBase, config_override: Dict[str, Any], guided_matching_pose: Optional[pygeometry.Pose], -) -> np.ndarray: +) -> NDArray: """Perform full matching (descriptor+robust, optionally guided) for a pair of images.""" # Override parameters overriden_config = data.config.copy() @@ -622,12 +632,12 @@ def match( def match_words( - f1: np.ndarray, - words1: np.ndarray, - f2: np.ndarray, - words2: np.ndarray, + f1: NDArray, + words1: NDArray, + f2: NDArray, + words2: NDArray, config: Dict[str, Any], -) -> np.ndarray: +) -> NDArray: """Match using words and apply Lowe's ratio filter. Args: @@ -643,10 +653,10 @@ def match_words( def match_words_symmetric( - f1: np.ndarray, - words1: np.ndarray, - f2: np.ndarray, - words2: np.ndarray, + f1: NDArray, + words1: NDArray, + f2: NDArray, + words2: NDArray, config: Dict[str, Any], ) -> List[Tuple[int, int]]: """Match using words in both directions and keep consistent matches. @@ -667,7 +677,7 @@ def match_words_symmetric( def match_flann( - index: Any, f2: np.ndarray, config: Dict[str, Any] + index: cv2.flann_Index, f2: NDArray, config: Dict[str, Any] ) -> List[Tuple[int, int]]: """Match using FLANN and apply Lowe's ratio filter. @@ -677,14 +687,18 @@ def match_flann( config: config parameters """ search_params = dict(checks=config["flann_checks"]) - results, dists = index.knnSearch(f2, 2, params=search_params) + results, dists = index.knnSearch(f2, 2, params=search_params) # pyre-ignore[16] squared_ratio = config["lowes_ratio"] ** 2 # Flann returns squared L2 distances good = dists[:, 0] < squared_ratio * dists[:, 1] return list(zip(results[good, 0], good.nonzero()[0])) def match_flann_symmetric( - fi: np.ndarray, indexi: Any, fj: np.ndarray, indexj: Any, config: Dict[str, Any] + fi: NDArray, + indexi: cv2.flann_Index, + fj: NDArray, + indexj: cv2.flann_Index, + config: Dict[str, Any], ) -> List[Tuple[int, int]]: """Match using FLANN in both directions and keep consistent matches. @@ -703,10 +717,10 @@ def match_flann_symmetric( def match_brute_force( - f1: np.ndarray, - f2: np.ndarray, + f1: NDArray, + f2: NDArray, config: Dict[str, Any], - maskij: Optional[np.ndarray] = None, + maskij: Optional[NDArray] = None, ) -> List[Tuple[int, int]]: """Brute force matching and Lowe's ratio filtering. @@ -735,19 +749,14 @@ def match_brute_force( m, n = match if m.distance < ratio * n.distance: good_matches.append(m) - return _convert_matches_to_vector(good_matches) - - -def _convert_matches_to_vector(matches: List[Any]) -> List[Tuple[int, int]]: - """Convert Dmatch object to matrix form.""" - return [(mm.queryIdx, mm.trainIdx) for mm in matches] + return [(mm.queryIdx, mm.trainIdx) for mm in good_matches] def match_brute_force_symmetric( - fi: np.ndarray, - fj: np.ndarray, + fi: NDArray, + fj: NDArray, config: Dict[str, Any], - maskij: Optional[np.ndarray] = None, + maskij: Optional[NDArray] = None, ) -> List[Tuple[int, int]]: """Match with brute force in both directions and keep consistent matches. @@ -765,11 +774,11 @@ def match_brute_force_symmetric( def robust_match_fundamental( - p1: np.ndarray, - p2: np.ndarray, - matches: np.ndarray, + p1: NDArray, + p2: NDArray, + matches: NDArray, config: Dict[str, Any], -) -> Tuple[np.ndarray, np.ndarray]: +) -> Tuple[NDArray, NDArray]: """Filter matches by estimating the Fundamental matrix via RANSAC.""" if len(matches) < 8: return np.array([]), np.array([]) @@ -789,7 +798,11 @@ def robust_match_fundamental( def compute_inliers_bearings( - b1: np.ndarray, b2: np.ndarray, R: np.ndarray, t: np.ndarray, threshold: float=0.01 + b1: NDArray, + b2: NDArray, + R: NDArray, + t: NDArray, + threshold: float = 0.01, ) -> List[bool]: """Compute points that can be triangulated. @@ -826,8 +839,8 @@ def compute_inliers_bearings( def compute_inliers_bearing_epipolar( - b1: np.ndarray, b2: np.ndarray, pose: pygeometry.Pose, threshold: float -) -> np.ndarray: + b1: NDArray, b2: NDArray, pose: pygeometry.Pose, threshold: float +) -> NDArray: """Compute mask of epipolarly consistent bearings, given two lists of bearings Args: @@ -848,13 +861,13 @@ def compute_inliers_bearing_epipolar( def robust_match_calibrated( - p1: np.ndarray, - p2: np.ndarray, + p1: NDArray, + p2: NDArray, camera1: pygeometry.Camera, camera2: pygeometry.Camera, - matches: np.ndarray, + matches: NDArray, config: Dict[str, Any], -) -> np.ndarray: +) -> NDArray: """Filter matches by estimating the Essential matrix via RANSAC.""" if len(matches) < 8: @@ -883,13 +896,13 @@ def robust_match_calibrated( def robust_match( - p1: np.ndarray, - p2: np.ndarray, + p1: NDArray, + p2: NDArray, camera1: pygeometry.Camera, camera2: pygeometry.Camera, - matches: np.ndarray, + matches: NDArray, config: Dict[str, Any], -) -> np.ndarray: +) -> NDArray: """Filter matches by fitting a geometric model. If cameras are perspective without distortion, then the Fundamental @@ -908,7 +921,7 @@ def robust_match( return robust_match_calibrated(p1, p2, camera1, camera2, matches, config) -def unfilter_matches(matches, m1, m2) -> np.ndarray: +def unfilter_matches(matches: NDArray, m1: NDArray, m2: NDArray) -> NDArray: """Given matches and masking arrays, get matches with un-masked indexes.""" i1 = np.flatnonzero(m1) i2 = np.flatnonzero(m2) @@ -920,10 +933,10 @@ def apply_adhoc_filters( matches: List[Tuple[int, int]], im1: str, camera1: pygeometry.Camera, - p1: np.ndarray, + p1: NDArray, im2: str, camera2: pygeometry.Camera, - p2: np.ndarray, + p2: NDArray, ) -> List[Tuple[int, int]]: """Apply a set of filters functions defined further below for removing static data in images. @@ -937,7 +950,7 @@ def apply_adhoc_filters( def _non_static_matches( - p1: np.ndarray, p2: np.ndarray, matches: List[Tuple[int, int]] + p1: NDArray, p2: NDArray, matches: List[Tuple[int, int]] ) -> List[Tuple[int, int]]: """Remove matches with same position in both images. @@ -948,7 +961,7 @@ def _non_static_matches( res = [] for match in matches: d = p1[match[0]] - p2[match[1]] - if d[0] ** 2 + d[1] ** 2 >= threshold ** 2: + if d[0] ** 2 + d[1] ** 2 >= threshold**2: res.append(match) static_ratio_threshold = 0.85 @@ -960,8 +973,8 @@ def _non_static_matches( def _not_on_pano_poles_matches( - p1: np.ndarray, - p2: np.ndarray, + p1: NDArray, + p2: NDArray, matches: List[Tuple[int, int]], camera1: pygeometry.Camera, camera2: pygeometry.Camera, @@ -987,8 +1000,8 @@ def _not_on_pano_poles_matches( def _not_on_vermont_watermark( - p1: np.ndarray, - p2: np.ndarray, + p1: NDArray, + p2: NDArray, matches: List[Tuple[int, int]], im1: str, im2: str, @@ -1005,7 +1018,7 @@ def _not_on_vermont_watermark( return matches -def _vermont_valid_mask(p: np.ndarray) -> bool: +def _vermont_valid_mask(p: NDArray) -> bool: """Check if pixel inside the valid region. Pixel coord Y should be larger than 50. @@ -1015,7 +1028,12 @@ def _vermont_valid_mask(p: np.ndarray) -> bool: def _not_on_blackvue_watermark( - p1: np.ndarray, p2: np.ndarray, matches: List[Tuple[int, int]], im1: str, im2: str, data: DataSetBase + p1: NDArray, + p2: NDArray, + matches: List[Tuple[int, int]], + im1: str, + im2: str, + data: DataSetBase, ) -> List[Tuple[int, int]]: """Filter Blackvue's watermark.""" meta1 = data.load_exif(im1) @@ -1028,7 +1046,7 @@ def _not_on_blackvue_watermark( return matches -def _blackvue_valid_mask(p: np.ndarray) -> bool: +def _blackvue_valid_mask(p: NDArray) -> bool: """Check if pixel inside the valid region. Pixel coord Y should be smaller than h - 70. diff --git a/opensfm/mesh.py b/opensfm/mesh.py index 47447c275..52e6eb00d 100644 --- a/opensfm/mesh.py +++ b/opensfm/mesh.py @@ -1,10 +1,11 @@ -#!/usr/bin/env python3 +# pyre-strict import itertools import logging -from typing import Any, Tuple, List +from typing import List, Tuple import numpy as np import scipy.spatial +from numpy.typing import NDArray from opensfm import pygeometry, pymap, types @@ -13,7 +14,7 @@ def triangle_mesh( shot_id: str, r: types.Reconstruction, tracks_manager: pymap.TracksManager -) -> Tuple[List[Any], List[Any]]: +) -> Tuple[List[List[float]], List[List[int]]]: """ Create triangle meshes in a list """ @@ -47,14 +48,14 @@ def triangle_mesh( def triangle_mesh_perspective( shot_id: str, r: types.Reconstruction, tracks_manager: pymap.TracksManager -) -> Tuple[List[Any], List[Any]]: +) -> Tuple[List[List[float]], List[List[int]]]: shot = r.shots[shot_id] cam = shot.camera dx = float(cam.width) / 2 / max(cam.width, cam.height) dy = float(cam.height) / 2 / max(cam.width, cam.height) pixels = [[-dx, -dy], [-dx, dy], [dx, dy], [dx, -dy]] - vertices = [None for i in range(4)] + vertices = [[0.0, 0.0, 0.0] for i in range(4)] for track_id in tracks_manager.get_shot_observations(shot_id): if track_id in r.points: point = r.points[track_id] @@ -92,7 +93,7 @@ def triangle_mesh_perspective( def back_project_no_distortion( shot: pymap.Shot, pixel: List[float], depth: float -) -> np.ndarray: +) -> NDArray: """ Back-project a pixel of a perspective camera ignoring its radial distortion """ @@ -105,14 +106,14 @@ def back_project_no_distortion( def triangle_mesh_fisheye( shot_id: str, r: types.Reconstruction, tracks_manager: pymap.TracksManager -) -> Tuple[List[Any], List[Any]]: +) -> Tuple[List[List[float]], List[List[int]]]: shot = r.shots[shot_id] bearings = [] vertices = [] # Add boundary vertices - num_circle_points = 20 + num_circle_points: int = 20 for i in range(num_circle_points): a = 2 * np.pi * float(i) / num_circle_points point = 30 * np.array([np.cos(a), np.sin(a), 0]) @@ -143,7 +144,7 @@ def triangle_mesh_fisheye( faces = tri.simplices.tolist() # Remove faces having only boundary vertices - def good_face(face: List[Any]) -> bool: + def good_face(face: List[int]) -> bool: return ( face[0] >= num_circle_points or face[1] >= num_circle_points @@ -157,7 +158,7 @@ def good_face(face: List[Any]) -> bool: def triangle_mesh_spherical( shot_id: str, r: types.Reconstruction, tracks_manager: pymap.TracksManager -) -> Tuple[List[Any], List[Any]]: +) -> Tuple[List[List[float]], List[List[int]]]: shot = r.shots[shot_id] bearings = [] diff --git a/opensfm/multiview.py b/opensfm/multiview.py index cf38d0d6f..c74127812 100644 --- a/opensfm/multiview.py +++ b/opensfm/multiview.py @@ -1,13 +1,14 @@ +# pyre-strict import math -import random from typing import Any, Dict, List, Optional, Tuple import cv2 import numpy as np +from numpy.typing import NDArray from opensfm import pygeometry, pymap, pyrobust, transformations as tf -def nullspace(A: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: +def nullspace(A: NDArray) -> Tuple[NDArray, NDArray]: """Compute the null space of A. Return the smallest singular value and the corresponding vector. @@ -16,29 +17,29 @@ def nullspace(A: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: return s[-1], vh[-1] -def homogeneous(x: np.ndarray) -> np.ndarray: +def homogeneous(x: NDArray) -> NDArray: """Add a column of ones to x.""" s = x.shape[:-1] + (1,) return np.hstack((x, np.ones(s))) -def homogeneous_vec(x: np.ndarray) -> np.ndarray: +def homogeneous_vec(x: NDArray) -> NDArray: """Add a column of zeros to x.""" s = x.shape[:-1] + (1,) return np.hstack((x, np.zeros(s))) -def euclidean(x: np.ndarray) -> np.ndarray: +def euclidean(x: NDArray) -> NDArray: """Divide by last column and drop it.""" return x[..., :-1] / x[..., -1:] -def cross_product_matrix(x: np.ndarray) -> np.ndarray: +def cross_product_matrix(x: NDArray) -> NDArray: """Return the matrix representation of x's cross product""" return np.array([[0, -x[2], x[1]], [x[2], 0, -x[0]], [-x[1], x[0], 0]]) -def P_from_KRt(K: np.ndarray, R: np.ndarray, t: np.ndarray) -> np.ndarray: +def P_from_KRt(K: NDArray, R: NDArray, t: NDArray) -> NDArray: """P = K[R|t].""" P = np.empty((3, 4)) P[:, :3] = np.dot(K, R) @@ -46,7 +47,7 @@ def P_from_KRt(K: np.ndarray, R: np.ndarray, t: np.ndarray) -> np.ndarray: return P -def KRt_from_P(P: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: +def KRt_from_P(P: NDArray) -> Tuple[NDArray, NDArray, NDArray]: """Factorize the camera matrix into K,R,t as P = K[R|t]. >>> K = np.array([[1, 2, 3], @@ -79,7 +80,7 @@ def KRt_from_P(P: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: return K, R, t -def rq(A: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: +def rq(A: NDArray) -> Tuple[NDArray, NDArray]: """Decompose a matrix into a triangular times rotation. (from PCV) @@ -103,7 +104,7 @@ def rq(A: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: return R[:, ::-1], Q[::-1, :] -def vector_angle(u: np.ndarray, v: np.ndarray) -> float: +def vector_angle(u: NDArray, v: NDArray) -> float: """Angle between two vectors. >>> u = [ 0.99500417, -0.33333333, -0.09983342] @@ -119,8 +120,8 @@ def vector_angle(u: np.ndarray, v: np.ndarray) -> float: def decompose_similarity_transform( - T: np.ndarray, -) -> Tuple[float, np.ndarray, np.ndarray]: + T: NDArray, +) -> Tuple[float, NDArray, NDArray]: """Decompose the similarity transform to scale, rotation and translation""" m, n = T.shape[0:2] assert m == n @@ -129,180 +130,9 @@ def decompose_similarity_transform( return s, A / s, b -def ransac_max_iterations( - kernel: Any, inliers: np.ndarray, failure_probability: float -) -> float: - if len(inliers) >= kernel.num_samples(): - return 0 - inlier_ratio = float(len(inliers)) / kernel.num_samples() - n = kernel.required_samples - return math.log(failure_probability) / math.log(1.0 - inlier_ratio**n) - - -TRansacSolution = Tuple[np.ndarray, np.ndarray, float] - - -def ransac(kernel: Any, threshold: float) -> TRansacSolution: - """Robustly fit a model to data. - - >>> x = np.array([1., 2., 3.]) - >>> y = np.array([2., 4., 7.]) - >>> kernel = TestLinearKernel(x, y) - >>> model, inliers, error = ransac(kernel, 0.1) - >>> np.allclose(model, 2.0) - True - >>> inliers - array([0, 1]) - >>> np.allclose(error, 0.1) - True - """ - max_iterations = 1000 - best_error = float("inf") - best_model = None - best_inliers = [] - i = 0 - while i < max_iterations: - try: - samples = kernel.sampling() - except AttributeError: - samples = random.sample( - range(kernel.num_samples()), kernel.required_samples - ) - models = kernel.fit(samples) - for model in models: - errors = kernel.evaluate(model) - inliers = np.flatnonzero(np.fabs(errors) < threshold) - error = np.fabs(errors).clip(0, threshold).sum() - if len(inliers) and error < best_error: - best_error = error - best_model = model - best_inliers = inliers - max_iterations = min( - max_iterations, ransac_max_iterations(kernel, best_inliers, 0.01) - ) - i += 1 - return best_model, best_inliers, best_error - - -class TestLinearKernel: - """A kernel for the model y = a * x. - - >>> x = np.array([1., 2., 3.]) - >>> y = np.array([2., 4., 7.]) - >>> kernel = TestLinearKernel(x, y) - >>> models = kernel.fit([0]) - >>> models - [2.0] - >>> errors = kernel.evaluate(models[0]) - >>> np.allclose(errors, [0., 0., 1.]) - True - """ - - required_samples = 1 - - def __init__(self, x: np.ndarray, y: np.ndarray) -> None: - self.x: np.ndarray = x - self.y: np.ndarray = y - - def num_samples(self) -> int: - return len(self.x) - - def fit(self, samples: np.ndarray) -> List[float]: - x = self.x[samples[0]] - y = self.y[samples[0]] - return [y / x] - - def evaluate(self, model: np.ndarray) -> np.ndarray: - return self.y - model * self.x - - -class PlaneKernel: - """ - A kernel for estimating plane from on-plane points and vectors - """ - - def __init__( - self, points, vectors, verticals, point_threshold=1.0, vector_threshold=5.0 - ) -> None: - self.points = points - self.vectors = vectors - self.verticals = verticals - self.required_samples = 3 - self.point_threshold = point_threshold - self.vector_threshold = vector_threshold - - def num_samples(self) -> int: - return len(self.points) - - def sampling(self) -> Dict[str, Any]: - samples = {} - if len(self.vectors) > 0: - samples["points"] = self.points[ - random.sample(range(len(self.points)), 2), : - ] - samples["vectors"] = [ - self.vectors[i] for i in random.sample(range(len(self.vectors)), 1) - ] - else: - samples["points"] = self.points[ - :, random.sample(range(len(self.points)), 3) - ] - samples["vectors"] = None - return samples - - def fit(self, samples: Dict[str, np.ndarray]) -> List[np.ndarray]: - model = fit_plane(samples["points"], samples["vectors"], self.verticals) - return [model] - - def evaluate(self, model) -> np.ndarray: - # only evaluate on points - normal = model[0:3] - normal_norm = np.linalg.norm(normal) + 1e-10 - point_error = np.abs(model.T.dot(homogeneous(self.points).T)) / normal_norm - vectors = np.array(self.vectors) - vector_norm = np.sum(vectors * vectors, axis=1) - vectors = (vectors.T / vector_norm).T - vector_error = abs( - np.rad2deg(abs(np.arccos(vectors.dot(normal) / normal_norm))) - 90 - ) - vector_error[vector_error < self.vector_threshold] = 0.0 - vector_error[vector_error >= self.vector_threshold] = self.point_threshold + 0.1 - point_error[point_error < self.point_threshold] = 0.0 - point_error[point_error >= self.point_threshold] = self.point_threshold + 0.1 - errors = np.hstack((point_error, vector_error)) - return errors - - -def fit_plane_ransac( - points: np.ndarray, - vectors: np.ndarray, - verticals: np.ndarray, - point_threshold: float = 1.2, - vector_threshold: float = 5.0, -) -> TRansacSolution: - vectors = np.array([v / math.pi * 180.0 for v in vectors]) - kernel = PlaneKernel( - points - points.mean(axis=0), - vectors, - verticals, - point_threshold, - vector_threshold, - ) - p, inliers, error = ransac(kernel, point_threshold) - num_point = points.shape[0] - points_inliers = points[inliers[inliers < num_point], :] - vectors_inliers = np.array( - [vectors[i - num_point] for i in inliers[inliers >= num_point]] - ) - p = fit_plane( - points_inliers - points_inliers.mean(axis=0), vectors_inliers, verticals - ) - return p, inliers, error - - def fit_plane( - points: np.ndarray, vectors: Optional[np.ndarray], verticals: Optional[np.ndarray] -) -> np.ndarray: + points: NDArray, vectors: Optional[NDArray], verticals: Optional[NDArray] +) -> NDArray: """Estimate a plane from on-plane points and vectors. >>> x = [[0,0,0], [1,0,0], [0,1,0]] @@ -345,7 +175,7 @@ def fit_plane( return p -def plane_horizontalling_rotation(p: np.ndarray) -> Optional[np.ndarray]: +def plane_horizontalling_rotation(p: NDArray) -> Optional[NDArray]: """Compute a rotation that brings p to z=0 >>> p = [1.0, 2.0, 3.0] @@ -382,8 +212,8 @@ def plane_horizontalling_rotation(p: np.ndarray) -> Optional[np.ndarray]: def fit_similarity_transform( - p1: np.ndarray, p2: np.ndarray, max_iterations: int = 1000, threshold: float = 1 -) -> Tuple[np.ndarray, np.ndarray]: + p1: NDArray, p2: NDArray, max_iterations: int = 1000, threshold: float = 1 +) -> Tuple[NDArray, NDArray]: """Fit a similarity transform T such as p2 = T . p1 between two points sets p1 and p2""" # TODO (Yubin): adapt to RANSAC class @@ -415,15 +245,18 @@ def fit_similarity_transform( errors = np.sqrt(np.sum((p2h.T - np.dot(best_T, p1h.T)) ** 2, axis=0)) best_inliers = np.argwhere(errors < threshold)[:, 0] + # pyre-fixme[7]: Expected `Tuple[ndarray[typing.Any, typing.Any], + # ndarray[typing.Any, typing.Any]]` but got `Tuple[ndarray[typing.Any, + # typing.Any], Union[List[typing.Any], ndarray[typing.Any, dtype[typing.Any]]]]`. return best_T, best_inliers -def K_from_camera(camera: Dict[str, Any]) -> np.ndarray: +def K_from_camera(camera: Dict[str, Any]) -> NDArray: f = float(camera["focal"]) return np.array([[f, 0.0, 0.0], [0.0, f, 0.0], [0.0, 0.0, 1.0]]) -def focal_from_homography(H: np.ndarray) -> np.ndarray: +def focal_from_homography(H: NDArray) -> NDArray: """Solve for w = H w H^t, with w = diag(a, a, b) >>> K = np.diag([0.8, 0.8, 1]) @@ -449,10 +282,10 @@ def focal_from_homography(H: np.ndarray) -> np.ndarray: return focal -def R_from_homography( - H: np.ndarray, f1: np.ndarray, f2: np.ndarray -) -> Optional[np.ndarray]: +def R_from_homography(H: NDArray, f1: NDArray, f2: NDArray) -> Optional[NDArray]: + # pyre-fixme[6]: For 1st argument expected `Union[_SupportsArray[dtype[typing.Any... K1 = np.diag([f1, f1, 1]) + # pyre-fixme[6]: For 1st argument expected `Union[_SupportsArray[dtype[typing.Any... K2 = np.diag([f2, f2, 1]) K2inv = np.linalg.inv(K2) R = K2inv.dot(H).dot(K1) @@ -460,7 +293,7 @@ def R_from_homography( return R -def project_to_rotation_matrix(A: np.ndarray) -> Optional[np.ndarray]: +def project_to_rotation_matrix(A: NDArray) -> Optional[NDArray]: try: u, d, vt = np.linalg.svd(A) except np.linalg.linalg.LinAlgError: @@ -468,7 +301,7 @@ def project_to_rotation_matrix(A: np.ndarray) -> Optional[np.ndarray]: return u.dot(vt) -def camera_up_vector(rotation_matrix: np.ndarray) -> np.ndarray: +def camera_up_vector(rotation_matrix: NDArray) -> NDArray: """Unit vector pointing to zenit in camera coords. :param rotation: camera pose rotation @@ -476,7 +309,7 @@ def camera_up_vector(rotation_matrix: np.ndarray) -> np.ndarray: return rotation_matrix[:, 2] -def camera_compass_angle(rotation_matrix: np.ndarray) -> float: +def camera_compass_angle(rotation_matrix: NDArray) -> float: """Compass angle of a camera Angle between world's Y axis and camera's Z axis projected @@ -491,7 +324,7 @@ def camera_compass_angle(rotation_matrix: np.ndarray) -> float: def rotation_matrix_from_up_vector_and_compass( up_vector: List[float], compass_angle: float -) -> np.ndarray: +) -> NDArray: """Camera rotation given up_vector and compass. >>> d = [1, 2, 3] @@ -530,8 +363,8 @@ def rotation_matrix_from_up_vector_and_compass( def motion_from_plane_homography( - H: np.ndarray, -) -> Optional[List[Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]]]: + H: NDArray, +) -> Optional[List[Tuple[NDArray, NDArray, NDArray, NDArray]]]: """Compute candidate camera motions from a plane-induced homography. Returns up to 8 motions. @@ -566,44 +399,54 @@ def motion_from_plane_homography( ] solutions = [] - # Case d' > 0 + # Compute solutions for the two cases (1) case d' > 0 and + # (2) case d' < 0 + for x1, x3 in possible_x1_x3: - sin_theta = (d1 - d3) * x1 * x3 / d2 - cos_theta = (d1 * x3**2 + d3 * x1**2) / d2 - Rp = np.array( + # compute sin and cos for d'>0 (theta, _n) and d'<0 (phi, _n) cases + sin_term = x1 * x3 / d2 + sin_theta = (d1 - d3) * sin_term + sin_phi = (d1 + d3) * sin_term + + d1_x3_2 = d1 * x3**2 + d3_x1_2 = d3 * x1**2 + cos_theta = (d3_x1_2 + d1_x3_2) / d2 + cos_phi = (d3_x1_2 - d1_x3_2) / d2 + + # define rotation matrices for both cases + Rp_p = np.array( [[cos_theta, 0, -sin_theta], [0, 1, 0], [sin_theta, 0, cos_theta]] - ) - tp = (d1 - d3) * np.array([x1, 0, -x3]) + ) # case d' > 0 + Rp_n = np.array( + [[cos_phi, 0, sin_phi], [0, -1, 0], [sin_phi, 0, -cos_phi]] + ) # case d' < 0 + + # compute transformations np_ = np.array([x1, 0, x3]) - R = s * np.dot(np.dot(u, Rp), vh) - t = np.dot(u, tp) + + tp_p = (d1 - d3) * np.array([x1, 0, -x3]) # case d' > 0 + tp_n = (d1 + d3) * np_ # case d' < 0 + + R_p = s * np.dot(np.dot(u, Rp_p), vh) # case d' > 0 + R_n = s * np.dot(np.dot(u, Rp_n), vh) # case d' < 0 + t_p = np.dot(u, tp_p) # case d' > 0 + t_n = np.dot(u, tp_n) # case d' < 0 n = -np.dot(vh.T, np_) d = s * d2 - solutions.append((R, t, n, d)) - # Case d' < 0 - for x1, x3 in possible_x1_x3: - sin_phi = (d1 + d3) * x1 * x3 / d2 - cos_phi = (d3 * x1**2 - d1 * x3**2) / d2 - Rp = np.array([[cos_phi, 0, sin_phi], [0, -1, 0], [sin_phi, 0, -cos_phi]]) - tp = (d1 + d3) * np.array([x1, 0, x3]) - np_ = np.array([x1, 0, x3]) - R = s * np.dot(np.dot(u, Rp), vh) - t = np.dot(u, tp) - n = -np.dot(vh.T, np_) - d = -s * d2 - solutions.append((R, t, n, d)) + solutions.append((R_p, t_p, n, d)) # case d' > 0 + solutions.append((R_n, t_n, n, -d)) # case d' < 0 return solutions def absolute_pose_known_rotation_ransac( - bs: np.ndarray, - Xs: np.ndarray, + bs: NDArray, + Xs: NDArray, threshold: float, iterations: int, probability: float, -) -> np.ndarray: +) -> NDArray: params = pyrobust.RobustEstimatorParams() params.iterations = iterations result = pyrobust.ransac_absolute_pose_known_rotation( @@ -616,12 +459,12 @@ def absolute_pose_known_rotation_ransac( def absolute_pose_ransac( - bs: np.ndarray, - Xs: np.ndarray, + bs: NDArray, + Xs: NDArray, threshold: float, iterations: int, probability: float, -) -> np.ndarray: +) -> NDArray: params = pyrobust.RobustEstimatorParams() params.iterations = iterations result = pyrobust.ransac_absolute_pose( @@ -636,12 +479,12 @@ def absolute_pose_ransac( def relative_pose_ransac( - b1: np.ndarray, - b2: np.ndarray, + b1: NDArray, + b2: NDArray, threshold: float, iterations: int, probability: float, -) -> np.ndarray: +) -> NDArray: params = pyrobust.RobustEstimatorParams() params.iterations = iterations result = pyrobust.ransac_relative_pose( @@ -656,12 +499,12 @@ def relative_pose_ransac( def relative_pose_ransac_rotation_only( - b1: np.ndarray, - b2: np.ndarray, + b1: NDArray, + b2: NDArray, threshold: float, iterations: int, probability: float, -) -> np.ndarray: +) -> NDArray: params = pyrobust.RobustEstimatorParams() params.iterations = iterations result = pyrobust.ransac_relative_rotation( @@ -671,8 +514,8 @@ def relative_pose_ransac_rotation_only( def relative_pose_optimize_nonlinear( - b1: np.ndarray, b2: np.ndarray, t: np.ndarray, R: np.ndarray, iterations: int -) -> np.ndarray: + b1: NDArray, b2: NDArray, t: NDArray, R: NDArray, iterations: int +) -> NDArray: Rt = np.zeros((3, 4)) Rt[:3, :3] = R.T Rt[:, 3] = -R.T.dot(t) @@ -689,7 +532,9 @@ def triangulate_gcp( shots: Dict[str, pymap.Shot], reproj_threshold: float = 0.02, min_ray_angle_degrees: float = 1.0, -) -> Optional[np.ndarray]: + min_depth: float = 0.001, + iterations: int = 10, +) -> Optional[NDArray]: """Compute the reconstructed position of a GCP from observations.""" os, bs, ids = [], [], [] @@ -706,13 +551,17 @@ def triangulate_gcp( if len(os) >= 2: thresholds = len(os) * [reproj_threshold] + os = np.asarray(os) + bs = np.asarray(bs) valid_triangulation, X = pygeometry.triangulate_bearings_midpoint( - np.asarray(os), - np.asarray(bs), + os, + bs, thresholds, np.radians(min_ray_angle_degrees), - np.radians(180.0 - min_ray_angle_degrees), + min_depth, ) + if valid_triangulation: + X = pygeometry.point_refinement(os, bs, X, iterations) return X return None diff --git a/opensfm/pairs_selection.py b/opensfm/pairs_selection.py index 28c7aa8f0..d335c31fe 100644 --- a/opensfm/pairs_selection.py +++ b/opensfm/pairs_selection.py @@ -1,13 +1,15 @@ +# pyre-strict import copy import logging import math from collections import defaultdict from itertools import combinations -from typing import Optional, Tuple, List, Set, Dict, Iterable, Any +from typing import Any, Dict, Iterable, List, Optional, Set, Tuple import numpy as np import scipy.spatial as spatial -from opensfm import bow, context, feature_loader, vlad, geo, geometry +from numpy.typing import NDArray +from opensfm import bow, context, feature_loader, geo, geometry, vlad from opensfm.dataset_base import DataSetBase logger: logging.Logger = logging.getLogger(__name__) @@ -31,20 +33,21 @@ def sorted_pair(im1: str, im2: str) -> Tuple[str, str]: def get_gps_point( exif: Dict[str, Any], reference: geo.TopocentricConverter -) -> Tuple[np.ndarray, np.ndarray]: +) -> Tuple[NDArray, NDArray]: """Return GPS-based representative point. Direction is returned as Z oriented (vertical assumption)""" gps = exif["gps"] altitude = 0 direction = np.array([0, 0, 1]) return ( - reference.to_topocentric(gps["latitude"], gps["longitude"], altitude), + np.array(reference.to_topocentric( + gps["latitude"], gps["longitude"], altitude)), direction, ) DEFAULT_Z = 1.0 MAXIMUM_Z = 8000 -SAMPLE_Z = 100 +SAMPLE_Z = 25 def sign(x: float) -> float: @@ -54,7 +57,7 @@ def sign(x: float) -> float: def get_gps_opk_point( exif: Dict[str, Any], reference: geo.TopocentricConverter -) -> Tuple[np.ndarray, np.ndarray]: +) -> Tuple[NDArray, NDArray]: """Return GPS-based representative point.""" opk = exif["opk"] @@ -72,7 +75,7 @@ def get_gps_opk_point( def find_best_altitude( - origin: Dict[str, np.ndarray], directions: Dict[str, np.ndarray] + origin: Dict[str, NDArray], directions: Dict[str, NDArray] ) -> float: """Find the altitude that minimize X/Y bounding box. Domain is [0, MAXIMUM_Z]. 'origin' contains per-image positions in worl coordinates @@ -83,28 +86,47 @@ def find_best_altitude( directions_base = np.array([p for p in directions.values()]) origin_base = np.array([p for p in origin.values()]) - samples_x, samples_y = [], [] - for current_z in range(1, MAXIMUM_Z, SAMPLE_Z): + samples = [] + for i, current_z in enumerate(range(1, MAXIMUM_Z, SAMPLE_Z)): scaled = origin_base + directions_base / DEFAULT_Z * current_z current_size = (np.max(scaled[:, 0]) - np.min(scaled[:, 0])) ** 2 + ( np.max(scaled[:, 1]) - np.min(scaled[:, 1]) ) ** 2 - samples_x.append(current_z) - samples_y.append(current_size) + samples.append((current_z, current_size, i)) - coeffs = np.polyfit(samples_x, samples_y, 2) - extrema = -coeffs[1] / (2 * coeffs[0]) - if extrema < 0: + # Find the minima manually + min_z, min_size, index = min(samples, key=lambda t: t[1]) + logger.info( + f"Minimum found at sampling altitude {min_z} with size {min_size}") + + if index == 0 or index == len(samples) - 1: logger.info( - f"Altitude is negative ({extrema}) : viewing directions are probably divergent. Using default altitude of {DEFAULT_Z}" + f"Altitude extrema is at the boundary of search space. Using default altitude of {DEFAULT_Z}" ) - extrema = DEFAULT_Z - return extrema + return DEFAULT_Z + + # Refine with polynomial fitting + before = max(0, index - 1) + after = min(len(samples) - 1, index + 1) + coeffs = np.polyfit( + [samples[i][0] for i in range(before, after + 1)], + [samples[i][1] for i in range(before, after + 1)], + 2, + ) + real_minimum = -coeffs[1] / (2 * coeffs[0]) + logger.info(f"Refined altitude extrema at {real_minimum}") + + if real_minimum < 0: + logger.info( + f"Altitude is negative ({real_minimum}) : viewing directions are probably divergent. Using default altitude of {DEFAULT_Z}" + ) + real_minimum = DEFAULT_Z + return real_minimum def get_representative_points( images: List[str], exifs: Dict[str, Any], reference: geo.TopocentricConverter -) -> Dict[str, np.ndarray]: +) -> Dict[str, NDArray]: """Return a topocentric point for each image, that is suited to run distance-based pair selection.""" origin = {} directions = {} @@ -135,12 +157,14 @@ def get_representative_points( raise RuntimeError( f"GPS / OPK / YPR {has_gps, has_opk, has_ypr} tag combination unsupported" ) - origin[image], directions[image] = map_method[method_id](exif, reference) + origin[image], directions[image] = map_method[method_id]( + exif, reference) if had_orientation: altitude = find_best_altitude(origin, directions) logger.info(f"Altitude for orientation based matching {altitude}") - directions_scaled = {k: v / DEFAULT_Z * altitude for k, v in directions.items()} + directions_scaled = {k: v / DEFAULT_Z * + altitude for k, v in directions.items()} points = {k: origin[k] + directions_scaled[k] for k in images} else: points = origin @@ -177,9 +201,11 @@ def match_candidates_by_distance( # we don't want to loose some images because of missing GPS : # either ALL of them or NONE of them are used for getting pairs - difference = abs(len(representative_points) - len(set(images_cand + images_ref))) + difference = abs(len(representative_points) - + len(set(images_cand + images_ref))) if difference > 0: - logger.warning(f"Couldn't fetch {difference} images. Returning NO pairs.") + logger.warning( + f"Couldn't fetch {difference} images. Returning NO pairs.") return set() points = np.zeros((len(representative_points), 3)) @@ -197,7 +223,7 @@ def match_candidates_by_distance( point, k=nn, distance_upper_bound=max_distance ) - if type(neighbors) == int: # special case with only one NN + if isinstance(neighbors, int): # special case with only one NN neighbors = [neighbors] for j in neighbors: @@ -209,7 +235,7 @@ def match_candidates_by_distance( return pairs -def norm_2d(vec: np.ndarray) -> float: +def norm_2d(vec: NDArray) -> float: """Return the 2D norm of a vector.""" return math.sqrt(vec[0] ** 2 + vec[1] ** 2) @@ -225,9 +251,9 @@ def match_candidates_by_graph( if len(images_cand) < 4 or rounds < 1: return set() - images_cand_set = set(images_cand) - images_ref_set = set(images_ref) - images = list(images_cand_set | images_ref_set) + images_cand_set: Set[str] = set(images_cand) + images_ref_set: Set[str] = set(images_ref) + images: List[str] = list(images_cand_set | images_ref_set) representative_points = get_representative_points(images, exifs, reference) @@ -235,7 +261,9 @@ def match_candidates_by_graph( for i, point in enumerate(representative_points.values()): points[i] = point[0:2] - def produce_edges(triangles): + def produce_edges( + triangles: List[Tuple[int, int, int]], + ) -> Iterable[Tuple[Tuple[str, str], Tuple[int, int]]]: for triangle in triangles: for vertex1, vertex2 in combinations(triangle, 2): image1, image2 = images[vertex1], images[vertex2] @@ -255,7 +283,9 @@ def produce_edges(triangles): except spatial.QhullError: # Initial simplex is flat # Scale the input to fit the unit cube ("QbB") - triangles = spatial.Delaunay(points, qhull_options="Qbb Qc Qz Q12 QbB").simplices + triangles = spatial.Delaunay( + points, qhull_options="Qbb Qc Qz Q12 QbB" + ).simplices for (image1, image2), (vertex1, vertex2) in produce_edges(triangles): pairs.add((image1, image2)) @@ -267,7 +297,8 @@ def produce_edges(triangles): # will only produce one diagonal edge, so by jittering it, we get more # chances of getting such diagonal edges and having more diversity for _ in range(rounds): - points_current = copy.copy(points) + np.random.rand(*points.shape) * scale + points_current = copy.copy( + points) + np.random.rand(*points.shape) * scale triangles = spatial.Delaunay(points_current).simplices for (image1, image2), _ in produce_edges(triangles): pairs.add((image1, image2)) @@ -350,7 +381,7 @@ def match_candidates_with_vlad( max_gps_distance: float, max_gps_neighbors: int, enforce_other_cameras: bool, - histograms: Dict[str, np.ndarray], + histograms: Dict[str, NDArray], ) -> Dict[Tuple[str, str], float]: """Find candidate matching pairs using VLAD-based distance. If max_gps_distance > 0, then we use first restrain a set of @@ -392,7 +423,7 @@ def compute_vlad_affinity( reference: geo.TopocentricConverter, max_gps_distance: float, max_gps_neighbors: int, - histograms: Dict[str, np.ndarray], + histograms: Dict[str, NDArray], ) -> List[Tuple[str, List[float], List[str]]]: """Compute affinity scores between references and candidates images using VLAD-based distance. @@ -428,7 +459,7 @@ def preempt_candidates( reference: geo.TopocentricConverter, max_gps_neighbors: int, max_gps_distance: float, -) -> Tuple[Dict[str, list], Set[str]]: +) -> Tuple[Dict[str, List[str]], Set[str]]: """Preempt candidates using GPS to reduce set of images from which to load data to save RAM. """ @@ -471,7 +502,10 @@ def construct_pairs( order = np.argsort(distances) if enforce_other_cameras: pairs.update( - pairs_from_neighbors(im, exifs, distances, order, other, max_neighbors) + # pyre-fixme[6]: For 4th argument expected `List[int]` but got + # `ndarray[typing.Any, dtype[typing.Any]]`. + pairs_from_neighbors(im, exifs, distances, + order, other, max_neighbors) ) else: for i in order[:max_neighbors]: @@ -481,9 +515,9 @@ def construct_pairs( def create_parallel_matching_args( data: DataSetBase, - preempted_cand: Dict[str, list], - histograms: Dict[str, np.ndarray], -) -> Tuple[List[Tuple[str, list, Dict[str, np.ndarray]]], int, int]: + preempted_cand: Dict[str, List[str]], + histograms: Dict[str, NDArray], +) -> Tuple[List[Tuple[str, List[str], Dict[str, NDArray]]], int, int]: """Create arguments to matching function""" args = [(im, cands, histograms) for im, cands in preempted_cand.items()] @@ -497,7 +531,7 @@ def create_parallel_matching_args( def match_bow_unwrap_args( - args: Tuple[str, Iterable[str], Dict[str, np.ndarray]] + args: Tuple[str, Iterable[str], Dict[str, NDArray]], ) -> Tuple[str, List[float], List[str]]: """Wrapper for parallel processing of BoW""" image, other_images, histograms = args @@ -505,7 +539,7 @@ def match_bow_unwrap_args( def match_vlad_unwrap_args( - args: Tuple[str, Iterable[str], Dict[str, np.ndarray]] + args: Tuple[str, Iterable[str], Dict[str, NDArray]], ) -> Tuple[str, List[float], List[str]]: """Wrapper for parallel processing of VLAD""" image, other_images, histograms = args @@ -536,7 +570,7 @@ def match_candidates_by_time( time = exifs[image_ref]["capture_time"] distances, neighbors = tree.query([time], k=nn) - if type(neighbors) == int: # special case with only one NN + if isinstance(neighbors, int): # special case with only one NN neighbors = [neighbors] for j in neighbors: @@ -603,7 +637,7 @@ def match_candidates_from_metadata( if not all(map(has_gps_info, exifs.values())): if gps_neighbors != 0: - logger.warn( + logger.warning( "Not all images have GPS info. " "Disabling matching_gps_neighbors." ) gps_neighbors = 0 @@ -629,7 +663,8 @@ def match_candidates_from_metadata( o = set() b = set() v = set() - pairs = {sorted_pair(i, j) for i in images_ref for j in images_cand if i != j} + pairs = {sorted_pair(i, j) + for i in images_ref for j in images_cand if i != j} else: d = match_candidates_by_distance( images_ref, images_cand, exifs, reference, gps_neighbors, max_distance @@ -637,7 +672,8 @@ def match_candidates_from_metadata( g = match_candidates_by_graph( images_ref, images_cand, exifs, reference, graph_rounds ) - t = match_candidates_by_time(images_ref, images_cand, exifs, time_neighbors) + t = match_candidates_by_time( + images_ref, images_cand, exifs, time_neighbors) o = match_candidates_by_order(images_ref, images_cand, order_neighbors) b = match_candidates_with_bow( data, @@ -678,7 +714,7 @@ def match_candidates_from_metadata( def bow_distances( - image: str, other_images: Iterable[str], histograms: Dict[str, np.ndarray] + image: str, other_images: Iterable[str], histograms: Dict[str, NDArray] ) -> Tuple[str, List[float], List[str]]: """Compute BoW-based distance (L1 on histogram of words) between an image and other images. @@ -697,14 +733,15 @@ def bow_distances( return image, distances, other -def load_histograms(data: DataSetBase, images: Iterable[str]) -> Dict[str, np.ndarray]: +def load_histograms(data: DataSetBase, images: Iterable[str]) -> Dict[str, NDArray]: """Load BoW histograms of given images""" min_num_feature = 8 histograms = {} bows = bow.load_bows(data.config) for im in images: - filtered_words = feature_loader.instance.load_words(data, im, masked=True) + filtered_words = feature_loader.instance.load_words( + data, im, masked=True) if filtered_words is None: logger.error("No words in image {}".format(im)) continue @@ -721,8 +758,8 @@ def load_histograms(data: DataSetBase, images: Iterable[str]) -> Dict[str, np.nd def vlad_histogram_unwrap_args( - args: Tuple[DataSetBase, str] -) -> Optional[Tuple[str, np.ndarray]]: + args: Tuple[DataSetBase, str], +) -> Optional[Tuple[str, NDArray]]: """Helper function for multithreaded VLAD computation. Returns the image and its descriptor. @@ -736,7 +773,7 @@ def vlad_histogram_unwrap_args( return None -def vlad_histograms(images: Iterable[str], data: DataSetBase) -> Dict[str, np.ndarray]: +def vlad_histograms(images: Iterable[str], data: DataSetBase) -> Dict[str, NDArray]: """Construct VLAD histograms from the image features. Returns a dictionary of VLAD vectors for the images. diff --git a/opensfm/reconstruction.py b/opensfm/reconstruction.py index dc10c981f..ee5f219ad 100644 --- a/opensfm/reconstruction.py +++ b/opensfm/reconstruction.py @@ -1,3 +1,4 @@ +# pyre-strict """Incremental reconstruction pipeline""" import datetime @@ -15,6 +16,7 @@ import cv2 import numpy as np +from numpy.typing import NDArray from opensfm import ( log, matching, @@ -23,13 +25,13 @@ pygeometry, pymap, pysfm, + reconstruction_helpers as helpers, rig, tracking, types, - reconstruction_helpers as helpers, ) from opensfm.align import align_reconstruction, apply_similarity -from opensfm.context import current_memory_usage, parallel_map +from opensfm import context from opensfm.dataset_base import DataSetBase @@ -51,11 +53,30 @@ def _get_camera_from_bundle( camera.set_parameter_value(k, v) +def log_bundle_stats(bundle_type: str, bundle_report: Dict[str, Any]) -> None: + times = bundle_report["wall_times"] + time_secs = times["run"] + times["setup"] + \ + times["teardown"] + times["triangulate"] + num_images, num_points, num_reprojections = ( + bundle_report["num_images"], + bundle_report["num_points"], + bundle_report["num_reprojections"], + ) + + msg = f"Ran {bundle_type} bundle in {time_secs:.2f} secs." + if num_points > 0: + msg += f"with {num_images}/{num_points}/{num_reprojections} ({num_reprojections / num_points:.2f}) " + msg += "shots/points/proj. (avg. length)" + + logger.info(msg) + + def bundle( reconstruction: types.Reconstruction, camera_priors: Dict[str, pygeometry.Camera], rig_camera_priors: Dict[str, pymap.RigCamera], gcp: Optional[List[pymap.GroundControlPoint]], + grid_size: int, config: Dict[str, Any], ) -> Dict[str, Any]: """Bundle adjust a reconstruction.""" @@ -64,9 +85,10 @@ def bundle( dict(camera_priors), dict(rig_camera_priors), gcp if gcp is not None else [], + grid_size, config, ) - + log_bundle_stats("GLOBAL", report) logger.debug(report["brief_report"]) return report @@ -94,6 +116,7 @@ def bundle_local( camera_priors: Dict[str, pygeometry.Camera], rig_camera_priors: Dict[str, pymap.RigCamera], gcp: Optional[List[pymap.GroundControlPoint]], + grid_size: int, central_shot_id: str, config: Dict[str, Any], ) -> Tuple[Dict[str, Any], List[int]]: @@ -104,76 +127,14 @@ def bundle_local( dict(rig_camera_priors), gcp if gcp is not None else [], central_shot_id, + grid_size, config, ) + log_bundle_stats("LOCAL", report) logger.debug(report["brief_report"]) return pt_ids, report -def shot_neighborhood( - reconstruction: types.Reconstruction, - central_shot_id: str, - radius: int, - min_common_points: int, - max_interior_size: int, -) -> Tuple[Set[str], Set[str]]: - """Reconstructed shots near a given shot. - - Returns: - a tuple with interior and boundary: - - interior: the list of shots at distance smaller than radius - - boundary: shots sharing at least on point with the interior - - Central shot is at distance 0. Shots at distance n + 1 share at least - min_common_points points with shots at distance n. - """ - max_boundary_size = 1000000 - interior = {central_shot_id} - for _distance in range(1, radius): - remaining = max_interior_size - len(interior) - if remaining <= 0: - break - neighbors = direct_shot_neighbors( - reconstruction, interior, min_common_points, remaining - ) - interior.update(neighbors) - boundary = direct_shot_neighbors(reconstruction, interior, 1, max_boundary_size) - return interior, boundary - - -def direct_shot_neighbors( - reconstruction: types.Reconstruction, - shot_ids: Set[str], - min_common_points: int, - max_neighbors: int, -) -> Set[str]: - """Reconstructed shots sharing reconstructed points with a shot set.""" - points = set() - for shot_id in shot_ids: - shot = reconstruction.shots[shot_id] - valid_landmarks = shot.get_valid_landmarks() - for track in valid_landmarks: - if track.id in reconstruction.points: - points.add(track) - - candidate_shots = set(reconstruction.shots) - set(shot_ids) - common_points = defaultdict(int) - for track in points: - neighbors = track.get_observations() - for neighbor in neighbors: - if neighbor.id in candidate_shots: - common_points[neighbor] += 1 - - pairs = sorted(common_points.items(), key=lambda x: -x[1]) - neighbors = set() - for neighbor, num_points in pairs[:max_neighbors]: - if num_points >= min_common_points: - neighbors.add(neighbor.id) - else: - break - return neighbors - - def pairwise_reconstructability(common_tracks: int, rotation_inliers: int) -> float: """Likeliness of an image pair giving a good initial reconstruction.""" outliers = common_tracks - rotation_inliers @@ -184,48 +145,24 @@ def pairwise_reconstructability(common_tracks: int, rotation_inliers: int) -> fl return 0 -TPairArguments = Tuple[ - str, str, np.ndarray, np.ndarray, pygeometry.Camera, pygeometry.Camera, float -] - - -def compute_image_pairs( - track_dict: Dict[Tuple[str, str], tracking.TPairTracks], data: DataSetBase -) -> List[Tuple[str, str]]: - """All matched image pairs sorted by reconstructability.""" - cameras = data.load_camera_models() - args = _pair_reconstructability_arguments(track_dict, cameras, data) - processes = data.config["processes"] - result = parallel_map(_compute_pair_reconstructability, args, processes) - result = list(result) - pairs = [(im1, im2) for im1, im2, r in result if r > 0] - score = [r for im1, im2, r in result if r > 0] - order = np.argsort(-np.array(score)) - return [pairs[o] for o in order] - - -def _pair_reconstructability_arguments( - track_dict: Dict[Tuple[str, str], tracking.TPairTracks], - cameras: Dict[str, pygeometry.Camera], - data: DataSetBase, -) -> List[TPairArguments]: - threshold = 4 * data.config["five_point_algo_threshold"] - args = [] - for (im1, im2), (_, p1, p2) in track_dict.items(): - camera1 = cameras[data.load_exif(im1)["camera"]] - camera2 = cameras[data.load_exif(im2)["camera"]] - args.append((im1, im2, p1, p2, camera1, camera2, threshold)) - return args - - -def _compute_pair_reconstructability(args: TPairArguments) -> Tuple[str, str, float]: - log.setup() - im1, im2, p1, p2, camera1, camera2, threshold = args +def calculate_pair_reconstructability( + tracks_manager: pymap.TracksManager, + im1: str, + im2: str, + camera1: pygeometry.Camera, + camera2: pygeometry.Camera, + threshold: float, +) -> float: + p1, p2 = _get_common_feature_arrays(tracks_manager, im1, im2) R, inliers = two_view_reconstruction_rotation_only( p1, p2, camera1, camera2, threshold ) - r = pairwise_reconstructability(len(p1), len(inliers)) - return (im1, im2, r) + return pairwise_reconstructability(len(p1), len(inliers)) + + +TPairArguments = Tuple[ + str, str, NDArray, NDArray, pygeometry.Camera, pygeometry.Camera, float +] def add_shot( @@ -250,7 +187,8 @@ def add_shot( added_shots = {shot_id} else: instance_id, _, instance_shots = rig_assignments[shot_id] - rig_instance = reconstruction.add_rig_instance(pymap.RigInstance(instance_id)) + rig_instance = reconstruction.add_rig_instance( + pymap.RigInstance(instance_id)) for shot in instance_shots: _, rig_camera_id, _ = rig_assignments[shot] @@ -270,18 +208,20 @@ def add_shot( def _two_view_reconstruction_inliers( - b1: np.ndarray, b2: np.ndarray, R: np.ndarray, t: np.ndarray, threshold: float + b1: NDArray, b2: NDArray, R: NDArray, t: NDArray, threshold: float ) -> List[int]: """Returns indices of matches that can be triangulated.""" ok = matching.compute_inliers_bearings(b1, b2, R, t, threshold) + # pyre-fixme[7]: Expected `List[int]` but got `ndarray[typing.Any, + # dtype[typing.Any]]`. return np.nonzero(ok)[0] def two_view_reconstruction_plane_based( - b1: np.ndarray, - b2: np.ndarray, + b1: NDArray, + b2: NDArray, threshold: float, -) -> Tuple[Optional[np.ndarray], Optional[np.ndarray], List[int]]: +) -> Tuple[Optional[NDArray], Optional[NDArray], List[int]]: """Reconstruct two views from point correspondences lying on a plane. Args: @@ -305,7 +245,8 @@ def two_view_reconstruction_plane_based( motion_inliers = [] for R, t, _, _ in motions: - inliers = _two_view_reconstruction_inliers(b1, b2, R.T, -R.T.dot(t), threshold) + inliers = _two_view_reconstruction_inliers( + b1, b2, R.T, -R.T.dot(t), threshold) motion_inliers.append(inliers) best = np.argmax(list(map(len, motion_inliers))) @@ -315,14 +256,14 @@ def two_view_reconstruction_plane_based( def two_view_reconstruction_and_refinement( - b1: np.ndarray, - b2: np.ndarray, - R: np.ndarray, - t: np.ndarray, + b1: NDArray, + b2: NDArray, + R: NDArray, + t: NDArray, threshold: float, iterations: int, transposed: bool, -) -> Tuple[np.ndarray, np.ndarray, List[int]]: +) -> Tuple[NDArray, NDArray, List[int]]: """Reconstruct two views using provided rotation and translation. Args: @@ -342,7 +283,8 @@ def two_view_reconstruction_and_refinement( t_curr = t.copy() R_curr = R.copy() - inliers = _two_view_reconstruction_inliers(b1, b2, R_curr, t_curr, threshold) + inliers = _two_view_reconstruction_inliers( + b1, b2, R_curr, t_curr, threshold) if len(inliers) > 5: T = multiview.relative_pose_optimize_nonlinear( @@ -350,26 +292,29 @@ def two_view_reconstruction_and_refinement( ) R_curr = T[:, :3] t_curr = T[:, 3] - inliers = _two_view_reconstruction_inliers(b1, b2, R_curr, t_curr, threshold) + inliers = _two_view_reconstruction_inliers( + b1, b2, R_curr, t_curr, threshold) return cv2.Rodrigues(R_curr.T)[0].ravel(), -R_curr.T.dot(t_curr), inliers def _two_view_rotation_inliers( - b1: np.ndarray, b2: np.ndarray, R: np.ndarray, threshold: float + b1: NDArray, b2: NDArray, R: NDArray, threshold: float ) -> List[int]: br2 = R.dot(b2.T).T ok = np.linalg.norm(br2 - b1, axis=1) < threshold + # pyre-fixme[7]: Expected `List[int]` but got `ndarray[typing.Any, + # dtype[typing.Any]]`. return np.nonzero(ok)[0] def two_view_reconstruction_rotation_only( - p1: np.ndarray, - p2: np.ndarray, + p1: NDArray, + p2: NDArray, camera1: pygeometry.Camera, camera2: pygeometry.Camera, threshold: float, -) -> Tuple[np.ndarray, List[int]]: +) -> Tuple[NDArray, List[int]]: """Find rotation between two views from point correspondences. Args: @@ -383,22 +328,23 @@ def two_view_reconstruction_rotation_only( b1 = camera1.pixel_bearing_many(p1) b2 = camera2.pixel_bearing_many(p2) - R = multiview.relative_pose_ransac_rotation_only(b1, b2, threshold, 1000, 0.999) + R = multiview.relative_pose_ransac_rotation_only( + b1, b2, threshold, 1000, 0.999) inliers = _two_view_rotation_inliers(b1, b2, R, threshold) return cv2.Rodrigues(R.T)[0].ravel(), inliers def two_view_reconstruction_5pt( - b1: np.ndarray, - b2: np.ndarray, - R: np.ndarray, - t: np.ndarray, + b1: NDArray, + b2: NDArray, + R: NDArray, + t: NDArray, threshold: float, iterations: int, check_reversal: bool = False, reversal_ratio: float = 1.0, -) -> Tuple[Optional[np.ndarray], Optional[np.ndarray], List[int]]: +) -> Tuple[Optional[NDArray], Optional[NDArray], List[int]]: """Run 5-point reconstruction and refinement, given computed relative rotation and translation. Optionally, the method will perform reconstruction and refinement for both given and transposed @@ -462,16 +408,16 @@ def two_view_reconstruction_5pt( return R_5p, t_5p, inliers_5p -def two_view_reconstruction_general( - p1: np.ndarray, - p2: np.ndarray, +def two_view_reconstruction_general( # pyre-ignore[3]: pyre is not happy with the Dict[str, Any] + p1: NDArray, + p2: NDArray, camera1: pygeometry.Camera, camera2: pygeometry.Camera, threshold: float, iterations: int, check_reversal: bool = False, reversal_ratio: float = 1.0, -) -> Tuple[Optional[np.ndarray], Optional[np.ndarray], List[int], Dict[str, Any]]: +) -> Tuple[Optional[NDArray], Optional[NDArray], List[int], Dict[str, Any]]: """Reconstruct two views from point correspondences. These will try different reconstruction methods and return the @@ -540,8 +486,8 @@ def reconstruction_from_relative_pose( tracks_manager: pymap.TracksManager, im1: str, im2: str, - R: np.ndarray, - t: np.ndarray, + R: NDArray, + t: NDArray, ) -> Tuple[Optional[types.Reconstruction], Dict[str, Any]]: """Create a reconstruction from 'im1' and 'im2' using the provided rotation 'R' and translation 't'.""" report = {} @@ -550,14 +496,16 @@ def reconstruction_from_relative_pose( camera_priors = data.load_camera_models() rig_camera_priors = data.load_rig_cameras() - rig_assignments = rig.rig_assignments_per_image(data.load_rig_assignments()) + rig_assignments = rig.rig_assignments_per_image( + data.load_rig_assignments()) reconstruction = types.Reconstruction() reconstruction.reference = data.load_reference() reconstruction.cameras = camera_priors reconstruction.rig_cameras = rig_camera_priors - new_shots = add_shot(data, reconstruction, rig_assignments, im1, pygeometry.Pose()) + new_shots = add_shot(data, reconstruction, + rig_assignments, im1, pygeometry.Pose()) if im2 not in new_shots: new_shots |= add_shot( @@ -565,7 +513,8 @@ def reconstruction_from_relative_pose( ) align_reconstruction(reconstruction, [], data.config) - triangulate_shot_features(tracks_manager, reconstruction, new_shots, data.config) + triangulate_shot_features( + tracks_manager, reconstruction, new_shots, data.config) logger.info("Triangulated: {}".format(len(reconstruction.points))) report["triangulated_points"] = len(reconstruction.points) @@ -581,9 +530,9 @@ def reconstruction_from_relative_pose( retriangulate(tracks_manager, reconstruction, data.config) if len(reconstruction.points) < min_inliers: - report[ - "decision" - ] = "Re-triangulation after initial motion did not generate enough points" + report["decision"] = ( + "Re-triangulation after initial motion did not generate enough points" + ) logger.info(report["decision"]) return None, report @@ -592,7 +541,7 @@ def reconstruction_from_relative_pose( ) report["decision"] = "Success" - report["memory_usage"] = current_memory_usage() + report["memory_usage"] = context.current_memory_usage() return reconstruction, report @@ -601,8 +550,8 @@ def bootstrap_reconstruction( tracks_manager: pymap.TracksManager, im1: str, im2: str, - p1: np.ndarray, - p2: np.ndarray, + p1: NDArray, + p2: NDArray, ) -> Tuple[Optional[types.Reconstruction], Dict[str, Any]]: """Start a reconstruction using two shots.""" logger.info("Starting reconstruction with {} and {}".format(im1, im2)) @@ -672,7 +621,8 @@ def resect( True on success. """ - rig_assignments = rig.rig_assignments_per_image(data.load_rig_assignments()) + rig_assignments = rig.rig_assignments_per_image( + data.load_rig_assignments()) camera = reconstruction.cameras[data.load_exif(shot_id)["camera"]] bs, Xs, ids = [], [], [] @@ -685,7 +635,7 @@ def resect( bs = np.array(bs) Xs = np.array(Xs) if len(bs) < 5: - return False, set(), {"num_common_points": len(bs)} + return False, set(), set(), {"num_common_points": len(bs)} T = multiview.absolute_pose_ransac(bs, Xs, threshold, 1000, 0.999) @@ -698,7 +648,8 @@ def resect( inliers = np.linalg.norm(reprojected_bs - bs, axis=1) < threshold ninliers = int(sum(inliers)) - logger.info("{} resection inliers: {} / {}".format(shot_id, ninliers, len(bs))) + logger.info( + "{} resection inliers: {} / {}".format(shot_id, ninliers, len(bs))) report: Dict[str, Any] = { "num_common_points": len(bs), "num_inliers": ninliers, @@ -709,22 +660,25 @@ def resect( assert shot_id not in reconstruction.shots new_shots = add_shot( - data, reconstruction, rig_assignments, shot_id, pygeometry.Pose(R, t) + data, reconstruction, rig_assignments, shot_id, pygeometry.Pose( + R, t) ) if shot_id in rig_assignments: triangulate_shot_features( tracks_manager, reconstruction, new_shots, data.config ) + tracks = set() for i, succeed in enumerate(inliers): if succeed: add_observation_to_reconstruction( tracks_manager, reconstruction, shot_id, ids[i] ) + tracks.add(ids[i]) report["shots"] = list(new_shots) - return True, new_shots, report + return True, new_shots, tracks, report else: - return False, set(), report + return False, set(), set(), report def corresponding_tracks( @@ -770,7 +724,7 @@ def resect_reconstruction( tracks_manager2: pymap.TracksManager, threshold: float, min_inliers: int, -) -> Tuple[bool, np.ndarray, List[Tuple[str, str]]]: +) -> Tuple[bool, NDArray, List[Tuple[str, str]]]: """Compute a similarity transform `similarity` such as : reconstruction2 = T . reconstruction1 @@ -813,7 +767,7 @@ def get_observations(self, track_id: str) -> Dict[str, pymap.Observation]: pass @abstractmethod - def store_track_coordinates(self, track_id: str, coordinates: np.ndarray) -> None: + def store_track_coordinates(self, track_id: str, coordinates: NDArray) -> None: """Stores coordinates of triangulated track.""" pass @@ -847,7 +801,7 @@ def get_observations(self, track_id: str) -> Dict[str, pymap.Observation]: if k in self.reconstruction.shots } - def store_track_coordinates(self, track_id: str, coordinates: np.ndarray) -> None: + def store_track_coordinates(self, track_id: str, coordinates: NDArray) -> None: """Stores coordinates of triangulated track.""" self.reconstruction.create_point(track_id, coordinates) @@ -870,9 +824,9 @@ class TrackTriangulator: tracks_handler: TrackHandlerBase # caches - origins: Dict[str, np.ndarray] = {} - rotation_inverses: Dict[str, np.ndarray] = {} - Rts: Dict[str, np.ndarray] = {} + origins: Dict[str, NDArray] = {} + rotation_inverses: Dict[str, NDArray] = {} + Rts: Dict[str, NDArray] = {} def __init__( self, reconstruction: types.Reconstruction, tracks_handler: TrackHandlerBase @@ -880,17 +834,18 @@ def __init__( """Build a triangulator for a specific reconstruction.""" self.reconstruction = reconstruction self.tracks_handler = tracks_handler - self.origins = {} - self.rotation_inverses = {} - self.Rts = {} + self.origins: Dict[str, NDArray] = {} + self.rotation_inverses: Dict[str, NDArray] = {} + self.Rts: Dict[str, NDArray] = {} def triangulate_robust( self, track: str, reproj_threshold: float, min_ray_angle_degrees: float, + min_depth: float, iterations: int, - ) -> None: + ) -> bool: """Triangulate track in a RANSAC way and add point to reconstruction.""" os, bs, ids = [], [], [] for shot_id, obs in self.tracks_handler.get_observations(track).items(): @@ -902,7 +857,7 @@ def triangulate_robust( ids.append(shot_id) if len(ids) < 2: - return + return False os = np.array(os) bs = np.array(bs) @@ -914,6 +869,7 @@ def triangulate_robust( all_combinations = list(combinations(range(len(ids)), 2)) thresholds = len(os) * [reproj_threshold] + min_ray_angle_radians = np.radians(min_ray_angle_degrees) for i in range(ransac_tries): random_id = int(np.random.rand() * (len(all_combinations) - 1)) if random_id in combinatiom_tried: @@ -929,16 +885,18 @@ def triangulate_robust( os_t, bs_t, thresholds, - np.radians(min_ray_angle_degrees), - np.radians(180.0 - min_ray_angle_degrees), + min_ray_angle_radians, + min_depth, ) X = pygeometry.point_refinement(os_t, bs_t, X, iterations) if valid_triangulation: reprojected_bs = X - os - reprojected_bs /= np.linalg.norm(reprojected_bs, axis=1)[:, np.newaxis] + reprojected_bs /= np.linalg.norm(reprojected_bs, + axis=1)[:, np.newaxis] inliers = np.nonzero( - np.linalg.norm(reprojected_bs - bs, axis=1) < reproj_threshold + np.linalg.norm(reprojected_bs - bs, + axis=1) < reproj_threshold )[0].tolist() if len(inliers) > len(best_inliers): @@ -946,8 +904,8 @@ def triangulate_robust( os[inliers], bs[inliers], len(inliers) * [reproj_threshold], - np.radians(min_ray_angle_degrees), - np.radians(180.0 - min_ray_angle_degrees), + min_ray_angle_radians, + min_depth, ) new_X = pygeometry.point_refinement( os[inliers], bs[inliers], X, iterations @@ -958,7 +916,8 @@ def triangulate_robust( :, np.newaxis ] ls_inliers = np.nonzero( - np.linalg.norm(reprojected_bs - bs, axis=1) < reproj_threshold + np.linalg.norm(reprojected_bs - bs, + axis=1) < reproj_threshold )[0] if len(ls_inliers) > len(inliers): best_inliers = ls_inliers @@ -981,14 +940,16 @@ def triangulate_robust( self.tracks_handler.store_track_coordinates(track, best_point) for i in best_inliers: self.tracks_handler.store_inliers_observation(track, ids[i]) + return len(best_inliers) > 1 def triangulate( self, track: str, reproj_threshold: float, min_ray_angle_degrees: float, + min_depth: float, iterations: int, - ) -> None: + ) -> bool: """Triangulate track and add point to reconstruction.""" os, bs, ids = [], [], [] for shot_id, obs in self.tracks_handler.get_observations(track).items(): @@ -1001,12 +962,13 @@ def triangulate( if len(os) >= 2: thresholds = len(os) * [reproj_threshold] + min_ray_angle_radians = np.radians(min_ray_angle_degrees) valid_triangulation, X = pygeometry.triangulate_bearings_midpoint( np.asarray(os), np.asarray(bs), thresholds, - np.radians(min_ray_angle_degrees), - np.radians(180.0 - min_ray_angle_degrees), + min_ray_angle_radians, + min_depth, ) if valid_triangulation: X = pygeometry.point_refinement( @@ -1014,15 +976,20 @@ def triangulate( ) self.tracks_handler.store_track_coordinates(track, X.tolist()) for shot_id in ids: - self.tracks_handler.store_inliers_observation(track, shot_id) + self.tracks_handler.store_inliers_observation( + track, shot_id) + return True + + return False def triangulate_dlt( self, track: str, reproj_threshold: float, min_ray_angle_degrees: float, + min_depth: float, iterations: int, - ) -> None: + ) -> bool: """Triangulate track using DLT and add point to reconstruction.""" Rts, bs, os, ids = [], [], [], [] for shot_id, obs in self.tracks_handler.get_observations(track).items(): @@ -1035,10 +1002,13 @@ def triangulate_dlt( if len(Rts) >= 2: e, X = pygeometry.triangulate_bearings_dlt( + # pyre-fixme[6]: For 1st argument expected `List[ndarray[typing.Any, + # typing.Any]]` but got `ndarray[typing.Any, dtype[typing.Any]]`. np.asarray(Rts), np.asarray(bs), reproj_threshold, np.radians(min_ray_angle_degrees), + min_depth, ) if e: X = pygeometry.point_refinement( @@ -1046,7 +1016,10 @@ def triangulate_dlt( ) self.tracks_handler.store_track_coordinates(track, X.tolist()) for shot_id in ids: - self.tracks_handler.store_inliers_observation(track, shot_id) + self.tracks_handler.store_inliers_observation( + track, shot_id) + return True + return False def triangulate_planar( self, track: str, threshold: float @@ -1105,7 +1078,7 @@ def _shot_origin(self, shot: pymap.Shot) -> np.ndarray: self.origins[shot.id] = o return o - def _shot_rotation_inverse(self, shot: pymap.Shot) -> np.ndarray: + def _shot_rotation_inverse(self, shot: pymap.Shot) -> NDArray: if shot.id in self.rotation_inverses: return self.rotation_inverses[shot.id] else: @@ -1113,7 +1086,7 @@ def _shot_rotation_inverse(self, shot: pymap.Shot) -> np.ndarray: self.rotation_inverses[shot.id] = r return r - def _shot_Rt(self, shot: pymap.Shot) -> np.ndarray: + def _shot_Rt(self, shot: pymap.Shot) -> NDArray: if shot.id in self.Rts: return self.Rts[shot.id] else: @@ -1127,14 +1100,16 @@ def triangulate_shot_features( reconstruction: types.Reconstruction, shot_ids: Set[str], config: Dict[str, Any], -) -> None: +) -> List[str]: """Reconstruct as many tracks seen in shot_id as possible.""" reproj_threshold = config["triangulation_threshold"] min_ray_angle = config["triangulation_min_ray_angle"] + min_depth = config["triangulation_min_depth"] refinement_iterations = config["triangulation_refinement_iterations"] triangulator = TrackTriangulator( - reconstruction, TrackHandlerTrackManager(tracks_manager, reconstruction) + reconstruction, TrackHandlerTrackManager( + tracks_manager, reconstruction) ) all_shots_ids = set(tracks_manager.get_shot_ids()) @@ -1144,16 +1119,29 @@ def triangulate_shot_features( if s in all_shots_ids for t in tracks_manager.get_shot_observations(s) } + + created_tracks = [] for track in tracks_ids: if track not in reconstruction.points: if config["triangulation_type"] == "ROBUST": - triangulator.triangulate_robust( - track, reproj_threshold, min_ray_angle, refinement_iterations - ) + if (triangulator.triangulate_robust( + track, + reproj_threshold, + min_ray_angle, + min_depth, + refinement_iterations, + )): + created_tracks.append(track) elif config["triangulation_type"] == "FULL": - triangulator.triangulate( - track, reproj_threshold, min_ray_angle, refinement_iterations - ) + if (triangulator.triangulate( + track, + reproj_threshold, + min_ray_angle, + min_depth, + refinement_iterations, + )): + created_tracks.append(track) + return created_tracks def retriangulate( @@ -1166,30 +1154,39 @@ def retriangulate( report = {} report["num_points_before"] = len(reconstruction.points) - threshold = config["triangulation_threshold"] - min_ray_angle = config["triangulation_min_ray_angle"] - refinement_iterations = config["triangulation_refinement_iterations"] + if config["triangulation_type"] == "FULL": + reconstruction.points = {} + pysfm.reconstruct_from_tracks_manager( + reconstruction.map, tracks_manager, config + ) + else: + threshold = config["triangulation_threshold"] + min_ray_angle = config["triangulation_min_ray_angle"] + min_depth = config["triangulation_min_depth"] + refinement_iterations = config["triangulation_refinement_iterations"] - reconstruction.points = {} + reconstruction.points = {} - all_shots_ids = set(tracks_manager.get_shot_ids()) + all_shots_ids = set(tracks_manager.get_shot_ids()) - triangulator = TrackTriangulator( - reconstruction, TrackHandlerTrackManager(tracks_manager, reconstruction) - ) - tracks = set() - for image in reconstruction.shots.keys(): - if image in all_shots_ids: - tracks.update(tracks_manager.get_shot_observations(image).keys()) - for track in tracks: - if config["triangulation_type"] == "ROBUST": - triangulator.triangulate_robust( - track, threshold, min_ray_angle, refinement_iterations - ) - elif config["triangulation_type"] == "FULL": - triangulator.triangulate( - track, threshold, min_ray_angle, refinement_iterations - ) + triangulator = TrackTriangulator( + reconstruction, TrackHandlerTrackManager( + tracks_manager, reconstruction) + ) + tracks = set() + for image in reconstruction.shots.keys(): + if image in all_shots_ids: + tracks.update( + tracks_manager.get_shot_observations(image).keys()) + for track in tracks: + if config["triangulation_type"] == "ROBUST": + triangulator.triangulate_robust( + track, threshold, min_ray_angle, min_depth, refinement_iterations + ) + else: + raise ValueError( + f"This should be handled before in CPP: {config['triangulation_type']}" + ) report["num_points_after"] = len(reconstruction.points) chrono.lap("retriangulate") @@ -1253,7 +1250,7 @@ def remove_outliers( reconstruction: types.Reconstruction, config: Dict[str, Any], points: Optional[Dict[str, pymap.Landmark]] = None, -) -> int: +) -> Tuple[List[Tuple[str, str]], Set[str]]: """Remove points with large reprojection error. A list of point ids to be processed can be given in ``points``. @@ -1275,14 +1272,16 @@ def remove_outliers( reconstruction.map.remove_observation(shot_id, track) track_ids.add(track) + removed_tracks = set() for track in track_ids: if track in reconstruction.points: lm = reconstruction.points[track] if lm.number_of_observations() < 2: reconstruction.map.remove_landmark(lm) + removed_tracks.add(track) logger.info("Removed outliers: {}".format(len(outliers))) - return len(outliers) + return outliers, removed_tracks def shot_lla_and_compass( @@ -1303,7 +1302,7 @@ def align_two_reconstruction( r2: types.Reconstruction, common_tracks: List[Tuple[str, str]], threshold: float, -) -> Tuple[bool, Optional[np.ndarray], List[int]]: +) -> Tuple[bool, Optional[NDArray], List[int]]: """Estimate similarity transform T between two, reconstructions r1 and r2 such as r2 = T . r1 """ @@ -1331,7 +1330,8 @@ def merge_two_reconstructions( ) -> List[types.Reconstruction]: """Merge two reconstructions with common tracks IDs.""" common_tracks = list(set(r1.points) & set(r2.points)) - worked, T, inliers = align_two_reconstruction(r1, r2, common_tracks, threshold) + worked, T, inliers = align_two_reconstruction( + r1, r2, common_tracks, threshold) if T and worked and len(inliers) >= 10: s, A, b = multiview.decompose_similarity_transform(T) @@ -1356,15 +1356,17 @@ def merge_reconstructions( reconstructions_merged = [] num_merge = 0 - for (i, j) in combinations(ids_reconstructions, 2): + for i, j in combinations(ids_reconstructions, 2): if (i in remaining_reconstruction) and (j in remaining_reconstruction): r = merge_two_reconstructions( reconstructions[i], reconstructions[j], config ) if len(r) == 1: - remaining_reconstruction = list(set(remaining_reconstruction) - {i, j}) + remaining_reconstruction = list( + set(remaining_reconstruction) - {i, j}) for k in remaining_reconstruction: - rr = merge_two_reconstructions(r[0], reconstructions[k], config) + rr = merge_two_reconstructions( + r[0], reconstructions[k], config) if len(r) == 2: break else: @@ -1433,7 +1435,7 @@ class ShouldRetriangulate: ratio: float reconstruction: types.Reconstruction - def __init__(self, data, reconstruction) -> None: + def __init__(self, data: DataSetBase, reconstruction: types.Reconstruction) -> None: self.active = data.config["retriangulation"] self.ratio = data.config["retriangulation_ratio"] self.reconstruction = reconstruction @@ -1447,6 +1449,75 @@ def done(self) -> None: self.num_points_last = len(self.reconstruction.points) +class ResectionCandidates: + """Helper to keep track of resection candidates.""" + + def __init__(self, tracks_manager: pymap.TracksManager, reconstruction: types.Reconstruction) -> None: + self.tracks_manager = tracks_manager + self.reconstruction = reconstruction + self.candidates: Dict[str, Set[str]] = {} + self.tracks = set() + + def update(self, reconstruction: types.Reconstruction) -> None: + """Update candidates based on a new reconstruction.""" + self.reconstruction = reconstruction + rec_tracks = set(reconstruction.points.keys()) + + # add new tracks + for track_id in rec_tracks: + if track_id not in self.tracks: + self.tracks.add(track_id) + for shot_id in self.tracks_manager.get_track_observations(track_id): + if shot_id not in self.candidates: + self.candidates[shot_id] = set() + self.candidates[shot_id].add(track_id) + + # remove tracks that are not in the reconstruction + to_remove = set() + for track_id in self.tracks: + if track_id not in rec_tracks: + for shot_id, shot_candidates in self.candidates.items(): + if track_id in shot_candidates: + shot_candidates.remove(track_id) + to_remove.add(track_id) + + for track_id in to_remove: + self.tracks.remove(track_id) + + def add(self, shots: List[str], tracks: Set[str]) -> None: + """Add newly resected tracks and the shots they belong to.""" + for shot_id in shots: + if shot_id in self.candidates: + del self.candidates[shot_id] + + for track_id in tracks: + self.tracks.add(track_id) + for shot_id in self.tracks_manager.get_track_observations(track_id): + if shot_id in self.reconstruction.shots: + continue + if shot_id not in self.candidates: + self.candidates[shot_id] = set() + self.candidates[shot_id].add(track_id) + + def remove(self, outliers: List[Tuple[str, str]], removed_tracks: List[str]) -> None: + """Remove outliers from candidates.""" + for shot_id, track_id in outliers: + if shot_id in self.candidates and track_id in self.candidates[shot_id]: + self.candidates[shot_id].remove(track_id) + if not self.candidates[shot_id]: + del self.candidates[shot_id] + + for track_id in removed_tracks: + for shot_id, shot_candidates in self.candidates.items(): + if track_id in shot_candidates: + shot_candidates.remove(track_id) + self.tracks.remove(track_id) + + def get_candidates(self, images: Set[str]) -> List[Tuple[str, int]]: + """Get sorted candidates for resection.""" + return [(k, len(v)) for k, v in filter(lambda x: x[0] in images, sorted(self.candidates.items(), key=lambda x: -len(x[1])))] + + def grow_reconstruction( data: DataSetBase, tracks_manager: pymap.TracksManager, @@ -1458,18 +1529,34 @@ def grow_reconstruction( config = data.config report = {"steps": []} + initial_memory = context.log_memory("grow_reconstruction start") + report["initial_memory_usage"] = initial_memory + camera_priors = data.load_camera_models() rig_camera_priors = data.load_rig_cameras() paint_reconstruction(data, tracks_manager, reconstruction) - align_reconstruction(reconstruction, gcp, config) + align_reconstruction(reconstruction, [], config) - bundle(reconstruction, camera_priors, rig_camera_priors, None, config) + bundle(reconstruction, camera_priors, rig_camera_priors, None, 0, config) remove_outliers(reconstruction, config) paint_reconstruction(data, tracks_manager, reconstruction) + resection_candidates = ResectionCandidates(tracks_manager, reconstruction) should_bundle = ShouldBundle(data, reconstruction) should_retriangulate = ShouldRetriangulate(data, reconstruction) + + local_ba_radius = config["local_bundle_radius"] + local_ba_grid = config["local_bundle_grid"] + final_bundle_grid = config["final_bundle_grid"] + + resection_candidates.add( + list(reconstruction.shots.keys()), + list(reconstruction.points.keys()), + ) + redundant_shots = set() + ratio_redundant = config["resect_redundancy_threshold"] + while True: if config["save_partial_reconstructions"]: paint_reconstruction(data, tracks_manager, reconstruction) @@ -1480,17 +1567,28 @@ def grow_reconstruction( ), ) - candidates = reconstructed_points_for_images( - tracks_manager, reconstruction, images - ) + candidates = resection_candidates.get_candidates(images) if not candidates: break logger.info("-------------------------------------------------------") threshold = data.config["resection_threshold"] min_inliers = data.config["resection_min_inliers"] - for image, _ in candidates: - ok, new_shots, resrep = resect( + + for image, resect_points in candidates: + + new_tracks = { + t for t in tracks_manager.get_shot_observations(image)} + ratio_existing = resect_points / \ + len(new_tracks) if new_tracks else 1.0 + logger.info("Ratio of resected tracks in {}: {:.2f}".format( + image, ratio_existing)) + if ratio_existing > ratio_redundant: + redundant_shots.add(image) + images.remove(image) + continue + + ok, new_shots, resected_tracks, resrep = resect( data, tracks_manager, reconstruction, @@ -1502,6 +1600,7 @@ def grow_reconstruction( continue images -= new_shots + redundant_shots -= new_shots bundle_shot_poses( reconstruction, new_shots, @@ -1509,72 +1608,113 @@ def grow_reconstruction( rig_camera_priors, data.config, ) + resection_candidates.add(new_shots, resected_tracks) - logger.info(f"Adding {' and '.join(new_shots)} to the reconstruction") + logger.info( + f"Adding {' and '.join(new_shots)} to the reconstruction") step: Dict[str, Union[List[int], List[str], int, List[int], Any]] = { "images": list(new_shots), "resection": resrep, - "memory_usage": current_memory_usage(), + "memory_usage": context.current_memory_usage(), } report["steps"].append(step) np_before = len(reconstruction.points) - triangulate_shot_features(tracks_manager, reconstruction, new_shots, config) + new_tracks = triangulate_shot_features( + tracks_manager, reconstruction, new_shots, config) + resection_candidates.add([], new_tracks) np_after = len(reconstruction.points) step["triangulated_points"] = np_after - np_before if should_retriangulate.should(): logger.info("Re-triangulating") - align_reconstruction(reconstruction, gcp, config) + align_reconstruction(reconstruction, [], config) b1rep = bundle( - reconstruction, camera_priors, rig_camera_priors, None, config + reconstruction, camera_priors, rig_camera_priors, None, local_ba_grid, config ) rrep = retriangulate(tracks_manager, reconstruction, config) + resection_candidates.update(reconstruction) b2rep = bundle( - reconstruction, camera_priors, rig_camera_priors, None, config + reconstruction, camera_priors, rig_camera_priors, None, local_ba_grid, config ) - remove_outliers(reconstruction, config) + resection_candidates.remove( + *remove_outliers(reconstruction, config)) step["bundle"] = b1rep step["retriangulation"] = rrep step["bundle_after_retriangulation"] = b2rep should_retriangulate.done() should_bundle.done() elif should_bundle.should(): - align_reconstruction(reconstruction, gcp, config) + align_reconstruction(reconstruction, [], config) brep = bundle( - reconstruction, camera_priors, rig_camera_priors, None, config + reconstruction, camera_priors, rig_camera_priors, None, local_ba_grid, config ) - remove_outliers(reconstruction, config) + resection_candidates.remove( + *remove_outliers(reconstruction, config)) step["bundle"] = brep should_bundle.done() - elif config["local_bundle_radius"] > 0: + elif local_ba_radius > 0: bundled_points, brep = bundle_local( reconstruction, camera_priors, rig_camera_priors, None, + local_ba_grid, image, config, ) - remove_outliers(reconstruction, config, bundled_points) + resection_candidates.remove( + *remove_outliers(reconstruction, config, bundled_points)) step["local_bundle"] = brep + logger.info( + f"Reconstruction now has {len(reconstruction.shots)} shots.") + break else: - logger.info("Some images can not be added") + if redundant_shots: + images.update(redundant_shots) + redundant_shots.clear() + ratio_redundant = 1 + local_ba_radius = 0 + else: + logger.info("Some images can not be added") + break + + if config["incremental_max_shots_count"] > 0 and len(reconstruction.shots) >= config["incremental_max_shots_count"]: + logger.info( + f"Reached the maximum number of shots: {config['incremental_max_shots_count']}") break logger.info("-------------------------------------------------------") - align_result = align_reconstruction(reconstruction, gcp, config, bias_override=True) + align_result = align_reconstruction( + reconstruction, gcp, config, bias_override=True) if not align_result and config["bundle_compensate_gps_bias"]: overidden_config = config.copy() overidden_config["bundle_compensate_gps_bias"] = False config = overidden_config - bundle(reconstruction, camera_priors, rig_camera_priors, gcp, config) - remove_outliers(reconstruction, config) + bundle(reconstruction, camera_priors, rig_camera_priors, + gcp, final_bundle_grid, config) + resection_candidates.remove(*remove_outliers(reconstruction, config)) + + if config["filter_final_point_cloud"]: + bad_condition = pysfm.filter_badly_conditioned_points( + reconstruction.map, config["triangulation_min_ray_angle"] + ) + logger.info("Removed bad-condition: {}".format(bad_condition)) + isolated = pysfm.remove_isolated_points(reconstruction.map) + logger.info("Removed isolated: {}".format(isolated)) + paint_reconstruction(data, tracks_manager, reconstruction) + final_memory = context.log_memory("grow_reconstruction end") + report["final_memory_usage"] = final_memory + report["memory_delta"] = final_memory - initial_memory + logger.info( + f"[Memory] Total memory change during grow_reconstruction: " + f"{(final_memory - initial_memory) / 1024 / 1024:.1f} GB" + ) return reconstruction, report @@ -1599,6 +1739,7 @@ def triangulation_reconstruction( config_override = config.copy() config_override["triangulation_type"] = "ROBUST" config_override["bundle_max_iterations"] = 10 + bundle_grid = config["final_bundle_grid"] report["steps"] = [] outer_iterations = 3 @@ -1614,14 +1755,15 @@ def triangulation_reconstruction( if config_override["save_partial_reconstructions"]: paint_reconstruction(data, tracks_manager, reconstruction) data.save_reconstruction( - [reconstruction], f"reconstruction.{i*inner_iterations+j}.json" + [reconstruction], f"reconstruction.{i * inner_iterations + j}.json" ) step = {} - logger.info(f"Triangulation SfM. Inner iteration {j}, running bundle ...") - align_reconstruction(reconstruction, gcp, config_override) + logger.info( + f"Triangulation SfM. Inner iteration {j}, running bundle ...") + align_reconstruction(reconstruction, [], config_override) b1rep = bundle( - reconstruction, camera_priors, rig_camera_priors, None, config_override + reconstruction, camera_priors, rig_camera_priors, None, bundle_grid, config_override ) remove_outliers(reconstruction, config_override) step["bundle"] = b1rep @@ -1633,18 +1775,106 @@ def triangulation_reconstruction( chrono.lap("compute_reconstructions") report["wall_times"] = dict(chrono.lap_times()) - align_result = align_reconstruction(reconstruction, gcp, config, bias_override=True) + align_result = align_reconstruction( + reconstruction, gcp, config, bias_override=True) if not align_result and config["bundle_compensate_gps_bias"]: overidden_bias_config = config.copy() overidden_bias_config["bundle_compensate_gps_bias"] = False config = overidden_bias_config - bundle(reconstruction, camera_priors, rig_camera_priors, gcp, config) + bundle(reconstruction, camera_priors, + rig_camera_priors, gcp, bundle_grid, config) remove_outliers(reconstruction, config_override) paint_reconstruction(data, tracks_manager, reconstruction) return report, [reconstruction] +def _get_common_feature_arrays( + tracks_manager: pymap.TracksManager, + im1: str, + im2: str, +) -> Tuple[NDArray, NDArray]: + """Return the feature arrays of common tracks between two images.""" + _, p1, p2 = tracks_manager.get_all_common_observations_arrays(im1, im2) + return p1, p2 + + +def compute_image_pairs_sequential( + data: DataSetBase, + tracks_manager: pymap.TracksManager, + min_common: int = 50, +) -> List[Tuple[str, str]]: + """Compute all possible pairs of images that share at least `min_common` points. + The pairs are sorted by "reconstructability" which is estimated by the + pairwise_reconstructability function. + """ + cameras = data.load_camera_models() + threshold = 4 * data.config["five_point_algo_threshold"] + results = [] + connectivity = tracks_manager.get_all_pairs_connectivity() + for (im1, im2), size in connectivity.items(): + if size < min_common: + continue + camera1 = cameras[data.load_exif(im1)["camera"]] + camera2 = cameras[data.load_exif(im2)["camera"]] + r = calculate_pair_reconstructability( + tracks_manager, im1, im2, camera1, camera2, threshold + ) + if r > 0: + results.append((im1, im2, r)) + results.sort(key=lambda x: x[2], reverse=True) + return [(im1, im2) for im1, im2, _ in results] + + +def compute_image_pairs_with_reconstructability_parallel( + data: DataSetBase, + tracks_manager: pymap.TracksManager, + min_common: int = 50, +) -> List[Tuple[str, str]]: + """Compute all possible pairs of images that share at least `min_common` points. + + This runs in parallel and fuses the feature extraction and reconstructability + computation to save memory. + """ + cameras = data.load_camera_models() + threshold = 4 * data.config["five_point_algo_threshold"] + + shot_ids = tracks_manager.get_shot_ids() + shot_to_camera_id = {} + for shot in shot_ids: + shot_to_camera_id[shot] = data.load_exif(shot)["camera"] + + def process_pair(pair): + im1, im2, size = pair + if size < min_common: + return None + + camera1 = cameras[shot_to_camera_id[im1]] + camera2 = cameras[shot_to_camera_id[im2]] + + r = calculate_pair_reconstructability( + tracks_manager, im1, im2, camera1, camera2, threshold + ) + if r > 0: + return (im1, im2, r) + return None + + logger.debug("Computing pairwise connectivity of images") + all_pairs_connectivity = tracks_manager.get_all_pairs_connectivity() + pairs = [(k[0], k[1], v) for k, v in all_pairs_connectivity.items()] + + logger.debug( + f"Computing pairwise reconstructability with {data.config['processes']} processes") + processes = data.config["processes"] + batch_size = max(1, len(pairs) // (2 * processes)) + + results = context.parallel_map(process_pair, pairs, processes, batch_size) + + valid_results = [r for r in results if r is not None] + valid_results.sort(key=lambda x: x[2], reverse=True) + return [(im1, im2) for im1, im2, _ in valid_results] + + def incremental_reconstruction( data: DataSetBase, tracks_manager: pymap.TracksManager ) -> Tuple[Dict[str, Any], List[types.Reconstruction]]: @@ -1659,9 +1889,21 @@ def incremental_reconstruction( remaining_images = set(images) gcp = data.load_ground_control_points() - common_tracks = tracking.all_common_tracks_with_features(tracks_manager) + logger.info(f"Loaded {len(gcp)} ground control points.") + + common_tracks = None + if data.config["processes"] > 1: + # Get pairs in parallel, computing reconstructability on the fly. + # Pros: fast, low memory usage + pairs = compute_image_pairs_with_reconstructability_parallel( + data, tracks_manager) + else: + # Get pairs sequentially, load features lazily. + # Pros: low memory usage; Cons: slow + pairs = compute_image_pairs_sequential(data, tracks_manager) + logging.info(f"Estimated reconstructability of {len(pairs)} image pairs.") + reconstructions = [] - pairs = compute_image_pairs(common_tracks, data) chrono.lap("compute_image_pairs") report["num_candidate_image_pairs"] = len(pairs) report["reconstructions"] = [] @@ -1669,7 +1911,7 @@ def incremental_reconstruction( if im1 in remaining_images and im2 in remaining_images: rec_report = {} report["reconstructions"].append(rec_report) - _, p1, p2 = common_tracks[im1, im2] + p1, p2 = _get_common_feature_arrays(tracks_manager, im1, im2) reconstruction, rec_report["bootstrap"] = bootstrap_reconstruction( data, tracks_manager, im1, im2, p1, p2 ) @@ -1684,7 +1926,15 @@ def incremental_reconstruction( gcp, ) reconstructions.append(reconstruction) - reconstructions = sorted(reconstructions, key=lambda x: -len(x.shots)) + reconstructions = sorted( + reconstructions, key=lambda x: -len(x.shots)) + + if data.config["incremental_max_shots_count"] > 0 and sum( + len(r.shots) for r in reconstructions + ) >= data.config["incremental_max_shots_count"]: + logger.info( + f"Reached the maximum number of shots") + break for k, r in enumerate(reconstructions): logger.info( @@ -1692,7 +1942,8 @@ def incremental_reconstruction( k, len(r.shots), len(r.points) ) ) - logger.info("{} partial reconstructions in total.".format(len(reconstructions))) + logger.info("{} partial reconstructions in total.".format( + len(reconstructions))) chrono.lap("compute_reconstructions") report["wall_times"] = dict(chrono.lap_times()) report["not_reconstructed_images"] = list(remaining_images) @@ -2036,7 +2287,8 @@ def reconstruct_from_prior( rec_report["num_remaining_images"] = len(remaining_images) # Start with the known poses - triangulate_shot_features(tracks_manager, reconstruction, prior_images, data.config) + triangulate_shot_features( + tracks_manager, reconstruction, prior_images, data.config) paint_reconstruction(data, tracks_manager, reconstruction) report["not_reconstructed_images"] = list(remaining_images) return report, reconstruction diff --git a/opensfm/reconstruction_helpers.py b/opensfm/reconstruction_helpers.py index 7c2ac892a..292612bea 100644 --- a/opensfm/reconstruction_helpers.py +++ b/opensfm/reconstruction_helpers.py @@ -1,8 +1,10 @@ +# pyre-strict import logging import math -from typing import Any, Dict, Iterable, List, Optional +from typing import Any, Dict, Iterable, Optional import numpy as np +from numpy.typing import NDArray from opensfm import exif as oexif, geometry, multiview, pygeometry, pymap, rig, types from opensfm.dataset_base import DataSetBase @@ -10,7 +12,7 @@ logger: logging.Logger = logging.getLogger(__name__) -def guess_gravity_up_from_orientation_tag(orientation: int) -> np.ndarray: +def guess_gravity_up_from_orientation_tag(orientation: int) -> NDArray: """Guess upward vector in camera coordinates given the orientation tag. Assumes camera is looking towards the horizon and horizon is horizontal @@ -36,7 +38,7 @@ def guess_gravity_up_from_orientation_tag(orientation: int) -> np.ndarray: raise RuntimeError(f"Error: Unknown orientation tag: {orientation}") -def shot_gravity_up_in_image_axis(shot: pymap.Shot) -> Optional[np.ndarray]: +def shot_gravity_up_in_image_axis(shot: pymap.Shot) -> Optional[NDArray]: """Get or guess shot's gravity up direction.""" if shot.metadata.gravity_down.has_value: return -shot.metadata.gravity_down.value @@ -53,14 +55,14 @@ def shot_gravity_up_in_image_axis(shot: pymap.Shot) -> Optional[np.ndarray]: return guess_gravity_up_from_orientation_tag(orientation) -def rotation_from_shot_metadata(shot: pymap.Shot) -> Optional[np.ndarray]: +def rotation_from_shot_metadata(shot: pymap.Shot) -> Optional[NDArray]: rotation = rotation_from_angles(shot) if rotation is None: rotation = rotation_from_orientation_compass(shot) return rotation -def rotation_from_orientation_compass(shot: pymap.Shot) -> Optional[np.ndarray]: +def rotation_from_orientation_compass(shot: pymap.Shot) -> Optional[NDArray]: up_vector = shot_gravity_up_in_image_axis(shot) if up_vector is None: return None @@ -71,7 +73,7 @@ def rotation_from_orientation_compass(shot: pymap.Shot) -> Optional[np.ndarray]: return multiview.rotation_matrix_from_up_vector_and_compass(list(up_vector), angle) -def rotation_from_angles(shot: pymap.Shot) -> Optional[np.ndarray]: +def rotation_from_angles(shot: pymap.Shot) -> Optional[NDArray]: if not shot.metadata.opk_angles.has_value: return None opk_degrees = shot.metadata.opk_angles.value @@ -160,7 +162,7 @@ def exif_to_metadata( if "compass" in exif: metadata.compass_angle.value = exif["compass"]["angle"] - if "accuracy" in exif["compass"]: + if exif["compass"].get("accuracy") is not None: metadata.compass_accuracy.value = exif["compass"]["accuracy"] if "capture_time" in exif: diff --git a/opensfm/report.py b/opensfm/report.py index 9387e6b2b..443de8447 100644 --- a/opensfm/report.py +++ b/opensfm/report.py @@ -1,13 +1,14 @@ +# pyre-strict import logging import os import subprocess import tempfile +from typing import Any, Dict, List, Optional import PIL from fpdf import FPDF from opensfm import io from opensfm.dataset import DataSet -from typing import Any, Dict logger: logging.Logger = logging.getLogger(__name__) @@ -42,17 +43,20 @@ def __init__(self, data: DataSet, stats = None) -> None: self.stats = self._read_stats_file("stats.json") def save_report(self, filename: str) -> None: - # pyre-fixme[28]: Unexpected keyword argument `dest`. - bytestring = self.pdf.output(dest="S") + bytestring = self.pdf.output() if isinstance(bytestring, str): bytestring = bytestring.encode("utf8") - with self.io_handler.open( - os.path.join(self.output_path, filename), "wb" - ) as fwb: + with self.io_handler.open_wb(os.path.join(self.output_path, filename)) as fwb: fwb.write(bytestring) - def _make_table(self, columns_names, rows, row_header=False) -> None: + + def _make_table( + self, + columns_names: Optional[List[str]], + rows: List[List[str]], + row_header: bool = False, + ) -> None: if len(rows) == 0: logger.warning("Cannot make table (rows missing)") return @@ -124,11 +128,11 @@ def _make_subsection(self, title: str) -> None: self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin) def _make_centered_image(self, image_path: str, desired_height: float) -> None: - with tempfile.TemporaryDirectory() as tmp_local_dir: - local_image_path = os.path.join(tmp_local_dir, os.path.basename(image_path)) - with self.io_handler.open(local_image_path, "wb") as fwb: - with self.io_handler.open(image_path, "rb") as f: + local_image_path = os.path.join( + tmp_local_dir, os.path.basename(image_path)) + with self.io_handler.open_wb(local_image_path) as fwb: + with self.io_handler.open_rb(image_path) as f: fwb.write(f.read()) width, height = PIL.Image.open(local_image_path).size @@ -181,12 +185,12 @@ def make_dataset_summary(self) -> None: ["Date", self.stats["processing_statistics"]["date"]], [ "Area Covered", - f"{self.stats['processing_statistics']['area']/1e6:.6f} km²", + f"{self.stats['processing_statistics']['area'] / 1e6:.6f} km²", ], [ "Processing Time", #f"{self.stats['processing_statistics']['steps_times']['Total Time']:.2f} seconds", - self.stats['odm_processing_statistics']['total_time_human'], + self.stats['odm_processing_statistics']['total_time_human'] if 'odm_processing_statistics' in self.stats else f"{self.stats['processing_statistics']['steps_times']['Total Time']:.2f} seconds", ], ["Capture Start", self.stats["processing_statistics"]["start_date"]], ["Capture End", self.stats["processing_statistics"]["end_date"]], @@ -232,7 +236,7 @@ def make_processing_summary(self) -> None: ], [ "Reconstructed Points (Sparse)", - f"{rec_points} over {init_points} points ({rec_points/init_points*100:.1f}%)", + f"{rec_points} over {init_points} points ({rec_points / init_points * 100:.1f}%)", ], # [ # "Reconstructed Components", @@ -258,11 +262,12 @@ def make_processing_summary(self) -> None: ]) # GSD (if available) - if self.stats['odm_processing_statistics'].get('average_gsd'): - rows.insert(3, [ - "Average Ground Sampling Distance (GSD)", - f"{self.stats['odm_processing_statistics']['average_gsd']:.1f} cm" - ]) + if 'odm_processing_statistics' in self.stats: + if self.stats['odm_processing_statistics'].get('average_gsd'): + rows.insert(3, [ + "Average Ground Sampling Distance (GSD)", + f"{self.stats['odm_processing_statistics']['average_gsd']:.1f} cm" + ]) row_gps_gcp = [" / ".join(geo_string) + " errors"] geo_errors = [] @@ -295,7 +300,8 @@ def make_processing_summary(self) -> None: def make_processing_time_details(self) -> None: self._make_section("Processing Time Details") - columns_names = list(self.stats["processing_statistics"]["steps_times"].keys()) + columns_names = list( + self.stats["processing_statistics"]["steps_times"].keys()) formatted_floats = [] for v in self.stats["processing_statistics"]["steps_times"].values(): formatted_floats.append(f"{v:.2f} sec.") @@ -335,9 +341,12 @@ def make_gps_details(self) -> None: continue for comp in ["x", "y", "z"]: row = [comp.upper() + " Error (meters)"] - row.append(f"{self.stats[error_type + '_errors']['mean'][comp]:.3f}") - row.append(f"{self.stats[error_type +'_errors']['std'][comp]:.3f}") - row.append(f"{self.stats[error_type +'_errors']['error'][comp]:.3f}") + row.append( + f"{self.stats[error_type + '_errors']['mean'][comp]:.3f}") + row.append( + f"{self.stats[error_type + '_errors']['std'][comp]:.3f}") + row.append( + f"{self.stats[error_type + '_errors']['error'][comp]:.3f}") rows.append(row) rows.append( @@ -345,7 +354,7 @@ def make_gps_details(self) -> None: "Total", "", "", - f"{self.stats[error_type +'_errors']['average_error']:.3f}", + f"{self.stats[error_type + '_errors']['average_error']:.3f}", ] ) self._make_table(columns_names, rows) @@ -420,6 +429,39 @@ def make_align_details(self) -> None: if os.path.isfile(dsm_feature_matches): self._make_centered_image(dsm_feature_matches, 80) + + def make_orientation_details(self) -> None: + if "opk_errors" not in self.stats: + return + if "average_error" not in self.stats["opk_errors"]: + return + + self._make_section("Orientation Error Details") + columns_names = ["Component", "Mean", "Sigma", "RMS Error"] + error_name = "opk_errors" + + rows = [] + for comp in ["omega", "phi", "kappa"]: + row = [comp.capitalize() + " Error (degrees)"] + row.append( + f"{self.stats[error_name]['mean'][comp]:.3f}") + row.append( + f"{self.stats[error_name]['std'][comp]:.3f}") + row.append( + f"{self.stats[error_name]['error'][comp]:.3f}") + rows.append(row) + + rows.append( + [ + "Total", + "", + "", + f"{self.stats[error_name]['average_error']:.3f}", + ] + ) + self._make_table(columns_names, rows) + self.pdf.set_xy(self.margin, self.pdf.get_y() + self.margin / 2) + def make_features_details(self) -> None: self._make_section("Features Details") @@ -500,7 +542,8 @@ def make_camera_models_details(self) -> None: rows = [] rows.append(["Initial"] + [f"{x:.4f}" for x in initial.values()]) - rows.append(["Optimized"] + [f"{x:.4f}" for x in optimized.values()]) + rows.append(["Optimized"] + + [f"{x:.4f}" for x in optimized.values()]) self._make_subsection(camera) self._make_table(names, rows) @@ -508,7 +551,8 @@ def make_camera_models_details(self) -> None: residual_grid_height = 100 self._make_centered_image( - os.path.join(self.output_path, residual_grids[0]), residual_grid_height + os.path.join(self.output_path, + residual_grids[0]), residual_grid_height ) def make_rig_cameras_details(self) -> None: @@ -681,6 +725,7 @@ def generate_report(self) -> None: else: self.make_align_details() + self.make_orientation_details() self.add_page_break() self.make_features_details() diff --git a/opensfm/rig.py b/opensfm/rig.py index e20003e42..bbf5fd3db 100644 --- a/opensfm/rig.py +++ b/opensfm/rig.py @@ -1,15 +1,16 @@ +# pyre-strict """Tool for handling rigs""" import logging import os import random import re -from typing import Dict, Tuple, List, Optional, Set, Iterable, TYPE_CHECKING +from typing import Dict, Iterable, List, Optional, Set, Tuple, TYPE_CHECKING import networkx as nx import numpy as np import scipy.spatial as spatial -from opensfm import reconstruction as orec, actions, pygeometry, pymap, types +from opensfm import actions, pygeometry, pymap, reconstruction as orec, types if TYPE_CHECKING: from opensfm.dataset import DataSet @@ -42,7 +43,7 @@ def rig_assignments_per_image( assignments_per_image = {} for instance_id, instance in rig_assignments.items(): instance_shots = [s[0] for s in instance] - for (shot_id, rig_camera_id) in instance: + for shot_id, rig_camera_id in instance: assignments_per_image[shot_id] = ( f"{instance_id}", rig_camera_id, @@ -115,7 +116,7 @@ def create_instances_with_patterns( def group_instances( - rig_instances: Dict[str, TRigInstance] + rig_instances: Dict[str, TRigInstance], ) -> Dict[str, List[TRigInstance]]: per_rig_camera_group: Dict[str, List[TRigInstance]] = {} for cameras in rig_instances.values(): diff --git a/opensfm/sensors.py b/opensfm/sensors.py index 69e448645..766fa12d8 100644 --- a/opensfm/sensors.py +++ b/opensfm/sensors.py @@ -1,5 +1,7 @@ +# pyre-strict from functools import lru_cache from typing import Any, Dict, List + import yaml from opensfm import context from opensfm import io @@ -28,7 +30,7 @@ def sensor_data() -> Dict[str, Any]: @lru_cache(1) -def camera_calibration()-> List[Dict[str, Any]]: +def camera_calibration() -> List[Dict[str, Any]]: with io.open_rt(context.CAMERA_CALIBRATION) as f: data = yaml.safe_load(f) return data diff --git a/opensfm/src/CMakeLists.txt b/opensfm/src/CMakeLists.txt index c0a20615c..6223a027f 100644 --- a/opensfm/src/CMakeLists.txt +++ b/opensfm/src/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.15) project(opensfm C CXX) @@ -15,6 +15,10 @@ else() set(CMAKE_MODULE_PATH ${opensfm_SOURCE_DIR}/cmake) endif() +cmake_policy(SET CMP0063 NEW) +# Re-activate once we can have a decent version of CMake (> 3.27) +# cmake_policy(SET CMP0148 NEW) + if (WIN32) # Place compilation results in opensfm/ folder, not in Debug/ or Release/ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${opensfm_SOURCE_DIR}/..") @@ -23,28 +27,33 @@ endif() ####### Compilation Options ####### # Visibility stuff -cmake_policy(SET CMP0063 NEW) set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_CXX_VISIBILITY_INLINES ON) # fPIC set(CMAKE_POSITION_INDEPENDENT_CODE ON) -# C++14 +# C++17 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Enable all warnings if (NOT WIN32) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-sign-compare") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-sign-compare") endif() # For compiling VLFeat add_definitions(-DVL_DISABLE_AVX) +# We're not using an inplace Glog +add_definitions(-DGLOG_USE_GLOG_EXPORT) + # Use the version of vlfeat in ./src/third_party/vlfeat add_definitions(-DINPLACE_VLFEAT) +if(NOT USE_SSE2) + add_definitions(-DVL_DISABLE_SSE2) +endif() if (WIN32) # Missing math constant @@ -68,17 +77,59 @@ endif() find_package(LAPACK) find_package(SuiteSparse) find_package(Eigen3 REQUIRED) -find_package(Ceres REQUIRED) -find_package(Gflags) +find_package(Ceres) +find_package(Gflags REQUIRED) -find_package(OpenCV) +find_library(TCMALLOC_LIBRARY tcmalloc) +if(TCMALLOC_LIBRARY) + message(STATUS "Found tcmalloc: ${TCMALLOC_LIBRARY}") + link_libraries(${TCMALLOC_LIBRARY}) +else() + message(WARNING "tcmalloc not found. Please install gperftools.") +endif() + +# Try to find glog using its native CMake config first (modern systems) +# Fall back to custom FindGlog.cmake for older systems (e.g., Ubuntu 20.04) +find_package(glog QUIET CONFIG) +if(glog_FOUND) + message(STATUS "Using glog native CMake config (modern glog)") +else() + message(STATUS + "glog CMake config not found, using FindGlog.cmake " + "(Ubuntu 20.04 compatibility)" + ) + find_package(Glog REQUIRED) +endif() + +# Ceres2 exposes Ceres::ceres target. +# Ceres1 exposes just ceres. +# - if there's no such target, cmake will convert it into -lceres +# and the linker will fail +if(Ceres_ceres_FOUND) + set(CERES_LIBRARIES Ceres::ceres) +else() + set(CERES_LIBRARIES ceres) +endif() + +# Only link against the OpenCV components we actually need +# This avoids pulling in unnecessary dependencies like viz (which requires VTK) # OpenCV's OpenCVConfig will enforce imgcodecs for < 3.0 -# (even if OPTIONAL_COMPONENTS) so we remove it as we don't need it +# (even if OPTIONAL_COMPONENTS) so we handle version detection carefully # Cause is imread/imwrite moved to imgcodecs on > 3.0 -if(${OpenCV_VERSION} LESS 3.0) - find_package(OpenCV REQUIRED core imgproc calib3d) +find_package(OpenCV QUIET) +if(OpenCV_FOUND AND ${OpenCV_VERSION} VERSION_LESS "3.0") + find_package(OpenCV REQUIRED COMPONENTS core imgproc calib3d) else() - find_package(OpenCV REQUIRED core imgproc calib3d OPTIONAL_COMPONENTS imgcodecs) + find_package(OpenCV REQUIRED COMPONENTS core imgproc calib3d + OPTIONAL_COMPONENTS imgcodecs) +endif() + +# Override OpenCV_LIBS to only include the components we explicitly requested +# This prevents linking against all OpenCV modules +# (especially viz which requires VTK) +set(OpenCV_LIBS opencv_core opencv_imgproc opencv_calib3d) +if(TARGET opencv_imgcodecs) + list(APPEND OpenCV_LIBS opencv_imgcodecs) endif() ####### Third party libraries ####### @@ -108,16 +159,23 @@ if (OPENSFM_BUILD_TESTS) include_directories(third_party/gtest) add_definitions(-DCERES_GFLAGS_NAMESPACE=${GFLAGS_NAMESPACE}) + # Gtest if old now and trigger these + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-field-initializers") + set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -Wno-deprecated-copy-with-user-provided-copy" + ) + add_library(gtest third_party/gtest/gmock_gtest_all.cc third_party/gtest/gmock_main.cc) target_include_directories(gtest PRIVATE ${GFLAGS_INCLUDE_DIR}) + target_link_libraries(gtest PRIVATE glog::glog) set(TEST_MAIN test_main) add_library(${TEST_MAIN} testing_main.cc) target_link_libraries(${TEST_MAIN} ${GFLAGS_LIBRARY} - ${GLOG_LIBRARY} + glog::glog gtest) endif() diff --git a/opensfm/src/bundle/CMakeLists.txt b/opensfm/src/bundle/CMakeLists.txt index 8cf23eea6..34c35fb00 100644 --- a/opensfm/src/bundle/CMakeLists.txt +++ b/opensfm/src/bundle/CMakeLists.txt @@ -58,3 +58,4 @@ target_link_libraries(pybundle PRIVATE set_target_properties(pybundle PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${opensfm_SOURCE_DIR}/.." ) +install(TARGETS pybundle LIBRARY DESTINATION .) diff --git a/opensfm/src/bundle/bundle_adjuster.h b/opensfm/src/bundle/bundle_adjuster.h index a19062abf..02212a302 100644 --- a/opensfm/src/bundle/bundle_adjuster.h +++ b/opensfm/src/bundle/bundle_adjuster.h @@ -9,12 +9,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -34,20 +36,20 @@ struct Reconstruction { bool constant; bool shared; - double *GetScalePtr(const std::string &shot) { + double* GetScalePtr(const std::string& shot) { if (shared) { return &(scales.begin()->second); } return &(scales.at(shot)); } - double GetScale(const std::string &shot) const { + double GetScale(const std::string& shot) const { if (shared) { return scales.begin()->second; } return scales.at(shot); } - void SetScale(const std::string &shot, double v) { + void SetScale(const std::string& shot, double v) { if (shared) { scales.begin()->second = v; } @@ -57,16 +59,17 @@ struct Reconstruction { struct PointProjectionObservation { Vec2d coordinates; - Point *point; - Shot *shot; - Camera *camera; + Point* point; + Shot* shot; + Camera* camera; double std_deviation; + std::optional depth_prior; }; struct RelativeMotion { - RelativeMotion(const std::string &rig_instance_i, - const std::string &rig_instance_j, const Vec3d &rotation, - const Vec3d &translation, double scale, + RelativeMotion(const std::string& rig_instance_i, + const std::string& rig_instance_j, const Vec3d& rotation, + const Vec3d& translation, double scale, double robust_multiplier, bool observed_scale) { rig_instance_id_i = rig_instance_i; rig_instance_id_j = rig_instance_j; @@ -83,7 +86,7 @@ struct RelativeMotion { this->observed_scale = observed_scale; } - void SetScaleMatrix(const MatXd &s) { scale_matrix = s; } + void SetScaleMatrix(const MatXd& s) { scale_matrix = s; } std::string rig_instance_id_i; std::string rig_instance_id_j; @@ -95,16 +98,16 @@ struct RelativeMotion { }; struct RelativeRotation { - RelativeRotation(const std::string &shot_i, const std::string &shot_j, - const Vec3d &r) { + RelativeRotation(const std::string& shot_i, const std::string& shot_j, + const Vec3d& r) { shot_id_i = shot_i; shot_id_j = shot_j; rotation = r; scale_matrix.setIdentity(); } Vec3d GetRotation() const { return rotation; } - void SetRotation(const Vec3d &r) { rotation = r; } - void SetScaleMatrix(const Mat3d &s) { scale_matrix = s; } + void SetRotation(const Vec3d& r) { rotation = r; } + void SetScaleMatrix(const Mat3d& s) { scale_matrix = s; } std::string shot_id_i; std::string shot_id_j; @@ -120,7 +123,7 @@ struct CommonPosition { }; struct HeatmapInterpolator { - HeatmapInterpolator(const std::vector &flatten_heatmap, size_t width, + HeatmapInterpolator(const std::vector& flatten_heatmap, size_t width, double resolution); std::vector heatmap; size_t width; @@ -169,74 +172,77 @@ class BundleAdjuster { // Bundle variables // Basic - void AddCamera(const std::string &id, const geometry::Camera &camera, - const geometry::Camera &prior, bool constant); - void AddPoint(const std::string &id, const Vec3d &position, bool constant); - void AddPointPrior(const std::string &id, const Vec3d &position, - const Vec3d &std_deviation, bool has_altitude_prior); - void SetCameraBias(const std::string &id, const geometry::Similarity &bias); + void AddCamera(const std::string& id, const geometry::Camera& camera, + const geometry::Camera& prior, bool constant); + Point* AddPoint(const std::string& id, const Vec3d& position, bool constant); + void AddPointPrior(const std::string& id, const Vec3d& position, + const Vec3d& std_deviation, bool has_altitude_prior); + void SetCameraBias(const std::string& id, const geometry::Similarity& bias); // Rigs void AddRigInstance( - const std::string &rig_instance_id, - const geometry::Pose &rig_instance_pose, - const std::unordered_map &shot_cameras, - const std::unordered_map &shot_rig_cameras, + const std::string& rig_instance_id, + const geometry::Pose& rig_instance_pose, + const std::unordered_map& shot_cameras, + const std::unordered_map& shot_rig_cameras, bool fixed); - void AddRigCamera(const std::string &rig_camera, const geometry::Pose &pose, - const geometry::Pose &pose_prior, bool fixed); - void AddRigInstancePositionPrior(const std::string &instance_id, - const Vec3d &position, - const Vec3d &std_deviation, - const std::string &scale_group); + void AddRigCamera(const std::string& rig_camera, const geometry::Pose& pose, + const geometry::Pose& pose_prior, bool fixed); + void AddRigInstancePositionPrior(const std::string& instance_id, + const Vec3d& position, + const Vec3d& std_deviation, + const std::string& scale_group); // Cluster-SfM related - void AddReconstruction(const std::string &id, bool constant); - void AddReconstructionInstance(const std::string &reconstruction_id, - double scale, const std::string &instance_id); - void SetScaleSharing(const std::string &id, bool share); + void AddReconstruction(const std::string& id, bool constant); + void AddReconstructionInstance(const std::string& reconstruction_id, + double scale, const std::string& instance_id); + void SetScaleSharing(const std::string& id, bool share); // Real bundle adjustment : point projections - void AddPointProjectionObservation(const std::string &shot, - const std::string &point, - const Vec2d &observation, - double std_deviation); + void AddPointProjectionObservation( + const std::string& shot, const std::string& point, + const Vec2d& observation, double std_deviation, + const std::optional& depth_prior = std::nullopt); + void AddPointProjectionObservationRaw( + Shot* shot, Point* point, const Vec2d& observation, double std_deviation, + const std::optional& depth_prior); // Relative motion constraints - void AddRelativeMotion(const RelativeMotion &rm); - void AddRelativeRotation(const RelativeRotation &rr); + void AddRelativeMotion(const RelativeMotion& rm); + void AddRelativeRotation(const RelativeRotation& rr); // Absolute motion constraints - void AddCommonPosition(const std::string &shot_id1, - const std::string &shot_id2, double margin, + void AddCommonPosition(const std::string& shot_id1, + const std::string& shot_id2, double margin, double std_deviation); - void AddHeatmap(const std::string &heatmap_id, - const std::vector &in_heatmap, size_t in_width, + void AddHeatmap(const std::string& heatmap_id, + const std::vector& in_heatmap, size_t in_width, double resolution); - void AddAbsolutePositionHeatmap(const std::string &shot_id, - const std::string &heatmap_id, + void AddAbsolutePositionHeatmap(const std::string& shot_id, + const std::string& heatmap_id, double x_offset, double y_offset, double std_deviation); - void AddAbsoluteUpVector(const std::string &shot_id, const Vec3d &up_vector, + void AddAbsoluteUpVector(const std::string& shot_id, const Vec3d& up_vector, double std_deviation); - void AddAbsolutePan(const std::string &shot_id, double angle, + void AddAbsolutePan(const std::string& shot_id, double angle, double std_deviation); - void AddAbsoluteTilt(const std::string &shot_id, double angle, + void AddAbsoluteTilt(const std::string& shot_id, double angle, double std_deviation); - void AddAbsoluteRoll(const std::string &shot_id, double angle, + void AddAbsoluteRoll(const std::string& shot_id, double angle, double std_deviation); // Motion priors - void AddLinearMotion(const std::string &shot0_id, const std::string &shot1_id, - const std::string &shot2_id, double alpha, + void AddLinearMotion(const std::string& shot0_id, const std::string& shot1_id, + const std::string& shot2_id, double alpha, double position_std_deviation, double orientation_std_deviation); // Gauge fixing - void SetGaugeFixShots(const std::string &shot_origin, - const std::string &shot_scale); + void SetGaugeFixShots(const std::string& shot_origin, + const std::string& shot_scale); // Minimization setup void SetPointProjectionLossFunction(std::string name, double threshold); @@ -249,9 +255,10 @@ class BundleAdjuster { void SetLinearSolverType(std::string t); void SetCovarianceAlgorithmType(std::string t); - void SetInternalParametersPriorSD(double focal_sd, double c_sd, double k1_sd, - double k2_sd, double p1_sd, double p2_sd, - double k3_sd, double k4_sd); + void SetInternalParametersPriorSD(double focal_sd, double aspect_ratio_sd, + double c_sd, double k1_sd, double k2_sd, + double p1_sd, double p2_sd, double k3_sd, + double k4_sd); void SetRigParametersPriorSD(double rig_translation_sd, double rig_rotation_sd); @@ -261,21 +268,23 @@ class BundleAdjuster { // Minimization void Run(); - void ComputeCovariances(ceres::Problem *problem); + void ComputeCovariances(ceres::Problem* problem); void ComputeReprojectionErrors(); // Getters int GetProjectionsCount() const; int GetRelativeMotionsCount() const; - geometry::Camera GetCamera(const std::string &id) const; - geometry::Similarity GetBias(const std::string &id) const; - Reconstruction GetReconstruction(const std::string &reconstruction_id) const; - Point GetPoint(const std::string &id) const; - bool HasPoint(const std::string &id) const; - RigCamera GetRigCamera(const std::string &rig_camera_id) const; - RigInstance GetRigInstance(const std::string &instance_id) const; + geometry::Camera GetCamera(const std::string& id) const; + geometry::Similarity GetBias(const std::string& id) const; + Reconstruction GetReconstruction(const std::string& reconstruction_id) const; + Point GetPoint(const std::string& id) const; + Point* GetPointRaw(const std::string& id); + bool HasPoint(const std::string& id) const; + RigCamera GetRigCamera(const std::string& rig_camera_id) const; + RigInstance GetRigInstance(const std::string& instance_id) const; std::map GetRigCameras() const; std::map GetRigInstances() const; + Shot* GetShotRaw(const std::string& id); // Minimization details std::string BriefReport() const; @@ -283,7 +292,7 @@ class BundleAdjuster { private: // default sigmas - geometry::Camera GetDefaultCameraSigma(const geometry::Camera &camera) const; + geometry::Camera GetDefaultCameraSigma(const geometry::Camera& camera) const; geometry::Pose GetDefaultRigPoseSigma() const; // minimized data @@ -321,6 +330,7 @@ class BundleAdjuster { // Camera parameters prior double focal_prior_sd_; + double aspect_ratio_prior_sd_; double c_prior_sd_; double k1_sd_; double k2_sd_; diff --git a/opensfm/src/bundle/data/bias.h b/opensfm/src/bundle/data/bias.h index 8c27633a7..38b272635 100644 --- a/opensfm/src/bundle/data/bias.h +++ b/opensfm/src/bundle/data/bias.h @@ -10,20 +10,20 @@ namespace bundle { struct Similarity : public Data { enum Parameter { RX, RY, RZ, TX, TY, TZ, SCALE, NUM_PARAMS }; - Similarity(const std::string &id, const geometry::Similarity &value) + Similarity(const std::string& id, const geometry::Similarity& value) : Data(id, value) { Init(); } private: - void ValueToData(const geometry::Similarity &value, VecXd &data) const final { + void ValueToData(const geometry::Similarity& value, VecXd& data) const final { data.resize(NUM_PARAMS); data.segment<3>(TX) = value.Translation(); data.segment<3>(RX) = value.Rotation(); data(SCALE) = value.Scale(); } - void DataToValue(const VecXd &data, geometry::Similarity &value) const final { + void DataToValue(const VecXd& data, geometry::Similarity& value) const final { value.SetTranslation(data.segment<3>(TX)); value.SetRotation(data.segment<3>(RX)); value.SetScale(data(SCALE)); @@ -34,11 +34,11 @@ struct SimilarityPriorTransform : public geometry::Functor { template - VecN operator()(T const *parameters, - T const *data) const { + VecN operator()(T const* parameters, + T const* data) const { Vec3 R = ShotRotationFunctor(0, FUNCTOR_NOT_SET)(¶meters); Vec3 t = ShotPositionFunctor(0, FUNCTOR_NOT_SET)(¶meters); - const T *const scale = parameters + Similarity::Parameter::SCALE; + const T* const scale = parameters + Similarity::Parameter::SCALE; VecN transformed = Eigen::Map>(data).eval(); diff --git a/opensfm/src/bundle/data/camera.h b/opensfm/src/bundle/data/camera.h index 0966117ce..f5ecc4388 100644 --- a/opensfm/src/bundle/data/camera.h +++ b/opensfm/src/bundle/data/camera.h @@ -6,18 +6,18 @@ namespace bundle { struct Camera : public Data { - Camera(const std::string &id, const geometry::Camera &value, - const geometry::Camera &prior, const geometry::Camera &sigma) + Camera(const std::string& id, const geometry::Camera& value, + const geometry::Camera& prior, const geometry::Camera& sigma) : Data(id, value, prior, sigma) { Init(); } private: - void ValueToData(const geometry::Camera &value, VecXd &data) const final { + void ValueToData(const geometry::Camera& value, VecXd& data) const final { data = value.GetParametersValues(); } - void DataToValue(const VecXd &data, geometry::Camera &value) const final { + void DataToValue(const VecXd& data, geometry::Camera& value) const final { value.SetParametersValues(data); } }; diff --git a/opensfm/src/bundle/data/data.h b/opensfm/src/bundle/data/data.h index d54fa7bb0..8e49feb60 100644 --- a/opensfm/src/bundle/data/data.h +++ b/opensfm/src/bundle/data/data.h @@ -10,7 +10,7 @@ namespace bundle { struct DataNode { - explicit DataNode(const std::string &id) : id_(id) {} + explicit DataNode(const std::string& id) : id_(id) {} std::string GetID() const { return id_; } protected: @@ -22,12 +22,12 @@ struct Data : public DataNode { public: using ValueType = T; - Data(const std::string &id, const T &value, const T &prior, const T &sigma) + Data(const std::string& id, const T& value, const T& prior, const T& sigma) : DataNode(id), value_(value), prior_(prior), sigma_(sigma) {} - Data(const std::string &id, const T &value) : DataNode(id), value_(value) {} + Data(const std::string& id, const T& value) : DataNode(id), value_(value) {} virtual ~Data() {} - VecXd &GetValueData() { return value_data_; } + VecXd& GetValueData() { return value_data_; } T GetValue() const { T v = value_; DataToValue(value_data_, v); @@ -43,7 +43,7 @@ struct Data : public DataNode { ValueToData(prior_.Value(), prior_data); return prior_data; } - void SetPrior(const T &prior) { prior_.SetValue(prior); } + void SetPrior(const T& prior) { prior_.SetValue(prior); } VecXd GetSigmaData() const { if (!sigma_.HasValue()) { @@ -53,9 +53,9 @@ struct Data : public DataNode { ValueToData(sigma_.Value(), sigma_data); return sigma_data; } - void SetSigma(const T &sigma) { sigma_.SetValue(sigma); } + void SetSigma(const T& sigma) { sigma_.SetValue(sigma); } - void SetCovariance(const MatXd &covariance) { + void SetCovariance(const MatXd& covariance) { covariance_.SetValue(covariance); } bool HasCovariance() const { return covariance_.HasValue(); } @@ -66,15 +66,15 @@ struct Data : public DataNode { return covariance_.Value(); } - const std::vector &GetParametersToOptimize() const { + const std::vector& GetParametersToOptimize() const { return parameters_to_optimize_; } - void SetParametersToOptimize(const std::vector ¶meters) { + void SetParametersToOptimize(const std::vector& parameters) { parameters_to_optimize_ = parameters; } - virtual void ValueToData(const T &value, VecXd &data) const = 0; - virtual void DataToValue(const VecXd &data, T &value) const = 0; + virtual void ValueToData(const T& value, VecXd& data) const = 0; + virtual void DataToValue(const VecXd& data, T& value) const = 0; protected: VecXd value_data_; @@ -94,13 +94,13 @@ struct Data : public DataNode { }; struct DataContainer : public DataNode { - explicit DataContainer(const std::string &id) : DataNode(id) {} + explicit DataContainer(const std::string& id) : DataNode(id) {} protected: - void RegisterData(const std::string &id, DataNode *data) { + void RegisterData(const std::string& id, DataNode* data) { ba_nodes_[id] = data; } - DataNode *GetData(const std::string &id) { + DataNode* GetData(const std::string& id) { const auto find_data = ba_nodes_.find(id); if (find_data == ba_nodes_.end()) { throw std::runtime_error("Data " + id + @@ -108,7 +108,7 @@ struct DataContainer : public DataNode { } return find_data->second; } - const DataNode *GetData(const std::string &id) const { + const DataNode* GetData(const std::string& id) const { const auto find_data = ba_nodes_.find(id); if (find_data == ba_nodes_.end()) { throw std::runtime_error("Data " + id + @@ -118,6 +118,6 @@ struct DataContainer : public DataNode { } private: - std::unordered_map ba_nodes_; + std::unordered_map ba_nodes_; }; } // namespace bundle diff --git a/opensfm/src/bundle/data/point.h b/opensfm/src/bundle/data/point.h index 177ab978d..ad01d90a0 100644 --- a/opensfm/src/bundle/data/point.h +++ b/opensfm/src/bundle/data/point.h @@ -9,12 +9,12 @@ namespace bundle { struct Point : public Data { enum Parameter { PX, PY, PZ, NUM_PARAMS }; - Point(const std::string &id, const Vec3d &value) : Data(id, value) { + Point(const std::string& id, const Vec3d& value) : Data(id, value) { Init(); } - Point(const std::string &id, const Vec3d &value, const Vec3d &prior, - const Vec3d &sigma) + Point(const std::string& id, const Vec3d& value, const Vec3d& prior, + const Vec3d& sigma) : Data(id, value, prior, sigma) { Init(); } @@ -23,11 +23,11 @@ struct Point : public Data { bool has_altitude_prior{true}; private: - void ValueToData(const Vec3d &value, VecXd &data) const final { + void ValueToData(const Vec3d& value, VecXd& data) const final { data = value; } - void DataToValue(const VecXd &data, Vec3d &value) const final { + void DataToValue(const VecXd& data, Vec3d& value) const final { value = data; } }; diff --git a/opensfm/src/bundle/data/pose.h b/opensfm/src/bundle/data/pose.h index 1b512ab00..5b39465b5 100644 --- a/opensfm/src/bundle/data/pose.h +++ b/opensfm/src/bundle/data/pose.h @@ -16,22 +16,22 @@ struct Pose : public Data { enum Parameter { RX, RY, RZ, TX, TY, TZ, NUM_PARAMS }; - Pose(const std::string &id, const geometry::Pose &value, - const Parametrization ¶metrization = Parametrization::CAM_TO_WORLD) + Pose(const std::string& id, const geometry::Pose& value, + const Parametrization& parametrization = Parametrization::CAM_TO_WORLD) : Data(id, value), parametrization_(parametrization) { Init(); } - Pose(const std::string &id, const geometry::Pose &value, - const geometry::Pose &prior, const geometry::Pose &sigma, - const Parametrization ¶metrization = Parametrization::CAM_TO_WORLD) + Pose(const std::string& id, const geometry::Pose& value, + const geometry::Pose& prior, const geometry::Pose& sigma, + const Parametrization& parametrization = Parametrization::CAM_TO_WORLD) : Data(id, value, prior, sigma), parametrization_(parametrization) { Init(); } private: - void ValueToData(const geometry::Pose &value, VecXd &data) const final { + void ValueToData(const geometry::Pose& value, VecXd& data) const final { data.resize(NUM_PARAMS); if (parametrization_ == Parametrization::CAM_TO_WORLD) { data.segment<3>(TX) = value.TranslationCameraToWorld(); @@ -42,7 +42,7 @@ struct Pose : public Data { } } - void DataToValue(const VecXd &data, geometry::Pose &value) const final { + void DataToValue(const VecXd& data, geometry::Pose& value) const final { if (parametrization_ == Parametrization::CAM_TO_WORLD) { value.SetFromCameraToWorld(Vec3d(data.segment<3>(RX)), data.segment<3>(TX)); diff --git a/opensfm/src/bundle/data/shot.h b/opensfm/src/bundle/data/shot.h index 582672db8..a882af7bc 100644 --- a/opensfm/src/bundle/data/shot.h +++ b/opensfm/src/bundle/data/shot.h @@ -11,8 +11,8 @@ namespace bundle { using RigCamera = Pose; struct RigInstance : public Pose { - RigInstance(const std::string &id, const geometry::Pose &value, - const std::unordered_map &shot_cameras) + RigInstance(const std::string& id, const geometry::Pose& value, + const std::unordered_map& shot_cameras) : Pose(id, value, Parametrization::CAM_TO_WORLD), shot_cameras(shot_cameras) {} @@ -21,22 +21,22 @@ struct RigInstance : public Pose { }; struct Shot : public DataContainer { - Shot(const std::string &id, bundle::Camera *camera, RigCamera *rig_camera, - RigInstance *rig_instance) + Shot(const std::string& id, bundle::Camera* camera, RigCamera* rig_camera, + RigInstance* rig_instance) : DataContainer(id) { RegisterData("camera", camera); RegisterData("rig_camera", rig_camera); RegisterData("rig_instance", rig_instance); } - bundle::Camera *GetCamera() { - return static_cast(GetData("camera")); + bundle::Camera* GetCamera() { + return static_cast(GetData("camera")); } - RigCamera *GetRigCamera() { - return static_cast(GetData("rig_camera")); + RigCamera* GetRigCamera() { + return static_cast(GetData("rig_camera")); } - RigInstance *GetRigInstance() { - return static_cast(GetData("rig_instance")); + RigInstance* GetRigInstance() { + return static_cast(GetData("rig_instance")); } }; } // namespace bundle diff --git a/opensfm/src/bundle/error/absolute_motion_errors.h b/opensfm/src/bundle/error/absolute_motion_errors.h index e3bc62c5d..dae858b16 100644 --- a/opensfm/src/bundle/error/absolute_motion_errors.h +++ b/opensfm/src/bundle/error/absolute_motion_errors.h @@ -28,7 +28,7 @@ struct UpVectorError { } Vec3d acceleration_; - double scale_; + const double scale_; }; struct PanAngleError { @@ -53,8 +53,8 @@ struct PanAngleError { return true; } - double angle_; - double scale_; + const double angle_; + const double scale_; }; struct TiltAngleError { @@ -77,8 +77,8 @@ struct TiltAngleError { return true; } - double angle_; - double scale_; + const double angle_; + const double scale_; }; struct RollAngleError { @@ -93,7 +93,6 @@ struct RollAngleError { T ex[3] = {T(1), T(0), T(0)}; // A point to the right of the camera (x=1) T ez[3] = {T(0), T(0), T(1)}; // A point in front of the camera (z=1) T Rt_ex[3], Rt_ez[3]; - T tangle_ = T(angle_); ceres::AngleAxisRotatePoint(R.data(), ex, Rt_ex); ceres::AngleAxisRotatePoint(R.data(), ez, Rt_ez); @@ -122,8 +121,8 @@ struct RollAngleError { return true; } - double angle_; - double scale_; + const double angle_; + const double scale_; }; struct HeatmapdCostFunctor { @@ -180,12 +179,14 @@ struct TranslationPriorError { template bool operator()(const T* const rig_instance1, const T* const rig_instance2, T* residuals) const { - auto t1 = Eigen::Map>(rig_instance1 + Pose::Parameter::TX); - auto t2 = Eigen::Map>(rig_instance2 + Pose::Parameter::TX); + const auto t1 = + Eigen::Map>(rig_instance1 + Pose::Parameter::TX); + const auto t2 = + Eigen::Map>(rig_instance2 + Pose::Parameter::TX); residuals[0] = log((t1 - t2).norm() / T(prior_norm_)); return true; } - double prior_norm_; + const double prior_norm_; }; } // namespace bundle diff --git a/opensfm/src/bundle/error/autodiff_ceres_compat.h b/opensfm/src/bundle/error/autodiff_ceres_compat.h new file mode 100644 index 000000000..3df4c13c3 --- /dev/null +++ b/opensfm/src/bundle/error/autodiff_ceres_compat.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +// Compatibility overloads for Eigen::AutoDiffScalar with Ceres 2.2+ +// These are needed because Ceres rotation.h uses std::hypot and std::fpclassify +// which don't have overloads for AutoDiffScalar types + +namespace Eigen { +// 3-argument hypot for AutoDiffScalar (used in ceres/rotation.h) +template +inline AutoDiffScalar hypot(const AutoDiffScalar& x, + const AutoDiffScalar& y, + const AutoDiffScalar& z) { + return sqrt(x * x + y * y + z * z); +} +} // namespace Eigen + +namespace std { +// fpclassify for AutoDiffScalar (used in ceres/rotation.h) +template +inline int fpclassify(const Eigen::AutoDiffScalar& x) { + return std::fpclassify(x.value()); +} +} // namespace std diff --git a/opensfm/src/bundle/error/error_utils.h b/opensfm/src/bundle/error/error_utils.h index b038d37c5..f54387cb2 100644 --- a/opensfm/src/bundle/error/error_utils.h +++ b/opensfm/src/bundle/error/error_utils.h @@ -5,6 +5,7 @@ #include +#include "autodiff_ceres_compat.h" #include "ceres/ceres.h" #include "ceres/rotation.h" @@ -84,7 +85,7 @@ void WorldToCameraCoordinatesRig(const T* scale, const T* const rig_instance, } template -T DiffBetweenAngles(T a, T b) { +T DiffBetweenAngles(const T a, const T b) { T d = a - b; if (d > T(M_PI)) { return d - T(2 * M_PI); diff --git a/opensfm/src/bundle/error/motion_prior_errors.h b/opensfm/src/bundle/error/motion_prior_errors.h index 7e80c479f..83e4be9ae 100644 --- a/opensfm/src/bundle/error/motion_prior_errors.h +++ b/opensfm/src/bundle/error/motion_prior_errors.h @@ -37,20 +37,34 @@ struct LinearMotionError { Eigen::Map > residual(r); // Position residual : op is translation - residual.segment(0, 3) = - T(position_scale_) * (T(alpha_) * (t2 - t0) + (t0 - t1)); + const auto eps = T(1e-15); + const auto t2_t0 = (t2 - t0); + const auto t1_t0 = (t1 - t0); + const auto t2_t0_norm = t2_t0.norm(); + const auto t1_t0_norm = t1_t0.norm(); + for (int i = 0; i < 3; ++i) { + // in case of non-zero speed, use speed ratio as it isn't subject + // to collapse when used together with position variance estimation + if (t2_t0_norm > eps) { + residual(i) = + T(position_scale_) * (T(alpha_) - t1_t0_norm / t2_t0_norm); + // otherwise, use classic difference between speeds, so one can still + // drag away position if they're coincident + } else { + residual(i) = T(position_scale_) * (T(alpha_) * t2_t0(i) - t1_t0(i)); + } + } // Rotation residual : op is rotation const Vec3 R2_R0t = T(alpha_) * MultRotations(R2.eval(), (-R0).eval()); const Vec3 R0_R1t = MultRotations(R0.eval(), (-R1).eval()); residual.segment(3, 3) = - T(position_scale_) * MultRotations(R2_R0t.eval(), R0_R1t); + T(orientation_scale_) * MultRotations(R2_R0t.eval(), R0_R1t); return true; } - double alpha_; - Vec3d acceleration_; - double position_scale_; - double orientation_scale_; + const double alpha_; + const double position_scale_; + const double orientation_scale_; int shot0_rig_camera_index{FUNCTOR_NOT_SET}; int shot1_rig_camera_index{FUNCTOR_NOT_SET}; diff --git a/opensfm/src/bundle/error/parameters_errors.h b/opensfm/src/bundle/error/parameters_errors.h index 9b2613956..969b58a5f 100644 --- a/opensfm/src/bundle/error/parameters_errors.h +++ b/opensfm/src/bundle/error/parameters_errors.h @@ -8,7 +8,7 @@ struct StdDeviationConstraint { StdDeviationConstraint() = default; template - bool operator()(const T *const std_deviation, T *residuals) const { + bool operator()(const T* const std_deviation, T* residuals) const { T std = std_deviation[0]; residuals[0] = ceres::log(T(1.0) / ceres::sqrt(T(2.0 * M_PI) * std * std)); return true; @@ -20,7 +20,7 @@ struct ParameterBarrier { : lower_bound_(lower_bound), upper_bound_(upper_bound), index_(index) {} template - bool operator()(const T *const parameters, T *residuals) const { + bool operator()(const T* const parameters, T* residuals) const { T eps = T(1e-10); T value = parameters[index_]; T zero = 2.0 * ceres::log((T(upper_bound_) - T(lower_bound_)) * 0.5); @@ -30,8 +30,8 @@ struct ParameterBarrier { return true; } - double lower_bound_; - double upper_bound_; - int index_; + const double lower_bound_; + const double upper_bound_; + const int index_; }; } // namespace bundle diff --git a/opensfm/src/bundle/error/prior_error.h b/opensfm/src/bundle/error/prior_error.h index 695ee888e..40719fac1 100644 --- a/opensfm/src/bundle/error/prior_error.h +++ b/opensfm/src/bundle/error/prior_error.h @@ -107,7 +107,7 @@ struct DataPriorError { VecXd scales_; // Are scale being adjusted for (global multiplier) ? - bool adjust_scales_; + const bool adjust_scales_; static constexpr int parameter_index = 0; static constexpr int transform_index = 1; static constexpr int scale_index = 2; diff --git a/opensfm/src/bundle/error/projection_errors.h b/opensfm/src/bundle/error/projection_errors.h index a103d115a..20d6fdb87 100644 --- a/opensfm/src/bundle/error/projection_errors.h +++ b/opensfm/src/bundle/error/projection_errors.h @@ -22,10 +22,10 @@ class ReprojectionError { use_rig_camera_(use_rig_camera) {} protected: - geometry::ProjectionType type_; - Vec2d observed_; - double scale_; - bool use_rig_camera_; + const geometry::ProjectionType type_; + const Vec2d observed_; + const double scale_; + const bool use_rig_camera_; }; class ReprojectionError2D : public ReprojectionError { @@ -215,8 +215,8 @@ class ReprojectionError3D : public ReprojectionError { const Vec2d& observed, double std_deviation, bool use_rig_camera) : ReprojectionError(type, observed, std_deviation, use_rig_camera) { - double lon = observed[0] * 2 * M_PI; - double lat = -observed[1] * 2 * M_PI; + const double lon = observed[0] * 2 * M_PI; + const double lat = -observed[1] * 2 * M_PI; bearing_vector_[0] = std::cos(lat) * std::sin(lon); bearing_vector_[1] = -std::sin(lat); bearing_vector_[2] = std::cos(lat) * std::cos(lon); @@ -261,10 +261,10 @@ class ReprojectionError3DAnalytic Vec3d transformed; /* Error only */ if (!jacobians) { - geometry::PoseFunctor::Forward(point, rig_instance, &transformed[0]); + geometry::PoseFunctor::Forward(point, rig_instance, transformed.data()); if (use_rig_camera_) { - geometry::PoseFunctor::Forward(&transformed[0], rig_camera, - &transformed[0]); + geometry::PoseFunctor::Forward(transformed.data(), rig_camera, + transformed.data()); } transformed.normalize(); } /* Jacobian + Error */ diff --git a/opensfm/src/bundle/error/relative_depth_error.h b/opensfm/src/bundle/error/relative_depth_error.h new file mode 100644 index 000000000..b912206b5 --- /dev/null +++ b/opensfm/src/bundle/error/relative_depth_error.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace bundle { +class RelativeDepthError { + public: + constexpr static int Size = 1; + RelativeDepthError(double depth, double std_deviation, bool use_rig_camera, + bool is_radial_depth = true) + : depth_(depth), + scale_(1.0 / std_deviation), + use_rig_camera_(use_rig_camera), + is_radial_depth_(is_radial_depth) {} + + template + bool operator()(const T* const rig_instance, const T* const rig_camera, + const T* const point, T* residuals) const { + T scale_one = T(1.0); + Vec3 point_in_camera; + const T* const actual_rig_camera = use_rig_camera_ ? rig_camera : nullptr; + WorldToCameraCoordinatesRig(&scale_one, rig_instance, actual_rig_camera, + point, point_in_camera.data()); + + // The error is the difference between the predicted and observed depth + T depth_in_camera = point_in_camera[2]; + if (is_radial_depth_) { + depth_in_camera = point_in_camera.norm(); + } + residuals[0] = T(scale_) * (depth_in_camera - T(depth_)); + + return true; + } + + protected: + const double depth_; + const double scale_; + const bool use_rig_camera_; + const bool is_radial_depth_; +}; +} // namespace bundle diff --git a/opensfm/src/bundle/pybundle.pyi b/opensfm/src/bundle/pybundle.pyi index 4e53f78a8..53b40a4ab 100644 --- a/opensfm/src/bundle/pybundle.pyi +++ b/opensfm/src/bundle/pybundle.pyi @@ -2,44 +2,103 @@ # Do not manually edit # To regenerate: # $ buck run //mapillary/opensfm/opensfm/src/bundle:pybundle_stubgen -# Use proper mode, e.g. @arvr/mode/linux/dev for arvr # @generated +# +# Tip: Be sure to run this with the build mode you use for your project, e.g., +# @//arvr/mode/linux/opt (or dev) in arvr. +# +# Ignore errors for [24] untyped generics. +# pyre-ignore-all-errors[24] import numpy import opensfm.pygeometry +import opensfm.pymap from typing import * -__all__ = [ -"BundleAdjuster", -"Point", -"RAReconstruction", -"RARelativeMotionConstraint", -"RAShot", -"Reconstruction", -"ReconstructionAlignment", -"RelativeMotion", -"RelativeRotation" + +__all__ = [ + "BundleAdjuster", + "Point", + "RAReconstruction", + "RARelativeMotionConstraint", + "RAShot", + "Reconstruction", + "ReconstructionAlignment", + "RelativeMotion", + "RelativeRotation", + "StaticExtensionLoader", ] + class BundleAdjuster: def __init__(self) -> None: ... def add_absolute_pan(self, arg0: str, arg1: float, arg2: float) -> None: ... - def add_absolute_position_heatmap(self, arg0: str, arg1: str, arg2: float, arg3: float, arg4: float) -> None: ... + def add_absolute_position_heatmap( + self, arg0: str, arg1: str, arg2: float, arg3: float, arg4: float + ) -> None: ... def add_absolute_roll(self, arg0: str, arg1: float, arg2: float) -> None: ... def add_absolute_tilt(self, arg0: str, arg1: float, arg2: float) -> None: ... - def add_absolute_up_vector(self, arg0: str, arg1: numpy.ndarray, arg2: float) -> None: ... - def add_camera(self, arg0: str, arg1: opensfm.pygeometry.Camera, arg2: opensfm.pygeometry.Camera, arg3: bool) -> None: ... - def add_common_position(self, arg0: str, arg1: str, arg2: float, arg3: float) -> None: ... - def add_heatmap(self, arg0: str, arg1: List[float], arg2: int, arg3: float) -> None: ... - def add_linear_motion(self, arg0: str, arg1: str, arg2: str, arg3: float, arg4: float, arg5: float) -> None: ... - def add_point(self, arg0: str, arg1: numpy.ndarray, arg2: bool) -> None: ... - def add_point_prior(self, arg0: str, arg1: numpy.ndarray, arg2: numpy.ndarray, arg3: bool) -> None: ... - def add_point_projection_observation(self, arg0: str, arg1: str, arg2: numpy.ndarray, arg3: float) -> None: ... + def add_absolute_up_vector( + self, arg0: str, arg1: numpy.typing.NDArray, arg2: float + ) -> None: ... + def add_camera( + self, + arg0: str, + arg1: opensfm.pygeometry.Camera, + arg2: opensfm.pygeometry.Camera, + arg3: bool, + ) -> None: ... + def add_common_position( + self, arg0: str, arg1: str, arg2: float, arg3: float + ) -> None: ... + def add_heatmap( + self, arg0: str, arg1: list[float], arg2: int, arg3: float + ) -> None: ... + def add_linear_motion( + self, arg0: str, arg1: str, arg2: str, arg3: float, arg4: float, arg5: float + ) -> None: ... + def add_point(self, arg0: str, arg1: numpy.typing.NDArray, arg2: bool) -> None: ... + def add_point_prior( + self, + arg0: str, + arg1: numpy.typing.NDArray, + arg2: numpy.typing.NDArray, + arg3: bool, + ) -> None: ... + def add_point_projection_observation( + self, + shot: str, + point: str, + observation: numpy.typing.NDArray, + std_deviation: float, + depth_prior: Optional[opensfm.pymap.Depth] = None, + ) -> None: ... def add_reconstruction(self, arg0: str, arg1: bool) -> None: ... - def add_reconstruction_instance(self, arg0: str, arg1: float, arg2: str) -> None: ... + def add_reconstruction_instance( + self, arg0: str, arg1: float, arg2: str + ) -> None: ... def add_relative_motion(self, arg0: RelativeMotion) -> None: ... def add_relative_rotation(self, arg0: RelativeRotation) -> None: ... - def add_rig_camera(self, arg0: str, arg1: opensfm.pygeometry.Pose, arg2: opensfm.pygeometry.Pose, arg3: bool) -> None: ... - def add_rig_instance(self, arg0: str, arg1: opensfm.pygeometry.Pose, arg2: Dict[str, str], arg3: Dict[str, str], arg4: bool) -> None: ... - def add_rig_instance_position_prior(self, arg0: str, arg1: numpy.ndarray, arg2: numpy.ndarray, arg3: str) -> None: ... + def add_rig_camera( + self, + arg0: str, + arg1: opensfm.pygeometry.Pose, + arg2: opensfm.pygeometry.Pose, + arg3: bool, + ) -> None: ... + def add_rig_instance( + self, + arg0: str, + arg1: opensfm.pygeometry.Pose, + arg2: dict[str, str], + arg3: dict[str, str], + arg4: bool, + ) -> None: ... + def add_rig_instance_position_prior( + self, + arg0: str, + arg1: numpy.typing.NDArray, + arg2: numpy.typing.NDArray, + arg3: str, + ) -> None: ... def brief_report(self) -> str: ... def full_report(self) -> str: ... def get_camera(self, arg0: str) -> opensfm.pygeometry.Camera: ... @@ -54,7 +113,18 @@ class BundleAdjuster: def set_compute_covariances(self, arg0: bool) -> None: ... def set_compute_reprojection_errors(self, arg0: bool) -> None: ... def set_gauge_fix_shots(self, arg0: str, arg1: str) -> None: ... - def set_internal_parameters_prior_sd(self, arg0: float, arg1: float, arg2: float, arg3: float, arg4: float, arg5: float, arg6: float, arg7: float) -> None: ... + def set_internal_parameters_prior_sd( + self, + focal_sd: float, + aspect_ratio_sd: float, + c_sd: float, + k1_sd: float, + k2_sd: float, + p1_sd: float, + p2_sd: float, + k3_sd: float, + k4_sd: float, + ) -> None: ... def set_linear_solver_type(self, arg0: str) -> None: ... def set_max_num_iterations(self, arg0: int) -> None: ... def set_num_threads(self, arg0: int) -> None: ... @@ -62,159 +132,229 @@ class BundleAdjuster: def set_relative_motion_loss_function(self, arg0: str, arg1: float) -> None: ... def set_scale_sharing(self, arg0: str, arg1: bool) -> None: ... def set_use_analytic_derivatives(self, arg0: bool) -> None: ... + class Point: @property - def id(self) -> str:... + def id(self) -> str: ... @property - def p(self) -> numpy.ndarray:... + def p(self) -> numpy.typing.NDArray: ... @property - def reprojection_errors(self) -> Dict[str, numpy.ndarray]:... + def reprojection_errors(self) -> dict[str, numpy.typing.NDArray]: ... @reprojection_errors.setter - def reprojection_errors(self, arg0: Dict[str, numpy.ndarray]) -> None:... + def reprojection_errors(self, arg0: dict[str, numpy.typing.NDArray]) -> None: ... + class RAReconstruction: def __init__(self) -> None: ... @property - def id(self) -> str:... + def id(self) -> str: ... @id.setter - def id(self, arg0: str) -> None:... + def id(self, arg0: str) -> None: ... @property - def rx(self) -> float:... + def rx(self) -> float: ... @rx.setter - def rx(self, arg1: float) -> None:... + def rx(self, arg1: float) -> None: ... @property - def ry(self) -> float:... + def ry(self) -> float: ... @ry.setter - def ry(self, arg1: float) -> None:... + def ry(self, arg1: float) -> None: ... @property - def rz(self) -> float:... + def rz(self) -> float: ... @rz.setter - def rz(self, arg1: float) -> None:... + def rz(self, arg1: float) -> None: ... @property - def scale(self) -> float:... + def scale(self) -> float: ... @scale.setter - def scale(self, arg1: float) -> None:... + def scale(self, arg1: float) -> None: ... @property - def tx(self) -> float:... + def tx(self) -> float: ... @tx.setter - def tx(self, arg1: float) -> None:... + def tx(self, arg1: float) -> None: ... @property - def ty(self) -> float:... + def ty(self) -> float: ... @ty.setter - def ty(self, arg1: float) -> None:... + def ty(self, arg1: float) -> None: ... @property - def tz(self) -> float:... + def tz(self) -> float: ... @tz.setter - def tz(self, arg1: float) -> None:... + def tz(self, arg1: float) -> None: ... + class RARelativeMotionConstraint: - def __init__(self, arg0: str, arg1: str, arg2: float, arg3: float, arg4: float, arg5: float, arg6: float, arg7: float) -> None: ... + def __init__( + self, + arg0: str, + arg1: str, + arg2: float, + arg3: float, + arg4: float, + arg5: float, + arg6: float, + arg7: float, + ) -> None: ... def set_scale_matrix(self, arg0: int, arg1: int, arg2: float) -> None: ... @property - def reconstruction(self) -> str:... + def reconstruction(self) -> str: ... @reconstruction.setter - def reconstruction(self, arg0: str) -> None:... + def reconstruction(self, arg0: str) -> None: ... @property - def rx(self) -> float:... + def rx(self) -> float: ... @rx.setter - def rx(self, arg1: float) -> None:... + def rx(self, arg1: float) -> None: ... @property - def ry(self) -> float:... + def ry(self) -> float: ... @ry.setter - def ry(self, arg1: float) -> None:... + def ry(self, arg1: float) -> None: ... @property - def rz(self) -> float:... + def rz(self) -> float: ... @rz.setter - def rz(self, arg1: float) -> None:... + def rz(self, arg1: float) -> None: ... @property - def shot(self) -> str:... + def shot(self) -> str: ... @shot.setter - def shot(self, arg0: str) -> None:... + def shot(self, arg0: str) -> None: ... @property - def tx(self) -> float:... + def tx(self) -> float: ... @tx.setter - def tx(self, arg1: float) -> None:... + def tx(self, arg1: float) -> None: ... @property - def ty(self) -> float:... + def ty(self) -> float: ... @ty.setter - def ty(self, arg1: float) -> None:... + def ty(self, arg1: float) -> None: ... @property - def tz(self) -> float:... + def tz(self) -> float: ... @tz.setter - def tz(self, arg1: float) -> None:... + def tz(self, arg1: float) -> None: ... + class RAShot: def __init__(self) -> None: ... @property - def id(self) -> str:... + def id(self) -> str: ... @id.setter - def id(self, arg0: str) -> None:... + def id(self, arg0: str) -> None: ... @property - def rx(self) -> float:... + def rx(self) -> float: ... @rx.setter - def rx(self, arg1: float) -> None:... + def rx(self, arg1: float) -> None: ... @property - def ry(self) -> float:... + def ry(self) -> float: ... @ry.setter - def ry(self, arg1: float) -> None:... + def ry(self, arg1: float) -> None: ... @property - def rz(self) -> float:... + def rz(self) -> float: ... @rz.setter - def rz(self, arg1: float) -> None:... + def rz(self, arg1: float) -> None: ... @property - def tx(self) -> float:... + def tx(self) -> float: ... @tx.setter - def tx(self, arg1: float) -> None:... + def tx(self, arg1: float) -> None: ... @property - def ty(self) -> float:... + def ty(self) -> float: ... @ty.setter - def ty(self, arg1: float) -> None:... + def ty(self, arg1: float) -> None: ... @property - def tz(self) -> float:... + def tz(self) -> float: ... @tz.setter - def tz(self, arg1: float) -> None:... + def tz(self, arg1: float) -> None: ... + class Reconstruction: def __init__(self) -> None: ... def get_scale(self, arg0: str) -> float: ... def set_scale(self, arg0: str, arg1: float) -> None: ... @property - def id(self) -> str:... + def id(self) -> str: ... @id.setter - def id(self, arg0: str) -> None:... + def id(self, arg0: str) -> None: ... + class ReconstructionAlignment: def __init__(self) -> None: ... - def add_absolute_position_constraint(self, arg0: str, arg1: float, arg2: float, arg3: float, arg4: float) -> None: ... - def add_common_camera_constraint(self, arg0: str, arg1: str, arg2: str, arg3: str, arg4: float, arg5: float) -> None: ... - def add_common_point_constraint(self, arg0: str, arg1: float, arg2: float, arg3: float, arg4: str, arg5: float, arg6: float, arg7: float, arg8: float) -> None: ... - def add_reconstruction(self, arg0: str, arg1: float, arg2: float, arg3: float, arg4: float, arg5: float, arg6: float, arg7: float, arg8: bool) -> None: ... - def add_relative_absolute_position_constraint(self, arg0: str, arg1: str, arg2: float, arg3: float, arg4: float, arg5: float) -> None: ... - def add_relative_motion_constraint(self, arg0: RARelativeMotionConstraint) -> None: ... - def add_shot(self, arg0: str, arg1: float, arg2: float, arg3: float, arg4: float, arg5: float, arg6: float, arg7: bool) -> None: ... + def add_absolute_position_constraint( + self, arg0: str, arg1: float, arg2: float, arg3: float, arg4: float + ) -> None: ... + def add_common_camera_constraint( + self, arg0: str, arg1: str, arg2: str, arg3: str, arg4: float, arg5: float + ) -> None: ... + def add_common_point_constraint( + self, + arg0: str, + arg1: float, + arg2: float, + arg3: float, + arg4: str, + arg5: float, + arg6: float, + arg7: float, + arg8: float, + ) -> None: ... + def add_reconstruction( + self, + arg0: str, + arg1: float, + arg2: float, + arg3: float, + arg4: float, + arg5: float, + arg6: float, + arg7: float, + arg8: bool, + ) -> None: ... + def add_relative_absolute_position_constraint( + self, arg0: str, arg1: str, arg2: float, arg3: float, arg4: float, arg5: float + ) -> None: ... + def add_relative_motion_constraint( + self, arg0: RARelativeMotionConstraint + ) -> None: ... + def add_shot( + self, + arg0: str, + arg1: float, + arg2: float, + arg3: float, + arg4: float, + arg5: float, + arg6: float, + arg7: bool, + ) -> None: ... def brief_report(self) -> str: ... def full_report(self) -> str: ... def get_reconstruction(self, arg0: str) -> RAReconstruction: ... def get_shot(self, arg0: str) -> RAShot: ... def run(self) -> None: ... + class RelativeMotion: - def __init__(self, arg0: str, arg1: str, arg2: numpy.ndarray, arg3: numpy.ndarray, arg4: float, arg5: float, arg6: bool) -> None: ... - def set_scale_matrix(self, arg0: numpy.ndarray) -> None: ... - @property - def rig_instance_i(self) -> str:... + def __init__( + self, + arg0: str, + arg1: str, + arg2: numpy.typing.NDArray, + arg3: numpy.typing.NDArray, + arg4: float, + arg5: float, + arg6: bool, + ) -> None: ... + def set_scale_matrix(self, arg0: numpy.typing.NDArray) -> None: ... + @property + def rig_instance_i(self) -> str: ... @rig_instance_i.setter - def rig_instance_i(self, arg0: str) -> None:... + def rig_instance_i(self, arg0: str) -> None: ... @property - def rig_instance_j(self) -> str:... + def rig_instance_j(self) -> str: ... @rig_instance_j.setter - def rig_instance_j(self, arg0: str) -> None:... + def rig_instance_j(self, arg0: str) -> None: ... + class RelativeRotation: - def __init__(self, arg0: str, arg1: str, arg2: numpy.ndarray) -> None: ... - def set_scale_matrix(self, arg0: numpy.ndarray) -> None: ... + def __init__(self, arg0: str, arg1: str, arg2: numpy.typing.NDArray) -> None: ... + def set_scale_matrix(self, arg0: numpy.typing.NDArray) -> None: ... @property - def r(self) -> numpy.ndarray:... + def r(self) -> numpy.typing.NDArray: ... @r.setter - def r(self, arg1: numpy.ndarray) -> None:... + def r(self, arg1: numpy.typing.NDArray) -> None: ... @property - def shot_i(self) -> str:... + def shot_i(self) -> str: ... @shot_i.setter - def shot_i(self, arg0: str) -> None:... + def shot_i(self, arg0: str) -> None: ... @property - def shot_j(self) -> str:... + def shot_j(self) -> str: ... @shot_j.setter - def shot_j(self, arg0: str) -> None:... + def shot_j(self, arg0: str) -> None: ... + +class StaticExtensionLoader: + pass diff --git a/opensfm/src/bundle/python/pybind.cc b/opensfm/src/bundle/python/pybind.cc index c8c63e4cd..9c8372786 100644 --- a/opensfm/src/bundle/python/pybind.cc +++ b/opensfm/src/bundle/python/pybind.cc @@ -7,10 +7,11 @@ PYBIND11_MODULE(pybundle, m) { py::module::import("opensfm.pygeometry"); + py::module::import("opensfm.pymap"); py::class_(m, "RelativeMotion") - .def(py::init()) .def_readwrite("rig_instance_i", &bundle::RelativeMotion::rig_instance_id_i) @@ -19,8 +20,8 @@ PYBIND11_MODULE(pybundle, m) { .def("set_scale_matrix", &bundle::RelativeMotion::SetScaleMatrix); py::class_(m, "RelativeRotation") - .def(py::init()) + .def(py::init()) .def_readwrite("shot_i", &bundle::RelativeRotation::shot_id_i) .def_readwrite("shot_j", &bundle::RelativeRotation::shot_id_j) .def_property("r", &bundle::RelativeRotation::GetRotation, @@ -35,11 +36,12 @@ PYBIND11_MODULE(pybundle, m) { py::class_(m, "Point") .def_property_readonly( - "p", [](const bundle::Point &p) { return p.GetValue(); }) + "p", [](const bundle::Point& p) { return p.GetValue(); }) .def_property_readonly("id", - [](const bundle::Point &p) { return p.GetID(); }) + [](const bundle::Point& p) { return p.GetID(); }) .def_readwrite("reprojection_errors", &bundle::Point::reprojection_errors); + py::class_(m, "BundleAdjuster") .def(py::init()) .def("run", &bundle::BundleAdjuster::Run, @@ -52,21 +54,22 @@ PYBIND11_MODULE(pybundle, m) { .def("get_camera", &bundle::BundleAdjuster::GetCamera) .def("add_rig_camera", &bundle::BundleAdjuster::AddRigCamera) .def("get_rig_camera_pose", - [](const bundle::BundleAdjuster &ba, - const std::string &rig_camera_id) { + [](const bundle::BundleAdjuster& ba, + const std::string& rig_camera_id) { return ba.GetRigCamera(rig_camera_id).GetValue(); }) .def("add_rig_instance", &bundle::BundleAdjuster::AddRigInstance) .def("get_rig_instance_pose", - [](const bundle::BundleAdjuster &ba, - const std::string &rig_instance_id) { + [](const bundle::BundleAdjuster& ba, + const std::string& rig_instance_id) { return ba.GetRigInstance(rig_instance_id).GetValue(); }) .def("add_rig_instance_position_prior", &bundle::BundleAdjuster::AddRigInstancePositionPrior) .def("set_scale_sharing", &bundle::BundleAdjuster::SetScaleSharing) .def("get_reconstruction", &bundle::BundleAdjuster::GetReconstruction) - .def("add_point", &bundle::BundleAdjuster::AddPoint) + .def("add_point", &bundle::BundleAdjuster::AddPoint, + py::return_value_policy::reference) .def("add_point_prior", &bundle::BundleAdjuster::AddPointPrior) .def("get_point", &bundle::BundleAdjuster::GetPoint) .def("has_point", &bundle::BundleAdjuster::HasPoint) @@ -74,7 +77,9 @@ PYBIND11_MODULE(pybundle, m) { .def("add_reconstruction_instance", &bundle::BundleAdjuster::AddReconstructionInstance) .def("add_point_projection_observation", - &bundle::BundleAdjuster::AddPointProjectionObservation) + &bundle::BundleAdjuster::AddPointProjectionObservation, + py::arg("shot"), py::arg("point"), py::arg("observation"), + py::arg("std_deviation"), py::arg("depth_prior") = std::nullopt) .def("add_relative_motion", &bundle::BundleAdjuster::AddRelativeMotion) .def("add_relative_rotation", &bundle::BundleAdjuster::AddRelativeRotation) @@ -90,7 +95,10 @@ PYBIND11_MODULE(pybundle, m) { .def("add_linear_motion", &bundle::BundleAdjuster::AddLinearMotion) .def("set_gauge_fix_shots", &bundle::BundleAdjuster::SetGaugeFixShots) .def("set_internal_parameters_prior_sd", - &bundle::BundleAdjuster::SetInternalParametersPriorSD) + &bundle::BundleAdjuster::SetInternalParametersPriorSD, + py::arg("focal_sd"), py::arg("aspect_ratio_sd"), py::arg("c_sd"), + py::arg("k1_sd"), py::arg("k2_sd"), py::arg("p1_sd"), + py::arg("p2_sd"), py::arg("k3_sd"), py::arg("k4_sd")) .def("set_compute_covariances", &bundle::BundleAdjuster::SetComputeCovariances) .def("get_covariance_estimation_valid", @@ -155,7 +163,7 @@ PYBIND11_MODULE(pybundle, m) { .def_readwrite("id", &RAReconstruction::id); py::class_(m, "RARelativeMotionConstraint") - .def(py::init()) .def_readwrite("reconstruction", &RARelativeMotionConstraint::reconstruction_id) diff --git a/opensfm/src/bundle/reconstruction_alignment.h b/opensfm/src/bundle/reconstruction_alignment.h index 8b108401a..6cb01fadb 100644 --- a/opensfm/src/bundle/reconstruction_alignment.h +++ b/opensfm/src/bundle/reconstruction_alignment.h @@ -75,8 +75,8 @@ struct RAReconstruction { }; struct RARelativeMotionConstraint { - RARelativeMotionConstraint(const std::string &reconstruction, - const std::string &shot, double rx, double ry, + RARelativeMotionConstraint(const std::string& reconstruction, + const std::string& shot, double rx, double ry, double rz, double tx, double ty, double tz) { reconstruction_id = reconstruction; shot_id = shot; @@ -115,51 +115,51 @@ struct RARelativeMotionConstraint { }; struct RAAbsolutePositionConstraint { - RAShot *shot; + RAShot* shot; double position[3]; double std_deviation; }; struct RARelativeAbsolutePositionConstraint { - RAShot *shot; - RAReconstruction *reconstruction; + RAShot* shot; + RAReconstruction* reconstruction; double position[3]; double std_deviation; }; struct RACommonCameraConstraint { - RAReconstruction *reconstruction_a; - RAShot *shot_a; - RAReconstruction *reconstruction_b; - RAShot *shot_b; + RAReconstruction* reconstruction_a; + RAShot* shot_a; + RAReconstruction* reconstruction_b; + RAShot* shot_b; double std_deviation_center; double std_deviation_rotation; }; struct RACommonPointConstraint { - RAReconstruction *reconstruction_a; + RAReconstruction* reconstruction_a; double point_a[3]; - RAReconstruction *reconstruction_b; + RAReconstruction* reconstruction_b; double point_b[3]; double std_deviation; }; struct RARelativeMotionError { - RARelativeMotionError(double *Rtai, double *scale_matrix) + RARelativeMotionError(double* Rtai, double* scale_matrix) : Rtai_(Rtai), scale_matrix_(scale_matrix) {} template - bool operator()(const T *const reconstruction_a, const T *const shot_i, - T *residuals) const { + bool operator()(const T* const reconstruction_a, const T* const shot_i, + T* residuals) const { T r[6]; // Get rotation and translation values. - const T *const Ri = shot_i + RA_SHOT_RX; - const T *const Ra = reconstruction_a + RA_RECONSTRUCTION_RX; + const T* const Ri = shot_i + RA_SHOT_RX; + const T* const Ra = reconstruction_a + RA_RECONSTRUCTION_RX; T Rit[3] = {-Ri[0], -Ri[1], -Ri[2]}; - const T *const ti = shot_i + RA_SHOT_TX; - const T *const ta = reconstruction_a + RA_RECONSTRUCTION_TX; - const T *const scale_a = reconstruction_a + RA_RECONSTRUCTION_SCALE; + const T* const ti = shot_i + RA_SHOT_TX; + const T* const ta = reconstruction_a + RA_RECONSTRUCTION_TX; + const T* const scale_a = reconstruction_a + RA_RECONSTRUCTION_SCALE; T Rai[3] = {T(Rtai_[RA_SHOT_RX]), T(Rtai_[RA_SHOT_RY]), T(Rtai_[RA_SHOT_RZ])}; T tai[3] = {T(Rtai_[RA_SHOT_TX]), T(Rtai_[RA_SHOT_TY]), @@ -199,19 +199,19 @@ struct RARelativeMotionError { return true; } - double *Rtai_; - double *scale_matrix_; + double* Rtai_; + double* scale_matrix_; }; struct RAAbsolutePositionError { - RAAbsolutePositionError(double *c_prior, double std_deviation) + RAAbsolutePositionError(double* c_prior, double std_deviation) : c_prior_(c_prior), scale_(1.0 / std_deviation) {} template - bool operator()(const T *const shot_i, T *residuals) const { + bool operator()(const T* const shot_i, T* residuals) const { // error: c + R^t t - const T *const Ri = shot_i + RA_SHOT_RX; - const T *const ti = shot_i + RA_SHOT_TX; + const T* const Ri = shot_i + RA_SHOT_RX; + const T* const ti = shot_i + RA_SHOT_TX; T Rit[3] = {-Ri[0], -Ri[1], -Ri[2]}; T Rit_ti[3]; @@ -224,16 +224,16 @@ struct RAAbsolutePositionError { return true; } - double *c_prior_; + double* c_prior_; double scale_; }; template -void transform_point(const T *const reconstruction, double *point, - T *transformed) { - const T *const R = reconstruction + RA_RECONSTRUCTION_RX; - const T *const t = reconstruction + RA_RECONSTRUCTION_TX; - const T &scale = reconstruction[RA_RECONSTRUCTION_SCALE]; +void transform_point(const T* const reconstruction, double* point, + T* transformed) { + const T* const R = reconstruction + RA_RECONSTRUCTION_RX; + const T* const t = reconstruction + RA_RECONSTRUCTION_TX; + const T& scale = reconstruction[RA_RECONSTRUCTION_SCALE]; T p_t_s[3] = {(T(point[0]) - t[0]) / scale, (T(point[1]) - t[1]) / scale, (T(point[2]) - t[2]) / scale}; T Rt[3] = {-R[0], -R[1], -R[2]}; @@ -241,15 +241,15 @@ void transform_point(const T *const reconstruction, double *point, } struct RARelativeAbsolutePositionError { - RARelativeAbsolutePositionError(double *c_prior, double *shot, + RARelativeAbsolutePositionError(double* c_prior, double* shot, double std_deviation) : c_prior_(c_prior), shot_(shot), scale_(1.0 / std_deviation) {} template - bool operator()(const T *const reconstruction, T *residuals) const { + bool operator()(const T* const reconstruction, T* residuals) const { // error: c - transform(-R^t t) - const double *const Ri = shot_ + RA_SHOT_RX; - const double *const ti = shot_ + RA_SHOT_TX; + const double* const Ri = shot_ + RA_SHOT_RX; + const double* const ti = shot_ + RA_SHOT_TX; double Rit[3] = {-Ri[0], -Ri[1], -Ri[2]}; double Rit_ti[3]; @@ -266,18 +266,18 @@ struct RARelativeAbsolutePositionError { return true; } - double *c_prior_; - double *shot_; + double* c_prior_; + double* shot_; double scale_; }; struct RACommonPointError { - RACommonPointError(double *pai, double *pbi, double std_deviation) + RACommonPointError(double* pai, double* pbi, double std_deviation) : pai_(pai), pbi_(pbi), inv_std_(1.0 / std_deviation) {} template - bool operator()(const T *const reconstruction_a, - const T *const reconstruction_b, T *residuals) const { + bool operator()(const T* const reconstruction_a, + const T* const reconstruction_b, T* residuals) const { T transformed_pai[3]; transform_point(reconstruction_a, pai_, transformed_pai); @@ -297,13 +297,13 @@ struct RACommonPointError { return true; } - double *pai_; - double *pbi_; + double* pai_; + double* pbi_; double inv_std_; }; struct RACommonCameraError { - RACommonCameraError(double *shot_ai, double *shot_bi, + RACommonCameraError(double* shot_ai, double* shot_bi, double std_deviation_center, double std_deviation_rotation) : shot_ai_(shot_ai), @@ -312,12 +312,12 @@ struct RACommonCameraError { inv_std_rotation_(1.0 / std_deviation_rotation) {} template - bool operator()(const T *const reconstruction_a, - const T *const reconstruction_b, T *residuals) const { - const double *const rotation_ai = shot_ai_ + RA_SHOT_RX; - const double *const rotation_bi = shot_bi_ + RA_SHOT_RX; - const double *const translation_ai = shot_ai_ + RA_SHOT_TX; - const double *const translation_bi = shot_bi_ + RA_SHOT_TX; + bool operator()(const T* const reconstruction_a, + const T* const reconstruction_b, T* residuals) const { + const double* const rotation_ai = shot_ai_ + RA_SHOT_RX; + const double* const rotation_bi = shot_bi_ + RA_SHOT_RX; + const double* const translation_ai = shot_ai_ + RA_SHOT_TX; + const double* const translation_bi = shot_bi_ + RA_SHOT_TX; double pose_ai[3], pose_bi[3]; const double rotation_ait[3] = {-rotation_ai[0], -rotation_ai[1], @@ -339,8 +339,8 @@ struct RACommonCameraError { // rotation error in world coordinates : // (Ra^t Rai^t)(^T)(Rb^t Rbi^t) = (Rai Ra)(Rb^t Rbi^t) - const T *const Ra = reconstruction_a + RA_RECONSTRUCTION_RX; - const T *const Rb = reconstruction_b + RA_RECONSTRUCTION_RX; + const T* const Ra = reconstruction_a + RA_RECONSTRUCTION_RX; + const T* const Rb = reconstruction_b + RA_RECONSTRUCTION_RX; const T Rbt[3] = {-Rb[0], -Rb[1], -Rb[2]}; const T Rbit[3] = {T(-rotation_bi[0]), T(-rotation_bi[1]), T(-rotation_bi[2])}; @@ -367,24 +367,24 @@ struct RACommonCameraError { return true; } - double *shot_ai_; - double *shot_bi_; + double* shot_ai_; + double* shot_bi_; double inv_std_center_; double inv_std_rotation_; }; class ReconstructionAlignment { public: - ReconstructionAlignment() {} - virtual ~ReconstructionAlignment() {} + ReconstructionAlignment() = default; + virtual ~ReconstructionAlignment() = default; - RAShot GetShot(const std::string &id) { return shots_[id]; } + RAShot GetShot(const std::string& id) { return shots_[id]; } - RAReconstruction GetReconstruction(const std::string &id) { + RAReconstruction GetReconstruction(const std::string& id) { return reconstructions_[id]; } - void AddShot(const std::string &id, double rx, double ry, double rz, + void AddShot(const std::string& id, double rx, double ry, double rz, double tx, double ty, double tz, bool constant) { RAShot s; s.id = id; @@ -398,7 +398,7 @@ class ReconstructionAlignment { shots_[id] = s; } - void AddReconstruction(const std::string &id, double rx, double ry, double rz, + void AddReconstruction(const std::string& id, double rx, double ry, double rz, double tx, double ty, double tz, double scale, bool constant) { RAReconstruction r; @@ -414,11 +414,11 @@ class ReconstructionAlignment { reconstructions_[id] = r; } - void AddRelativeMotionConstraint(const RARelativeMotionConstraint &rm) { + void AddRelativeMotionConstraint(const RARelativeMotionConstraint& rm) { relative_motions_.push_back(rm); } - void AddAbsolutePositionConstraint(const std::string &shot_id, double x, + void AddAbsolutePositionConstraint(const std::string& shot_id, double x, double y, double z, double std_deviation) { RAAbsolutePositionConstraint a; a.shot = &shots_[shot_id]; @@ -430,7 +430,7 @@ class ReconstructionAlignment { } void AddRelativeAbsolutePositionConstraint( - const std::string &reconstruction_id, const std::string &shot_id, + const std::string& reconstruction_id, const std::string& shot_id, double x, double y, double z, double std_deviation) { RARelativeAbsolutePositionConstraint a; a.reconstruction = &reconstructions_[reconstruction_id]; @@ -442,9 +442,9 @@ class ReconstructionAlignment { relative_absolute_positions_.push_back(a); } - void AddCommonPointConstraint(const std::string &reconstruction_a_id, + void AddCommonPointConstraint(const std::string& reconstruction_a_id, double xa, double ya, double za, - const std::string &reconstruction_b_id, + const std::string& reconstruction_b_id, double xb, double yb, double zb, double std_deviation) { RACommonPointConstraint c; @@ -460,10 +460,10 @@ class ReconstructionAlignment { common_points_.push_back(c); } - void AddCommonCameraConstraint(const std::string &reconstruction_a_id, - const std::string &shot_a_id, - const std::string &reconstruction_b_id, - const std::string &shot_b_id, + void AddCommonCameraConstraint(const std::string& reconstruction_a_id, + const std::string& shot_a_id, + const std::string& reconstruction_b_id, + const std::string& shot_b_id, double std_deviation_center, double std_deviation_rotation) { RACommonCameraConstraint c; @@ -480,14 +480,14 @@ class ReconstructionAlignment { ceres::Problem problem; // Init parameter blocks. - for (auto &i : shots_) { + for (auto& i : shots_) { if (i.second.constant) { problem.AddParameterBlock(i.second.parameters, RA_SHOT_NUM_PARAMS); problem.SetParameterBlockConstant(i.second.parameters); } } - for (auto &i : reconstructions_) { + for (auto& i : reconstructions_) { problem.AddParameterBlock(i.second.parameters, RA_RECONSTRUCTION_NUM_PARAMS); if (i.second.constant) { @@ -502,9 +502,9 @@ class ReconstructionAlignment { } // Add relative motion errors - ceres::LossFunction *loss = new ceres::CauchyLoss(1.0); - for (auto &rp : relative_motions_) { - ceres::CostFunction *cost_function = + ceres::LossFunction* loss = new ceres::CauchyLoss(1.0); + for (auto& rp : relative_motions_) { + ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction( new RARelativeMotionError(rp.parameters, rp.scale_matrix)); @@ -515,18 +515,18 @@ class ReconstructionAlignment { } // Add absolute position errors - for (auto &a : absolute_positions_) { - ceres::CostFunction *cost_function = + for (auto& a : absolute_positions_) { + ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction( new RAAbsolutePositionError(a.position, a.std_deviation)); - problem.AddResidualBlock(cost_function, NULL, a.shot->parameters); + problem.AddResidualBlock(cost_function, nullptr, a.shot->parameters); } // Add relative-absolute position errors - ceres::LossFunction *l1_loss = new ceres::SoftLOneLoss(1.0); - for (auto &a : relative_absolute_positions_) { - ceres::CostFunction *cost_function = + ceres::LossFunction* l1_loss = new ceres::SoftLOneLoss(1.0); + for (auto& a : relative_absolute_positions_) { + ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction( new RARelativeAbsolutePositionError( @@ -537,8 +537,8 @@ class ReconstructionAlignment { } // Add common cameras constraints - for (auto &a : common_cameras_) { - ceres::CostFunction *cost_function = + for (auto& a : common_cameras_) { + ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction( new RACommonCameraError( a.shot_a->parameters, a.shot_b->parameters, @@ -550,8 +550,8 @@ class ReconstructionAlignment { } // Add common point errors - for (auto &a : common_points_) { - ceres::CostFunction *cost_function = + for (auto& a : common_points_) { + ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction( new RACommonPointError(a.point_a, a.point_b, a.std_deviation)); diff --git a/opensfm/src/bundle/src/bundle_adjuster.cc b/opensfm/src/bundle/src/bundle_adjuster.cc index 7433a22cb..ad5b89033 100644 --- a/opensfm/src/bundle/src/bundle_adjuster.cc +++ b/opensfm/src/bundle/src/bundle_adjuster.cc @@ -2,9 +2,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -14,17 +14,18 @@ #include "bundle/data/bias.h" namespace { -bool IsRigCameraUseful(bundle::RigCamera &rig_camera) { +bool IsRigCameraUseful(bundle::RigCamera& rig_camera) { return !(rig_camera.GetParametersToOptimize().empty() && rig_camera.GetValueData().isConstant(0.)); } -}; // namespace +} // namespace namespace bundle { BundleAdjuster::BundleAdjuster() { SetPointProjectionLossFunction("CauchyLoss", 1.0); SetRelativeMotionLossFunction("CauchyLoss", 1.0); focal_prior_sd_ = 1; + aspect_ratio_prior_sd_ = 1; c_prior_sd_ = 1; k1_sd_ = 1; k2_sd_ = 1; @@ -43,12 +44,12 @@ BundleAdjuster::BundleAdjuster() { } geometry::Camera BundleAdjuster::GetDefaultCameraSigma( - const geometry::Camera &camera) const { + const geometry::Camera& camera) const { std::unordered_map std_dev_map; std_dev_map[static_cast(geometry::Camera::Parameters::Focal)] = focal_prior_sd_; std_dev_map[static_cast(geometry::Camera::Parameters::AspectRatio)] = - focal_prior_sd_; + aspect_ratio_prior_sd_; std_dev_map[static_cast(geometry::Camera::Parameters::Cx)] = c_prior_sd_; std_dev_map[static_cast(geometry::Camera::Parameters::Cy)] = c_prior_sd_; std_dev_map[static_cast(geometry::Camera::Parameters::K1)] = k1_sd_; @@ -72,10 +73,10 @@ geometry::Pose BundleAdjuster::GetDefaultRigPoseSigma() const { return sigma; } -void BundleAdjuster::AddCamera(const std::string &id, - const geometry::Camera &camera, - const geometry::Camera &prior, bool constant) { - auto &camera_data = +void BundleAdjuster::AddCamera(const std::string& id, + const geometry::Camera& camera, + const geometry::Camera& prior, bool constant) { + auto& camera_data = cameras_ .emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(id, camera, prior, @@ -86,7 +87,7 @@ void BundleAdjuster::AddCamera(const std::string &id, } // identity bias by default - auto &bias_data = + auto& bias_data = bias_ .emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple( @@ -96,8 +97,8 @@ void BundleAdjuster::AddCamera(const std::string &id, bias_data.SetParametersToOptimize({}); } -void BundleAdjuster::SetCameraBias(const std::string &camera_id, - const geometry::Similarity &bias) { +void BundleAdjuster::SetCameraBias(const std::string& camera_id, + const geometry::Similarity& bias) { auto bias_exists = bias_.find(camera_id); if (bias_exists == bias_.end()) { throw std::runtime_error("Camera " + camera_id + " doesn't exist."); @@ -106,11 +107,11 @@ void BundleAdjuster::SetCameraBias(const std::string &camera_id, } void BundleAdjuster::AddRigInstance( - const std::string &rig_instance_id, const geometry::Pose &rig_instance_pose, - const std::unordered_map &shot_cameras, - const std::unordered_map &shot_rig_cameras, + const std::string& rig_instance_id, const geometry::Pose& rig_instance_pose, + const std::unordered_map& shot_cameras, + const std::unordered_map& shot_rig_cameras, bool fixed) { - auto &rig_instance = + auto& rig_instance = rig_instances_ .emplace(std::piecewise_construct, std::forward_as_tuple(rig_instance_id), @@ -121,7 +122,7 @@ void BundleAdjuster::AddRigInstance( rig_instance.SetParametersToOptimize({}); } - for (const auto &shot_camera : shot_cameras) { + for (const auto& shot_camera : shot_cameras) { const auto shot_id = shot_camera.first; const auto camera_id = shot_camera.second; const auto rig_camera_id = shot_rig_cameras.at(shot_id); @@ -140,18 +141,18 @@ void BundleAdjuster::AddRigInstance( &rig_camera_exists->second, &rig_instances_.at(rig_instance_id))); } -}; +} -void BundleAdjuster::AddRigCamera(const std::string &rig_camera_id, - const geometry::Pose &pose, - const geometry::Pose &pose_prior, +void BundleAdjuster::AddRigCamera(const std::string& rig_camera_id, + const geometry::Pose& pose, + const geometry::Pose& pose_prior, bool fixed) { const auto rig_camera_exists = rig_cameras_.find(rig_camera_id); if (rig_camera_exists != rig_cameras_.end()) { throw std::runtime_error("Rig model " + rig_camera_id + " already exist."); } - auto &rig_camera = + auto& rig_camera = rig_cameras_ .emplace(std::piecewise_construct, std::forward_as_tuple(rig_camera_id), @@ -161,11 +162,11 @@ void BundleAdjuster::AddRigCamera(const std::string &rig_camera_id, if (fixed) { rig_camera.SetParametersToOptimize({}); } -}; +} void BundleAdjuster::AddRigInstancePositionPrior( - const std::string &instance_id, const Vec3d &position, - const Vec3d &std_deviation, const std::string &scale_group) { + const std::string& instance_id, const Vec3d& position, + const Vec3d& std_deviation, const std::string& scale_group) { auto rig_instance_exists = rig_instances_.find(instance_id); if (rig_instance_exists == rig_instances_.end()) { throw std::runtime_error("Rig instance " + instance_id + " doesn't exist."); @@ -182,7 +183,7 @@ void BundleAdjuster::AddRigInstancePositionPrior( rig_instance_exists->second.scale_group.SetValue(scale_group); } -void BundleAdjuster::SetScaleSharing(const std::string &id, bool share) { +void BundleAdjuster::SetScaleSharing(const std::string& id, bool share) { const auto find = reconstructions_.find(id); if (find == reconstructions_.end()) { return; @@ -190,7 +191,7 @@ void BundleAdjuster::SetScaleSharing(const std::string &id, bool share) { find->second.shared = share; } -void BundleAdjuster::AddReconstruction(const std::string &id, bool constant) { +void BundleAdjuster::AddReconstruction(const std::string& id, bool constant) { Reconstruction r; r.id = id; r.constant = constant; @@ -199,8 +200,8 @@ void BundleAdjuster::AddReconstruction(const std::string &id, bool constant) { } void BundleAdjuster::AddReconstructionInstance( - const std::string &reconstruction_id, double scale, - const std::string &instance_id) { + const std::string& reconstruction_id, double scale, + const std::string& instance_id) { const auto find = reconstructions_.find(reconstruction_id); if (find == reconstructions_.end()) { return; @@ -209,8 +210,8 @@ void BundleAdjuster::AddReconstructionInstance( reconstructions_assignments_[instance_id] = reconstruction_id; } -void BundleAdjuster::AddPoint(const std::string &id, const Vec3d &position, - bool constant) { +Point* BundleAdjuster::AddPoint(const std::string& id, const Vec3d& position, + bool constant) { auto point = &points_ .emplace(std::piecewise_construct, std::forward_as_tuple(id), @@ -220,11 +221,12 @@ void BundleAdjuster::AddPoint(const std::string &id, const Vec3d &position, if (constant) { point->SetParametersToOptimize({}); } + return point; } -void BundleAdjuster::AddPointPrior(const std::string &point_id, - const Vec3d &position, - const Vec3d &std_deviation, +void BundleAdjuster::AddPointPrior(const std::string& point_id, + const Vec3d& position, + const Vec3d& std_deviation, bool has_altitude_prior) { auto point_exist = points_.find(point_id); if (point_exist == points_.end()) { @@ -236,29 +238,42 @@ void BundleAdjuster::AddPointPrior(const std::string &point_id, point_exist->second.has_altitude_prior = has_altitude_prior; } -void BundleAdjuster::AddPointProjectionObservation(const std::string &shot, - const std::string &point, - const Vec2d &observation, - double std_deviation) { +void BundleAdjuster::AddPointProjectionObservation( + const std::string& shot, const std::string& point, const Vec2d& observation, + double std_deviation, const std::optional& depth_prior) { PointProjectionObservation o; o.shot = &shots_.at(shot); o.camera = &cameras_.at(o.shot->GetCamera()->GetID()); o.point = &points_.at(point); o.coordinates = observation; o.std_deviation = std_deviation; - point_projection_observations_.push_back(o); + o.depth_prior = depth_prior; + point_projection_observations_.emplace_back(o); +} + +void BundleAdjuster::AddPointProjectionObservationRaw( + Shot* shot, Point* point, const Vec2d& observation, double std_deviation, + const std::optional& depth_prior) { + PointProjectionObservation o; + o.shot = shot; + o.camera = shot->GetCamera(); + o.point = point; + o.coordinates = observation; + o.std_deviation = std_deviation; + o.depth_prior = depth_prior; + point_projection_observations_.emplace_back(o); } -void BundleAdjuster::AddRelativeMotion(const RelativeMotion &rm) { +void BundleAdjuster::AddRelativeMotion(const RelativeMotion& rm) { relative_motions_.push_back(rm); } -void BundleAdjuster::AddRelativeRotation(const RelativeRotation &rr) { +void BundleAdjuster::AddRelativeRotation(const RelativeRotation& rr) { relative_rotations_.push_back(rr); } -void BundleAdjuster::AddCommonPosition(const std::string &shot_i, - const std::string &shot_j, double margin, +void BundleAdjuster::AddCommonPosition(const std::string& shot_i, + const std::string& shot_j, double margin, double std_deviation) { CommonPosition a; a.shot_i = shot_i; @@ -268,7 +283,7 @@ void BundleAdjuster::AddCommonPosition(const std::string &shot_i, common_positions_.push_back(a); } -HeatmapInterpolator::HeatmapInterpolator(const std::vector &in_heatmap, +HeatmapInterpolator::HeatmapInterpolator(const std::vector& in_heatmap, size_t in_width, double in_resolution) : heatmap(in_heatmap), width(in_width), @@ -277,15 +292,15 @@ HeatmapInterpolator::HeatmapInterpolator(const std::vector &in_heatmap, interpolator(grid), resolution(in_resolution) {} -void BundleAdjuster::AddHeatmap(const std::string &heatmap_id, - const std::vector &in_heatmap, +void BundleAdjuster::AddHeatmap(const std::string& heatmap_id, + const std::vector& in_heatmap, size_t in_width, double resolution) { heatmaps_[heatmap_id] = std::make_shared(in_heatmap, in_width, resolution); } -void BundleAdjuster::AddAbsolutePositionHeatmap(const std::string &shot_id, - const std::string &heatmap_id, +void BundleAdjuster::AddAbsolutePositionHeatmap(const std::string& shot_id, + const std::string& heatmap_id, double x_offset, double y_offset, double std_deviation) { @@ -298,8 +313,8 @@ void BundleAdjuster::AddAbsolutePositionHeatmap(const std::string &shot_id, absolute_positions_heatmaps_.push_back(a); } -void BundleAdjuster::AddAbsoluteUpVector(const std::string &shot_id, - const Vec3d &up_vector, +void BundleAdjuster::AddAbsoluteUpVector(const std::string& shot_id, + const Vec3d& up_vector, double std_deviation) { AbsoluteUpVector a; a.shot_id = shot_id; @@ -308,7 +323,7 @@ void BundleAdjuster::AddAbsoluteUpVector(const std::string &shot_id, absolute_up_vectors_.push_back(a); } -void BundleAdjuster::AddAbsolutePan(const std::string &shot_id, double angle, +void BundleAdjuster::AddAbsolutePan(const std::string& shot_id, double angle, double std_deviation) { AbsoluteAngle a; a.shot_id = shot_id; @@ -317,7 +332,7 @@ void BundleAdjuster::AddAbsolutePan(const std::string &shot_id, double angle, absolute_pans_.push_back(a); } -void BundleAdjuster::AddAbsoluteTilt(const std::string &shot_id, double angle, +void BundleAdjuster::AddAbsoluteTilt(const std::string& shot_id, double angle, double std_deviation) { AbsoluteAngle a; a.shot_id = shot_id; @@ -326,7 +341,7 @@ void BundleAdjuster::AddAbsoluteTilt(const std::string &shot_id, double angle, absolute_tilts_.push_back(a); } -void BundleAdjuster::AddAbsoluteRoll(const std::string &shot_id, double angle, +void BundleAdjuster::AddAbsoluteRoll(const std::string& shot_id, double angle, double std_deviation) { AbsoluteAngle a; a.shot_id = shot_id; @@ -335,9 +350,9 @@ void BundleAdjuster::AddAbsoluteRoll(const std::string &shot_id, double angle, absolute_rolls_.push_back(a); } -void BundleAdjuster::SetGaugeFixShots(const std::string &shot_origin, - const std::string &shot_scale) { - Shot *shot = &shots_.at(shot_origin); +void BundleAdjuster::SetGaugeFixShots(const std::string& shot_origin, + const std::string& shot_scale) { + Shot* shot = &shots_.at(shot_origin); shot->GetRigInstance()->SetParametersToOptimize({}); gauge_fix_shots_.SetValue(std::make_pair(shot_origin, shot_scale)); } @@ -376,11 +391,11 @@ void BundleAdjuster::SetCovarianceAlgorithmType(std::string t) { covariance_algorithm_type_ = t; } -void BundleAdjuster::SetInternalParametersPriorSD(double focal_sd, double c_sd, - double k1_sd, double k2_sd, - double p1_sd, double p2_sd, - double k3_sd, double k4_sd) { +void BundleAdjuster::SetInternalParametersPriorSD( + double focal_sd, double aspect_ratio_sd, double c_sd, double k1_sd, + double k2_sd, double p1_sd, double p2_sd, double k3_sd, double k4_sd) { focal_prior_sd_ = focal_sd; + aspect_ratio_prior_sd_ = aspect_ratio_sd; c_prior_sd_ = c_sd; k1_sd_ = k1_sd; k2_sd_ = k2_sd; @@ -388,7 +403,7 @@ void BundleAdjuster::SetInternalParametersPriorSD(double focal_sd, double c_sd, p2_sd_ = p2_sd; k3_sd_ = k3_sd; k4_sd_ = k4_sd; - for (auto &camera : cameras_) { + for (auto& camera : cameras_) { camera.second.SetSigma(GetDefaultCameraSigma(camera.second.GetValue())); } } @@ -397,7 +412,7 @@ void BundleAdjuster::SetRigParametersPriorSD(double rig_translation_sd, double rig_rotation_sd) { rig_translation_sd_ = rig_translation_sd; rig_rotation_sd_ = rig_rotation_sd; - for (auto &rig_camera : rig_cameras_) { + for (auto& rig_camera : rig_cameras_) { rig_camera.second.SetSigma(GetDefaultRigPoseSigma()); } } @@ -412,7 +427,7 @@ void BundleAdjuster::SetComputeReprojectionErrors(bool v) { compute_reprojection_errors_ = v; } -ceres::LossFunction *CreateLossFunction(std::string name, double threshold) { +ceres::LossFunction* CreateLossFunction(std::string name, double threshold) { if (name.compare("TrivialLoss") == 0) { return new ceres::TrivialLoss(); } else if (name.compare("HuberLoss") == 0) { @@ -424,12 +439,13 @@ ceres::LossFunction *CreateLossFunction(std::string name, double threshold) { } else if (name.compare("ArctanLoss") == 0) { return new ceres::ArctanLoss(threshold); } - return NULL; + throw std::runtime_error("ceres::LossFunction with name " + name + + " not found."); } -void BundleAdjuster::AddLinearMotion(const std::string &shot0_id, - const std::string &shot1_id, - const std::string &shot2_id, double alpha, +void BundleAdjuster::AddLinearMotion(const std::string& shot0_id, + const std::string& shot1_id, + const std::string& shot2_id, double alpha, double position_std_deviation, double orientation_std_deviation) { LinearMotion a; @@ -463,15 +479,15 @@ struct ErrorTraitsAnalytic { struct AddProjectionError { template - static void Apply(bool use_analytical, const PointProjectionObservation &obs, - ceres::LossFunction *loss, ceres::Problem *problem) { + static void Apply(bool use_analytical, const PointProjectionObservation& obs, + ceres::LossFunction* loss, ceres::Problem* problem) { constexpr static int ErrorSize = ErrorTraits::Type::Size; constexpr static int CameraSize = T::Size; constexpr static int ShotSize = 6; const bool is_rig_camera_useful = IsRigCameraUseful(*obs.shot->GetRigCamera()); - ceres::CostFunction *cost_function = nullptr; + ceres::CostFunction* cost_function = nullptr; if (use_analytical) { using ErrorType = typename ErrorTraitsAnalytic::Type; cost_function = new ErrorType(obs.camera->GetValue().GetProjectionType(), @@ -493,10 +509,44 @@ struct AddProjectionError { } }; +struct AddRelativeDepthError { + template + static void Apply(const PointProjectionObservation& obs, + ceres::LossFunction* loss, ceres::Problem* problem) { + constexpr static int ShotSize = 6; + constexpr static int PointSize = 3; + + if (!obs.depth_prior.has_value()) { + return; + } + const auto& depth = obs.depth_prior.value(); + if (!std::isfinite(depth.value)) { + throw std::runtime_error(obs.shot->GetID() + + " has non-finite depth prior"); + } + + const bool is_rig_camera_useful = + IsRigCameraUseful(*obs.shot->GetRigCamera()); + ceres::CostFunction* cost_function = nullptr; + + cost_function = + new ceres::AutoDiffCostFunction( + new RelativeDepthError(depth.value, depth.std_deviation, + is_rig_camera_useful, depth.is_radial)); + + problem->AddResidualBlock(cost_function, loss, + obs.shot->GetRigInstance()->GetValueData().data(), + obs.shot->GetRigCamera()->GetValueData().data(), + obs.point->GetValueData().data()); + } +}; + struct ComputeResidualError { template static void Apply(bool use_analytical, - const PointProjectionObservation &obs) { + const PointProjectionObservation& obs) { const bool is_rig_camera_useful = IsRigCameraUseful(*obs.shot->GetRigCamera()); if (use_analytical) { @@ -507,7 +557,7 @@ struct ComputeResidualError { VecNd residuals; ErrorType error(obs.camera->GetValue().GetProjectionType(), obs.coordinates, 1.0, is_rig_camera_useful); - const double *params[] = { + const double* params[] = { obs.camera->GetValueData().data(), obs.shot->GetRigInstance()->GetValueData().data(), obs.shot->GetRigCamera()->GetValueData().data(), @@ -532,8 +582,8 @@ struct ComputeResidualError { struct AddCameraPriorError { template - static void Apply(Camera &camera, ceres::Problem *problem) { - auto *prior_function = new DataPriorError(&camera); + static void Apply(Camera& camera, ceres::Problem* problem) { + auto* prior_function = new DataPriorError(&camera); // Set some logarithmic prior for Focal and Aspect ratio (if any) const auto camera_object = camera.GetValue(); @@ -548,7 +598,7 @@ struct AddCameraPriorError { } constexpr static int CameraSize = T::Size; - auto *cost_function = new ceres::DynamicAutoDiffCostFunction< + auto* cost_function = new ceres::DynamicAutoDiffCostFunction< DataPriorError>(prior_function); cost_function->SetNumResiduals(CameraSize); cost_function->AddParameterBlock(CameraSize); @@ -561,26 +611,29 @@ void BundleAdjuster::Run() { ceres::Problem problem; // Add cameras - for (auto &i : cameras_) { - auto &data = i.second.GetValueData(); + for (auto& [_, cam] : cameras_) { + auto& data = cam.GetValueData(); problem.AddParameterBlock(data.data(), data.size()); // Lock parameters based on bitmask of parameters : only constant for now - if (i.second.GetParametersToOptimize().empty()) { + if (cam.GetParametersToOptimize().empty()) { problem.SetParameterBlockConstant(data.data()); - }else if (i.second.GetValue().GetProjectionType() == geometry::ProjectionType::BROWN){ - // Keep aspect ratio constant (BROWN only) + } else if (cam.GetValue().GetProjectionType() == + geometry::ProjectionType::BROWN) { + // Keep aspect ratio constant (BROWN only) #if CERES_VERSION_MAJOR == 2 && CERES_VERSION_MINOR >= 2 - ceres::SubsetManifold *subset_parameterization = new ceres::SubsetManifold(data.size(), { 6 }); - problem.SetManifold(data.data(), subset_parameterization); + ceres::SubsetManifold* subset_parameterization = + new ceres::SubsetManifold(data.size(), {6}); + problem.SetManifold(data.data(), subset_parameterization); #else - ceres::SubsetParameterization *subset_parameterization = new ceres::SubsetParameterization(data.size(), { 6 }); - problem.SetParameterization(data.data(), subset_parameterization); + ceres::SubsetParameterization* subset_parameterization = + new ceres::SubsetParameterization(data.size(), {6}); + problem.SetParameterization(data.data(), subset_parameterization); #endif } // Add a barrier for constraining transition of dual to stay in [0, 1] - const auto camera = i.second.GetValue(); + const auto camera = cam.GetValue(); if (camera.GetProjectionType() == geometry::ProjectionType::DUAL) { const auto types = camera.GetParametersTypes(); int index = -1; @@ -590,7 +643,7 @@ void BundleAdjuster::Run() { } } if (index >= 0) { - ceres::CostFunction *transition_barrier = + ceres::CostFunction* transition_barrier = new ceres::AutoDiffCostFunction( new ParameterBarrier(0.0, 1.0, index)); @@ -600,8 +653,8 @@ void BundleAdjuster::Run() { } // Add cameras biases - for (auto &b : bias_) { - auto &data = b.second.GetValueData(); + for (auto& b : bias_) { + auto& data = b.second.GetValueData(); problem.AddParameterBlock(data.data(), data.size()); // Lock parameters based on bitmask of parameters : only constant for now @@ -611,8 +664,8 @@ void BundleAdjuster::Run() { } // Add rig cameras - for (auto &rc : rig_cameras_) { - auto &data = rc.second.GetValueData(); + for (auto& rc : rig_cameras_) { + auto& data = rc.second.GetValueData(); problem.AddParameterBlock(data.data(), data.size()); // Lock parameters based on bitmask of parameters : only constant for now @@ -622,8 +675,8 @@ void BundleAdjuster::Run() { } // Add rig instances - for (auto &ri : rig_instances_) { - auto &data = ri.second.GetValueData(); + for (auto& ri : rig_instances_) { + auto& data = ri.second.GetValueData(); problem.AddParameterBlock(data.data(), data.size()); // Lock parameters based on bitmask of parameters : only constant for now @@ -633,8 +686,8 @@ void BundleAdjuster::Run() { } // Add points - for (auto &p : points_) { - auto &data = p.second.GetValueData(); + for (auto& p : points_) { + auto& data = p.second.GetValueData(); problem.AddParameterBlock(data.data(), data.size()); // Lock parameters based on bitmask of parameters : only constant for now @@ -644,8 +697,8 @@ void BundleAdjuster::Run() { } // Reconstructions - for (auto &i : reconstructions_) { - for (auto &s : i.second.scales) { + for (auto& i : reconstructions_) { + for (auto& s : i.second.scales) { if (i.second.constant) { problem.AddParameterBlock(&s.second, 1); problem.SetParameterBlockConstant(&s.second); @@ -659,11 +712,11 @@ void BundleAdjuster::Run() { } // New generic prior errors (only rig instances + rig models + points for now) - for (auto &i : points_) { + for (auto& i : points_) { if (!i.second.HasPrior()) { continue; } - auto *position_prior = new DataPriorError(&i.second); + auto* position_prior = new DataPriorError(&i.second); if (i.second.has_altitude_prior) { position_prior->SetConstrainedDataIndexes( {Point::Parameter::PX, Point::Parameter::PY, Point::Parameter::PZ}); @@ -671,7 +724,7 @@ void BundleAdjuster::Run() { position_prior->SetConstrainedDataIndexes( {Point::Parameter::PX, Point::Parameter::PY}); } - auto *cost_function = + auto* cost_function = new ceres::DynamicAutoDiffCostFunction>( position_prior); cost_function->SetNumResiduals(i.second.has_altitude_prior ? 3 : 2); @@ -683,7 +736,7 @@ void BundleAdjuster::Run() { // Gather scale groups for rig instance priors std::map std_dev_group_remap; - for (auto &i : rig_instances_) { + for (auto& i : rig_instances_) { if (!i.second.scale_group.HasValue()) { continue; } @@ -701,7 +754,7 @@ void BundleAdjuster::Run() { // them up. if (adjust_absolute_position_std_) { for (int i = 0; i < std_deviations.size(); ++i) { - ceres::CostFunction *std_dev_cost_function = + ceres::CostFunction* std_dev_cost_function = new ceres::AutoDiffCostFunction( new StdDeviationConstraint()); problem.AddResidualBlock(std_dev_cost_function, nullptr, @@ -709,25 +762,25 @@ void BundleAdjuster::Run() { } } else { for (int i = 0; i < std_deviations.size(); ++i) { - auto *data = &std_deviations[i]; + auto* data = &std_deviations[i]; problem.AddParameterBlock(data, 1); problem.SetParameterBlockConstant(data); } } - for (auto &i : rig_instances_) { + for (auto& i : rig_instances_) { if (!i.second.HasPrior()) { continue; } using PriorType = DataPriorError; - auto *position_prior = + auto* position_prior = new PriorType(&i.second, adjust_absolute_position_std_); position_prior->SetTransform(SimilarityPriorTransform()); position_prior->SetConstrainedDataIndexes( {Pose::Parameter::TX, Pose::Parameter::TY, Pose::Parameter::TZ}); - const auto &bias_reference_camera = i.second.shot_cameras.begin()->second; + const auto& bias_reference_camera = i.second.shot_cameras.begin()->second; const auto maybe_bias = bias_.find(bias_reference_camera); if (maybe_bias == bias_.end()) { throw std::runtime_error("Reference camera " + bias_reference_camera + @@ -736,9 +789,9 @@ void BundleAdjuster::Run() { } const auto scale_group = i.second.scale_group.Value(); - auto *scale_param = &std_deviations.at(std_dev_group_remap.at(scale_group)); + auto* scale_param = &std_deviations.at(std_dev_group_remap.at(scale_group)); - auto *cost_function = + auto* cost_function = new ceres::DynamicAutoDiffCostFunction(position_prior); cost_function->SetNumResiduals(3); cost_function->AddParameterBlock(Pose::Parameter::NUM_PARAMS); @@ -748,12 +801,12 @@ void BundleAdjuster::Run() { cost_function, nullptr, i.second.GetValueData().data(), maybe_bias->second.GetValueData().data(), scale_param); } - for (auto &rc : rig_cameras_) { + for (auto& rc : rig_cameras_) { if (!rc.second.HasPrior()) { continue; } - auto *pose_prior = new DataPriorError(&rc.second); - auto *cost_function = + auto* pose_prior = new DataPriorError(&rc.second); + auto* cost_function = new ceres::DynamicAutoDiffCostFunction>( pose_prior); cost_function->SetNumResiduals(Pose::Parameter::NUM_PARAMS); @@ -762,36 +815,40 @@ void BundleAdjuster::Run() { rc.second.GetValueData().data()); } // Add internal parameter priors blocks - for (auto &i : cameras_) { + for (auto& i : cameras_) { const auto projection_type = i.second.GetValue().GetProjectionType(); geometry::Dispatch(projection_type, i.second, &problem); } // Add reprojection error blocks - ceres::LossFunction *projection_loss = + ceres::LossFunction* projection_loss = point_projection_observations_.empty() ? nullptr : CreateLossFunction(point_projection_loss_name_, point_projection_loss_threshold_); - for (auto &observation : point_projection_observations_) { + for (auto& observation : point_projection_observations_) { const auto projection_type = observation.camera->GetValue().GetProjectionType(); geometry::Dispatch( projection_type, use_analytic_, observation, projection_loss, &problem); + + // Add relative depth error blocks + geometry::Dispatch(projection_type, observation, + projection_loss, &problem); } // Add relative motion errors - for (auto &rp : relative_motions_) { + for (auto& rp : relative_motions_) { double robust_threshold = relative_motion_loss_threshold_ * rp.robust_multiplier; - ceres::LossFunction *relative_motion_loss = + ceres::LossFunction* relative_motion_loss = CreateLossFunction(relative_motion_loss_name_, robust_threshold); - auto *relative_motion = new RelativeMotionError( + auto* relative_motion = new RelativeMotionError( rp.parameters, rp.scale_matrix, rp.observed_scale); - auto *cost_function = + auto* cost_function = new ceres::DynamicAutoDiffCostFunction( relative_motion); cost_function->AddParameterBlock(6); @@ -799,18 +856,18 @@ void BundleAdjuster::Run() { cost_function->AddParameterBlock(1); cost_function->SetNumResiduals(Similarity::Parameter::NUM_PARAMS); - auto &rig_instance_i = rig_instances_.at(rp.rig_instance_id_i); - auto &rig_instance_j = rig_instances_.at(rp.rig_instance_id_j); + auto& rig_instance_i = rig_instances_.at(rp.rig_instance_id_i); + auto& rig_instance_j = rig_instances_.at(rp.rig_instance_id_j); - auto *scale_i = + auto* scale_i = reconstructions_[reconstructions_assignments_.at(rp.rig_instance_id_i)] .GetScalePtr(rp.rig_instance_id_i); - auto *scale_j = + auto* scale_j = reconstructions_[reconstructions_assignments_.at(rp.rig_instance_id_j)] .GetScalePtr(rp.rig_instance_id_j); auto parameter_blocks = - std::vector({rig_instance_i.GetValueData().data(), - rig_instance_j.GetValueData().data(), scale_i}); + std::vector({rig_instance_i.GetValueData().data(), + rig_instance_j.GetValueData().data(), scale_i}); if (scale_i != scale_j) { cost_function->AddParameterBlock(1); @@ -823,27 +880,27 @@ void BundleAdjuster::Run() { } // Add relative rotation errors - ceres::LossFunction *relative_rotation_loss = + ceres::LossFunction* relative_rotation_loss = relative_rotations_.empty() ? nullptr : CreateLossFunction(relative_motion_loss_name_, relative_motion_loss_threshold_); - for (auto &rr : relative_rotations_) { - auto *relative_rotation = + for (auto& rr : relative_rotations_) { + auto* relative_rotation = new RelativeRotationError(rr.rotation, rr.scale_matrix); - auto *cost_function = + auto* cost_function = new ceres::DynamicAutoDiffCostFunction( relative_rotation); cost_function->AddParameterBlock(6); cost_function->AddParameterBlock(6); cost_function->SetNumResiduals(3); - auto &shot_i = shots_.at(rr.shot_id_i); - auto &shot_j = shots_.at(rr.shot_id_j); + auto& shot_i = shots_.at(rr.shot_id_i); + auto& shot_j = shots_.at(rr.shot_id_j); auto parameter_blocks = - std::vector({shot_i.GetRigInstance()->GetValueData().data(), - shot_j.GetRigInstance()->GetValueData().data()}); + std::vector({shot_i.GetRigInstance()->GetValueData().data(), + shot_j.GetRigInstance()->GetValueData().data()}); auto shot_i_rig_camera = shot_i.GetRigCamera()->GetValueData().data(); if (IsRigCameraUseful(*shot_i.GetRigCamera())) { @@ -868,25 +925,25 @@ void BundleAdjuster::Run() { } // Add common position errors - ceres::LossFunction *common_position_loss = nullptr; - for (auto &c : common_positions_) { + ceres::LossFunction* common_position_loss = nullptr; + for (auto& c : common_positions_) { if (common_position_loss == nullptr) { common_position_loss = new ceres::TukeyLoss(1); } - auto *common_position = new CommonPositionError(c.margin, c.std_deviation); - auto *cost_function = + auto* common_position = new CommonPositionError(c.margin, c.std_deviation); + auto* cost_function = new ceres::DynamicAutoDiffCostFunction( common_position); cost_function->AddParameterBlock(6); cost_function->AddParameterBlock(6); cost_function->SetNumResiduals(3); - auto &shot_i = shots_.at(c.shot_i); - auto &shot_j = shots_.at(c.shot_j); + auto& shot_i = shots_.at(c.shot_i); + auto& shot_j = shots_.at(c.shot_j); auto parameter_blocks = - std::vector({shot_i.GetRigInstance()->GetValueData().data(), - shot_j.GetRigInstance()->GetValueData().data()}); + std::vector({shot_i.GetRigInstance()->GetValueData().data(), + shot_j.GetRigInstance()->GetValueData().data()}); auto shot_i_rig_camera = shot_i.GetRigCamera()->GetValueData().data(); if (IsRigCameraUseful(*shot_i.GetRigCamera())) { @@ -911,28 +968,28 @@ void BundleAdjuster::Run() { } // Add heatmap cost - for (const auto &a : absolute_positions_heatmaps_) { - auto *cost_function = HeatmapdCostFunctor::Create( + for (const auto& a : absolute_positions_heatmaps_) { + auto* cost_function = HeatmapdCostFunctor::Create( a.heatmap->interpolator, a.x_offset, a.y_offset, a.heatmap->height, a.heatmap->width, a.heatmap->resolution, a.std_deviation); - auto &shot = shots_.at(a.shot_id); + auto& shot = shots_.at(a.shot_id); problem.AddResidualBlock(cost_function, nullptr, shot.GetRigInstance()->GetValueData().data(), shot.GetRigCamera()->GetValueData().data()); } // Add absolute up vector errors - ceres::LossFunction *up_vector_loss = nullptr; - for (auto &a : absolute_up_vectors_) { + ceres::LossFunction* up_vector_loss = nullptr; + for (auto& a : absolute_up_vectors_) { if (a.std_deviation > 0) { if (up_vector_loss == nullptr) { up_vector_loss = new ceres::CauchyLoss(1); } - ceres::CostFunction *up_cost_function = + ceres::CostFunction* up_cost_function = new ceres::AutoDiffCostFunction( new UpVectorError(a.up_vector, a.std_deviation)); - auto &shot = shots_.at(a.shot_id); + auto& shot = shots_.at(a.shot_id); problem.AddResidualBlock(up_cost_function, up_vector_loss, shot.GetRigInstance()->GetValueData().data(), shot.GetRigCamera()->GetValueData().data()); @@ -940,16 +997,16 @@ void BundleAdjuster::Run() { } // Add absolute pan (compass) errors - ceres::LossFunction *pan_loss = nullptr; - for (auto &a : absolute_pans_) { + ceres::LossFunction* pan_loss = nullptr; + for (auto& a : absolute_pans_) { if (a.std_deviation > 0) { if (pan_loss == nullptr) { pan_loss = new ceres::CauchyLoss(1); } - ceres::CostFunction *pan_cost_function = + ceres::CostFunction* pan_cost_function = new ceres::AutoDiffCostFunction( new PanAngleError(a.angle, a.std_deviation)); - auto &shot = shots_.at(a.shot_id); + auto& shot = shots_.at(a.shot_id); problem.AddResidualBlock(pan_cost_function, pan_loss, shot.GetRigInstance()->GetValueData().data(), shot.GetRigCamera()->GetValueData().data()); @@ -957,16 +1014,16 @@ void BundleAdjuster::Run() { } // Add absolute tilt errors - ceres::LossFunction *tilt_loss = nullptr; - for (auto &a : absolute_tilts_) { + ceres::LossFunction* tilt_loss = nullptr; + for (auto& a : absolute_tilts_) { if (a.std_deviation > 0) { if (tilt_loss == nullptr) { tilt_loss = new ceres::CauchyLoss(1); } - ceres::CostFunction *tilt_cost_function = + ceres::CostFunction* tilt_cost_function = new ceres::AutoDiffCostFunction( new TiltAngleError(a.angle, a.std_deviation)); - auto &shot = shots_.at(a.shot_id); + auto& shot = shots_.at(a.shot_id); problem.AddResidualBlock(tilt_cost_function, tilt_loss, shot.GetRigInstance()->GetValueData().data(), shot.GetRigCamera()->GetValueData().data()); @@ -974,16 +1031,16 @@ void BundleAdjuster::Run() { } // Add absolute roll errors - ceres::LossFunction *roll_loss = nullptr; - for (auto &a : absolute_rolls_) { + ceres::LossFunction* roll_loss = nullptr; + for (auto& a : absolute_rolls_) { if (a.std_deviation > 0) { if (roll_loss == nullptr) { roll_loss = new ceres::CauchyLoss(1); } - ceres::CostFunction *roll_cost_function = + ceres::CostFunction* roll_cost_function = new ceres::AutoDiffCostFunction( new RollAngleError(a.angle, a.std_deviation)); - auto &shot = shots_.at(a.shot_id); + auto& shot = shots_.at(a.shot_id); problem.AddResidualBlock(roll_cost_function, roll_loss, shot.GetRigInstance()->GetValueData().data(), shot.GetRigCamera()->GetValueData().data()); @@ -991,15 +1048,15 @@ void BundleAdjuster::Run() { } // Add linear motion priors - ceres::LossFunction *linear_motion_prior_loss_ = nullptr; - for (auto &a : linear_motion_prior_) { + ceres::LossFunction* linear_motion_prior_loss_ = nullptr; + for (auto& a : linear_motion_prior_) { if (linear_motion_prior_loss_ == nullptr) { linear_motion_prior_loss_ = new ceres::CauchyLoss(1); } - auto *linear_motion = new LinearMotionError( + auto* linear_motion = new LinearMotionError( a.alpha, a.position_std_deviation, a.orientation_std_deviation); - auto *cost_function = + auto* cost_function = new ceres::DynamicAutoDiffCostFunction( linear_motion); cost_function->AddParameterBlock(6); @@ -1007,14 +1064,14 @@ void BundleAdjuster::Run() { cost_function->AddParameterBlock(6); cost_function->SetNumResiduals(6); - auto &shot0 = shots_.at(a.shot0); - auto &shot1 = shots_.at(a.shot1); - auto &shot2 = shots_.at(a.shot2); + auto& shot0 = shots_.at(a.shot0); + auto& shot1 = shots_.at(a.shot1); + auto& shot2 = shots_.at(a.shot2); auto parameter_blocks = - std::vector({shot0.GetRigInstance()->GetValueData().data(), - shot1.GetRigInstance()->GetValueData().data(), - shot2.GetRigInstance()->GetValueData().data()}); + std::vector({shot0.GetRigInstance()->GetValueData().data(), + shot1.GetRigInstance()->GetValueData().data(), + shot2.GetRigInstance()->GetValueData().data()}); auto shot0_rig_camera = shot0.GetRigCamera()->GetValueData().data(); if (IsRigCameraUseful(*shot0.GetRigCamera())) { @@ -1053,14 +1110,14 @@ void BundleAdjuster::Run() { // Gauge fix if (gauge_fix_shots_.HasValue()) { - const auto &gauge_shots = gauge_fix_shots_.Value(); + const auto& gauge_shots = gauge_fix_shots_.Value(); auto instance1 = shots_.at(gauge_shots.first).GetRigInstance(); auto instance2 = shots_.at(gauge_shots.second).GetRigInstance(); const double norm = (instance1->GetValue().GetOrigin() - instance2->GetValue().GetOrigin()) .norm(); - ceres::CostFunction *cost_function = + ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction( new TranslationPriorError(norm)); @@ -1078,6 +1135,17 @@ void BundleAdjuster::Run() { } options.num_threads = num_threads_; options.max_num_iterations = max_num_iterations_; + options.dense_linear_algebra_library_type = ceres::LAPACK; + options.sparse_linear_algebra_library_type = ceres::SUITE_SPARSE; + +// Activate NESDIS only for Ceres >= 2.2 +#if CERES_VERSION_MAJOR >= 2 && CERES_VERSION_MINOR >= 2 +#ifdef CERES_NO_EIGEN_METIS + options.linear_solver_ordering_type = ceres::AMD; +#else + options.linear_solver_ordering_type = ceres::NESDIS; +#endif // CERES_NO_EIGEN_METIS +#endif // CERES_VERSION_MAJOR >= 2 && CERES_VERSION_MINOR >= 2 ceres::Solve(options, &problem, &last_run_summary_); @@ -1089,7 +1157,7 @@ void BundleAdjuster::Run() { } } -void BundleAdjuster::ComputeCovariances(ceres::Problem *problem) { +void BundleAdjuster::ComputeCovariances(ceres::Problem* problem) { bool computed = false; if (last_run_summary_.termination_type != ceres::FAILURE) { @@ -1101,17 +1169,17 @@ void BundleAdjuster::ComputeCovariances(ceres::Problem *problem) { } ceres::Covariance covariance(options); - std::vector> covariance_blocks; - for (auto &i : shots_) { - covariance_blocks.push_back( - std::make_pair(i.second.GetRigInstance()->GetValueData().data(), - i.second.GetRigInstance()->GetValueData().data())); + std::vector> covariance_blocks; + for (auto& i : shots_) { + covariance_blocks.emplace_back( + i.second.GetRigInstance()->GetValueData().data(), + i.second.GetRigInstance()->GetValueData().data()); } bool worked = covariance.Compute(covariance_blocks, problem); if (worked) { - for (auto &i : shots_) { + for (auto& i : shots_) { covariance_estimation_valid_ = true; MatXd covariance_matrix(6, 6); @@ -1130,7 +1198,7 @@ void BundleAdjuster::ComputeCovariances(ceres::Problem *problem) { // So maybe we can find a better solution if (computed) { // Check for NaNs - for (auto &i : shots_) { + for (auto& i : shots_) { if (!i.second.GetRigInstance()->HasCovariance() || !i.second.GetRigInstance()->GetCovariance().allFinite()) { covariance_estimation_valid_ = false; @@ -1138,7 +1206,9 @@ void BundleAdjuster::ComputeCovariances(ceres::Problem *problem) { break; } // stop after first Nan value - if (!computed) break; + if (!computed) { + break; + } } } @@ -1153,7 +1223,7 @@ void BundleAdjuster::ComputeCovariances(ceres::Problem *problem) { default_rotation_variance); default_covariance_matrix.diagonal().segment<3>(3).setConstant( default_translation_variance); - for (auto &i : shots_) { + for (auto& i : shots_) { i.second.GetRigInstance()->SetCovariance(default_covariance_matrix); } } @@ -1161,11 +1231,11 @@ void BundleAdjuster::ComputeCovariances(ceres::Problem *problem) { void BundleAdjuster::ComputeReprojectionErrors() { // Init errors - for (auto &i : points_) { + for (auto& i : points_) { i.second.reprojection_errors.clear(); } - for (auto &observation : point_projection_observations_) { + for (auto& observation : point_projection_observations_) { const auto projection_type = observation.camera->GetValue().GetProjectionType(); geometry::Dispatch(projection_type, use_analytic_, @@ -1181,33 +1251,37 @@ int BundleAdjuster::GetRelativeMotionsCount() const { return relative_motions_.size(); } -geometry::Camera BundleAdjuster::GetCamera(const std::string &id) const { +geometry::Camera BundleAdjuster::GetCamera(const std::string& id) const { if (cameras_.find(id) == cameras_.end()) { throw std::runtime_error("Camera " + id + " doesn't exists"); } return cameras_.at(id).GetValue(); } -geometry::Similarity BundleAdjuster::GetBias(const std::string &id) const { +geometry::Similarity BundleAdjuster::GetBias(const std::string& id) const { if (bias_.find(id) == bias_.end()) { throw std::runtime_error("Camera " + id + " doesn't exists"); } return bias_.at(id).GetValue(); } -Point BundleAdjuster::GetPoint(const std::string &id) const { +Point BundleAdjuster::GetPoint(const std::string& id) const { if (points_.find(id) == points_.end()) { throw std::runtime_error("Point " + id + " doesn't exists"); } return points_.at(id); } -bool BundleAdjuster::HasPoint(const std::string &id) const { +Point* BundleAdjuster::GetPointRaw(const std::string& id) { + return &points_.at(id); +} + +bool BundleAdjuster::HasPoint(const std::string& id) const { return points_.find(id) != points_.end(); } Reconstruction BundleAdjuster::GetReconstruction( - const std::string &reconstruction_id) const { + const std::string& reconstruction_id) const { const auto it = reconstructions_.find(reconstruction_id); if (it == reconstructions_.end()) { throw std::runtime_error("Reconstruction " + reconstruction_id + @@ -1216,7 +1290,7 @@ Reconstruction BundleAdjuster::GetReconstruction( return it->second; } -RigCamera BundleAdjuster::GetRigCamera(const std::string &rig_camera_id) const { +RigCamera BundleAdjuster::GetRigCamera(const std::string& rig_camera_id) const { if (rig_cameras_.find(rig_camera_id) == rig_cameras_.end()) { throw std::runtime_error("Rig camera " + rig_camera_id + " doesn't exists"); } @@ -1224,7 +1298,7 @@ RigCamera BundleAdjuster::GetRigCamera(const std::string &rig_camera_id) const { } RigInstance BundleAdjuster::GetRigInstance( - const std::string &instance_id) const { + const std::string& instance_id) const { if (rig_instances_.find(instance_id) == rig_instances_.end()) { throw std::runtime_error("Rig instance " + instance_id + " doesn't exists"); } @@ -1234,10 +1308,15 @@ RigInstance BundleAdjuster::GetRigInstance( std::map BundleAdjuster::GetRigCameras() const { return rig_cameras_; } + std::map BundleAdjuster::GetRigInstances() const { return rig_instances_; } +Shot* BundleAdjuster::GetShotRaw(const std::string& id) { + return &shots_.at(id); +} + std::string BundleAdjuster::BriefReport() const { return last_run_summary_.BriefReport(); } diff --git a/opensfm/src/bundle/test/reprojection_errors_test.cc b/opensfm/src/bundle/test/reprojection_errors_test.cc index cfb9edb64..7c9093c19 100644 --- a/opensfm/src/bundle/test/reprojection_errors_test.cc +++ b/opensfm/src/bundle/test/reprojection_errors_test.cc @@ -1,13 +1,28 @@ -#include #include #include #include #include +using TEigenDiff = Eigen::AutoDiffScalar>; + +namespace std { +int fpclassify(const TEigenDiff& x) { + return std::fpclassify(x.value()); +} + +inline TEigenDiff hypot(const TEigenDiff& x, const TEigenDiff& y, + const TEigenDiff& z) { + return sqrt(x * x + y * y + z * z); +} +} // namespace std + +// Has to be included AFTER std:: maths overloads for Eigen Autodiff above +#include + class ReprojectionError2DFixtureBase : public ::testing::Test { public: - typedef Eigen::AutoDiffScalar AScalar; + using AScalar = Eigen::AutoDiffScalar; ReprojectionError2DFixtureBase() { observed << 0.5, 0.5; } constexpr static int size_residual = 2; @@ -118,7 +133,7 @@ TEST_F(ReprojectionError2DFixture, BrownAnalyticErrorEvaluatesOK) { // focal, ar, cx, cy, k1, k2, k3, p1, p2 constexpr std::array camera{0.3, 1.0, 0.001, -0.02, 0.1, -0.03, 0.001, -0.005, 0.001}; - RunTest(geometry::ProjectionType::BROWN, &camera[0]); + RunTest(geometry::ProjectionType::BROWN, camera.data()); } TEST_F(ReprojectionError2DFixture, PerspectiveAnalyticErrorEvaluatesOK) { @@ -126,7 +141,7 @@ TEST_F(ReprojectionError2DFixture, PerspectiveAnalyticErrorEvaluatesOK) { // focal, k1, k2 constexpr std::array camera{0.3, 0.1, -0.03}; - RunTest(geometry::ProjectionType::PERSPECTIVE, &camera[0]); + RunTest(geometry::ProjectionType::PERSPECTIVE, camera.data()); } TEST_F(ReprojectionError2DFixture, FisheyeAnalyticErrorEvaluatesOK) { @@ -134,7 +149,7 @@ TEST_F(ReprojectionError2DFixture, FisheyeAnalyticErrorEvaluatesOK) { // focal, k1, k2, k3 constexpr std::array camera{0.3, 0.1, -0.03}; - RunTest(geometry::ProjectionType::FISHEYE, &camera[0]); + RunTest(geometry::ProjectionType::FISHEYE, camera.data()); } TEST_F(ReprojectionError2DFixture, FisheyeOpencvAnalyticErrorEvaluatesOK) { @@ -143,7 +158,7 @@ TEST_F(ReprojectionError2DFixture, FisheyeOpencvAnalyticErrorEvaluatesOK) { // focal, ar, cx, cy, k1, k2, k3, k4 constexpr std::array camera{0.3, 1.0, 0.001, -0.02, 0.1, -0.03, 0.001, -0.005}; - RunTest(geometry::ProjectionType::FISHEYE_OPENCV, &camera[0]); + RunTest(geometry::ProjectionType::FISHEYE_OPENCV, camera.data()); } TEST_F(ReprojectionError2DFixture, Fisheye62AnalyticErrorEvaluatesOK) { @@ -153,7 +168,7 @@ TEST_F(ReprojectionError2DFixture, Fisheye62AnalyticErrorEvaluatesOK) { constexpr std::array camera{0.3, 1.0, 0.001, -0.02, 0.1, -0.03, 0.001, -0.005, 0.01, 0.006, 0.02, 0.003}; - RunTest(geometry::ProjectionType::FISHEYE62, &camera[0]); + RunTest(geometry::ProjectionType::FISHEYE62, camera.data()); } TEST_F(ReprojectionError2DFixture, Fisheye624AnalyticErrorEvaluatesOK) { @@ -163,7 +178,7 @@ TEST_F(ReprojectionError2DFixture, Fisheye624AnalyticErrorEvaluatesOK) { constexpr std::array camera{ 0.3, 1.0, 0.001, -0.02, 0.1, -0.03, 0.001, -0.005, 0.01, 0.006, 0.02, 0.003, 0.001, -0.009, -0.01, 0.03}; - RunTest(geometry::ProjectionType::FISHEYE624, &camera[0]); + RunTest(geometry::ProjectionType::FISHEYE624, camera.data()); } TEST_F(ReprojectionError2DFixture, DualAnalyticErrorEvaluatesOK) { @@ -171,14 +186,14 @@ TEST_F(ReprojectionError2DFixture, DualAnalyticErrorEvaluatesOK) { // transition, focal, k1, k2 constexpr std::array camera{0.5, 0.3, 0.1, -0.03}; - RunTest(geometry::ProjectionType::DUAL, &camera[0]); + RunTest(geometry::ProjectionType::DUAL, camera.data()); } class ReprojectionError3DFixture : public ::testing::Test { public: static constexpr int size = 3; - typedef Eigen::AutoDiffScalar AScalar; + using AScalar = Eigen::AutoDiffScalar; ReprojectionError3DFixture() { observed << 0.5, 0.5; } void SetupADiff() { diff --git a/opensfm/src/cmake/FindGlog.cmake b/opensfm/src/cmake/FindGlog.cmake index 0dde218ee..b1244c15d 100644 --- a/opensfm/src/cmake/FindGlog.cmake +++ b/opensfm/src/cmake/FindGlog.cmake @@ -157,6 +157,16 @@ ENDIF (GLOG_LIBRARY AND IF (GLOG_FOUND) SET(GLOG_INCLUDE_DIRS ${GLOG_INCLUDE_DIR}) SET(GLOG_LIBRARIES ${GLOG_LIBRARY}) + + # Create imported target for modern CMake usage + # This provides compatibility with systems that don't have glog CMake config + IF (NOT TARGET glog::glog) + ADD_LIBRARY(glog::glog UNKNOWN IMPORTED) + SET_TARGET_PROPERTIES(glog::glog PROPERTIES + IMPORTED_LOCATION "${GLOG_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${GLOG_INCLUDE_DIR}" + INTERFACE_COMPILE_DEFINITIONS "GLOG_USE_GLOG_EXPORT") + ENDIF() ENDIF (GLOG_FOUND) # Handle REQUIRED / QUIET optional arguments. diff --git a/opensfm/src/debug_c_extension.cc b/opensfm/src/debug_c_extension.cc index f20318119..b4599c785 100644 --- a/opensfm/src/debug_c_extension.cc +++ b/opensfm/src/debug_c_extension.cc @@ -14,10 +14,11 @@ // where module is the name of the module that you wrote. #include + #include namespace py = pybind11; -int main(int argc, char *argv[]) { +int main(int argc, char* argv[]) { py::scoped_interpreter guard{}; if (argc != 2) { diff --git a/opensfm/src/dense/CMakeLists.txt b/opensfm/src/dense/CMakeLists.txt index 2728749df..da67193e8 100644 --- a/opensfm/src/dense/CMakeLists.txt +++ b/opensfm/src/dense/CMakeLists.txt @@ -27,3 +27,4 @@ target_link_libraries(pydense PRIVATE dense foundation) set_target_properties(pydense PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${opensfm_SOURCE_DIR}/.." ) +install(TARGETS pydense LIBRARY DESTINATION .) diff --git a/opensfm/src/dense/depthmap.h b/opensfm/src/dense/depthmap.h index 940a868ba..973ba5993 100644 --- a/opensfm/src/dense/depthmap.h +++ b/opensfm/src/dense/depthmap.h @@ -5,7 +5,7 @@ namespace dense { -float Variance(float *x, int n); +float Variance(float* x, int n); class NCCEstimator { public: @@ -19,31 +19,31 @@ class NCCEstimator { float sumw_; }; -void ApplyHomography(const cv::Matx33f &H, float x1, float y1, float *x2, - float *y2); +void ApplyHomography(const cv::Matx33f& H, float x1, float y1, float* x2, + float* y2); -cv::Matx33d PlaneInducedHomography(const cv::Matx33d &K1, const cv::Matx33d &R1, - const cv::Vec3d &t1, const cv::Matx33d &K2, - const cv::Matx33d &R2, const cv::Vec3d &t2, - const cv::Vec3d &v); +cv::Matx33d PlaneInducedHomography(const cv::Matx33d& K1, const cv::Matx33d& R1, + const cv::Vec3d& t1, const cv::Matx33d& K2, + const cv::Matx33d& R2, const cv::Vec3d& t2, + const cv::Vec3d& v); -cv::Matx33f PlaneInducedHomographyBaked(const cv::Matx33d &K1inv, - const cv::Matx33d &Q2, - const cv::Vec3d &a2, - const cv::Matx33d &K2, - const cv::Vec3d &v); +cv::Matx33f PlaneInducedHomographyBaked(const cv::Matx33d& K1inv, + const cv::Matx33d& Q2, + const cv::Vec3d& a2, + const cv::Matx33d& K2, + const cv::Vec3d& v); -cv::Vec3d Project(const cv::Vec3d &x, const cv::Matx33d &K, - const cv::Matx33d &R, const cv::Vec3d &t); +cv::Vec3d Project(const cv::Vec3d& x, const cv::Matx33d& K, + const cv::Matx33d& R, const cv::Vec3d& t); -cv::Vec3d Backproject(double x, double y, double depth, const cv::Matx33d &K, - const cv::Matx33d &R, const cv::Vec3d &t); +cv::Vec3d Backproject(double x, double y, double depth, const cv::Matx33d& K, + const cv::Matx33d& R, const cv::Vec3d& t); -float DepthOfPlaneBackprojection(double x, double y, const cv::Matx33d &K, - const cv::Vec3d &plane); +float DepthOfPlaneBackprojection(double x, double y, const cv::Matx33d& K, + const cv::Vec3d& plane); -cv::Vec3f PlaneFromDepthAndNormal(float x, float y, const cv::Matx33d &K, - float depth, const cv::Vec3f &normal); +cv::Vec3f PlaneFromDepthAndNormal(float x, float y, const cv::Matx33d& K, + float depth, const cv::Vec3f& normal); float UniformRand(float a, float b); @@ -57,38 +57,38 @@ struct DepthmapEstimatorResult { class DepthmapEstimator { public: DepthmapEstimator(); - void AddView(const double *pK, const double *pR, const double *pt, - const unsigned char *pimage, const unsigned char *pmask, + void AddView(const double* pK, const double* pR, const double* pt, + const unsigned char* pimage, const unsigned char* pmask, int width, int height); void SetDepthRange(double min_depth, double max_depth, int num_depth_planes); void SetPatchMatchIterations(int n); void SetPatchSize(int size); void SetMinPatchSD(float sd); - void ComputeBruteForce(DepthmapEstimatorResult *result); - void ComputePatchMatch(DepthmapEstimatorResult *result); - void ComputePatchMatchSample(DepthmapEstimatorResult *result); - void AssignMatrices(DepthmapEstimatorResult *result); - void RandomInitialization(DepthmapEstimatorResult *result, bool sample); - void ComputeIgnoreMask(DepthmapEstimatorResult *result); + void ComputeBruteForce(DepthmapEstimatorResult* result); + void ComputePatchMatch(DepthmapEstimatorResult* result); + void ComputePatchMatchSample(DepthmapEstimatorResult* result); + void AssignMatrices(DepthmapEstimatorResult* result); + void RandomInitialization(DepthmapEstimatorResult* result, bool sample); + void ComputeIgnoreMask(DepthmapEstimatorResult* result); float PatchVariance(int i, int j); - void PatchMatchForwardPass(DepthmapEstimatorResult *result, bool sample); - void PatchMatchBackwardPass(DepthmapEstimatorResult *result, bool sample); - void PatchMatchUpdatePixel(DepthmapEstimatorResult *result, int i, int j, + void PatchMatchForwardPass(DepthmapEstimatorResult* result, bool sample); + void PatchMatchBackwardPass(DepthmapEstimatorResult* result, bool sample); + void PatchMatchUpdatePixel(DepthmapEstimatorResult* result, int i, int j, int adjacent[2][2], bool sample); - void CheckPlaneCandidate(DepthmapEstimatorResult *result, int i, int j, - const cv::Vec3f &plane); - void CheckPlaneImageCandidate(DepthmapEstimatorResult *result, int i, int j, - const cv::Vec3f &plane, int nghbr); - void AssignPixel(DepthmapEstimatorResult *result, int i, int j, - const float depth, const cv::Vec3f &plane, const float score, + void CheckPlaneCandidate(DepthmapEstimatorResult* result, int i, int j, + const cv::Vec3f& plane); + void CheckPlaneImageCandidate(DepthmapEstimatorResult* result, int i, int j, + const cv::Vec3f& plane, int nghbr); + void AssignPixel(DepthmapEstimatorResult* result, int i, int j, + const float depth, const cv::Vec3f& plane, const float score, const int nghbr); - void ComputePlaneScore(int i, int j, const cv::Vec3f &plane, float *score, - int *nghbr); - float ComputePlaneImageScoreUnoptimized(int i, int j, const cv::Vec3f &plane, + void ComputePlaneScore(int i, int j, const cv::Vec3f& plane, float* score, + int* nghbr); + float ComputePlaneImageScoreUnoptimized(int i, int j, const cv::Vec3f& plane, int other); - float ComputePlaneImageScore(int i, int j, const cv::Vec3f &plane, int other); + float ComputePlaneImageScore(int i, int j, const cv::Vec3f& plane, int other); float BilateralWeight(float dcolor, float dx, float dy); - void PostProcess(DepthmapEstimatorResult *result); + void PostProcess(DepthmapEstimatorResult* result); private: std::vector images_; @@ -115,9 +115,9 @@ class DepthmapCleaner { DepthmapCleaner(); void SetSameDepthThreshold(float t); void SetMinConsistentViews(int n); - void AddView(const double *pK, const double *pR, const double *pt, - const float *pdepth, int width, int height); - void Clean(cv::Mat *clean_depth); + void AddView(const double* pK, const double* pR, const double* pt, + const float* pdepth, int width, int height); + void Clean(cv::Mat* clean_depth); private: std::vector depths_; @@ -132,14 +132,14 @@ class DepthmapPruner { public: DepthmapPruner(); void SetSameDepthThreshold(float t); - void AddView(const double *pK, const double *pR, const double *pt, - const float *pdepth, const float *pplane, - const unsigned char *pcolor, const unsigned char *plabel, + void AddView(const double* pK, const double* pR, const double* pt, + const float* pdepth, const float* pplane, + const unsigned char* pcolor, const unsigned char* plabel, int width, int height); - void Prune(std::vector *merged_points, - std::vector *merged_normals, - std::vector *merged_colors, - std::vector *merged_labels); + void Prune(std::vector* merged_points, + std::vector* merged_normals, + std::vector* merged_colors, + std::vector* merged_labels); private: std::vector depths_; diff --git a/opensfm/src/dense/depthmap_bind.h b/opensfm/src/dense/depthmap_bind.h index 2c62127fe..83dfca782 100644 --- a/opensfm/src/dense/depthmap_bind.h +++ b/opensfm/src/dense/depthmap_bind.h @@ -3,15 +3,16 @@ #include #include -using namespace foundation; - namespace dense { class DepthmapEstimatorWrapper { public: - void AddView(pyarray_d K, pyarray_d R, pyarray_d t, pyarray_uint8 image, - pyarray_uint8 mask) { - if ((image.shape(0) != mask.shape(0)) || (image.shape(1) != mask.shape(1))){ + void AddView(const foundation::pyarray_d& K, const foundation::pyarray_d& R, + const foundation::pyarray_d& t, + const foundation::pyarray_uint8& image, + const foundation::pyarray_uint8& mask) { + if ((image.shape(0) != mask.shape(0)) || + (image.shape(1) != mask.shape(1))) { throw std::invalid_argument("image and mask must have matching shapes."); } de_.AddView(K.data(), R.data(), t.data(), image.data(), mask.data(), @@ -28,7 +29,7 @@ class DepthmapEstimatorWrapper { void SetMinPatchSD(float sd) { de_.SetMinPatchSD(sd); } - py::object ComputePatchMatch() { + py::list ComputePatchMatch() { DepthmapEstimatorResult result; { py::gil_scoped_release release; @@ -37,7 +38,7 @@ class DepthmapEstimatorWrapper { return ComputeReturnValues(result); } - py::object ComputePatchMatchSample() { + py::list ComputePatchMatchSample() { DepthmapEstimatorResult result; { py::gil_scoped_release release; @@ -46,7 +47,7 @@ class DepthmapEstimatorWrapper { return ComputeReturnValues(result); } - py::object ComputeBruteForce() { + py::list ComputeBruteForce() { DepthmapEstimatorResult result; { py::gil_scoped_release release; @@ -55,17 +56,17 @@ class DepthmapEstimatorWrapper { return ComputeReturnValues(result); } - py::object ComputeReturnValues(const DepthmapEstimatorResult &result) { + py::list ComputeReturnValues(const DepthmapEstimatorResult& result) { py::list retn; - retn.append(py_array_from_data(result.depth.ptr(0), - result.depth.rows, result.depth.cols)); - retn.append(py_array_from_data(result.plane.ptr(0), - result.plane.rows, result.plane.cols, 3)); - retn.append(py_array_from_data(result.score.ptr(0), - result.score.rows, result.score.cols)); - retn.append(py_array_from_data(result.nghbr.ptr(0), result.nghbr.rows, - result.nghbr.cols)); - return std::move(retn); + retn.append(foundation::py_array_from_data( + result.depth.ptr(0), result.depth.rows, result.depth.cols)); + retn.append(foundation::py_array_from_data( + result.plane.ptr(0), result.plane.rows, result.plane.cols, 3)); + retn.append(foundation::py_array_from_data( + result.score.ptr(0), result.score.rows, result.score.cols)); + retn.append(foundation::py_array_from_data( + result.nghbr.ptr(0), result.nghbr.rows, result.nghbr.cols)); + return retn; } private: @@ -78,18 +79,21 @@ class DepthmapCleanerWrapper { void SetMinConsistentViews(int n) { dc_.SetMinConsistentViews(n); } - void AddView(pyarray_d K, pyarray_d R, pyarray_d t, pyarray_f depth) { + void AddView(const foundation::pyarray_d& K, const foundation::pyarray_d& R, + const foundation::pyarray_d& t, + const foundation::pyarray_f& depth) { dc_.AddView(K.data(), R.data(), t.data(), depth.data(), depth.shape(1), depth.shape(0)); } - py::object Clean() { + foundation::pyarray_f Clean() { cv::Mat depth; { py::gil_scoped_release release; dc_.Clean(&depth); } - return py_array_from_data(depth.ptr(0), depth.rows, depth.cols); + return foundation::py_array_from_data(depth.ptr(0), depth.rows, + depth.cols); } private: @@ -100,22 +104,29 @@ class DepthmapPrunerWrapper { public: void SetSameDepthThreshold(float t) { dp_.SetSameDepthThreshold(t); } - void AddView(pyarray_d K, pyarray_d R, pyarray_d t, pyarray_f depth, - pyarray_f plane, pyarray_uint8 color, pyarray_uint8 label) { - if ((depth.shape(0) != plane.shape(0)) || (depth.shape(1) != plane.shape(1))){ + void AddView(const foundation::pyarray_d& K, const foundation::pyarray_d& R, + const foundation::pyarray_d& t, + const foundation::pyarray_f& depth, + const foundation::pyarray_f& plane, + const foundation::pyarray_uint8& color, + const foundation::pyarray_uint8& label) { + if ((depth.shape(0) != plane.shape(0)) || + (depth.shape(1) != plane.shape(1))) { throw std::invalid_argument("depth and plane must have matching shapes."); } - if ((depth.shape(0) != color.shape(0)) || (depth.shape(1) != color.shape(1))){ + if ((depth.shape(0) != color.shape(0)) || + (depth.shape(1) != color.shape(1))) { throw std::invalid_argument("depth and color must have matching shapes."); } - if ((depth.shape(0) != label.shape(0)) || (depth.shape(1) != label.shape(1))){ + if ((depth.shape(0) != label.shape(0)) || + (depth.shape(1) != label.shape(1))) { throw std::invalid_argument("depth and label must have matching shapes."); } dp_.AddView(K.data(), R.data(), t.data(), depth.data(), plane.data(), color.data(), label.data(), depth.shape(1), depth.shape(0)); } - py::object Prune() { + py::list Prune() { std::vector points; std::vector normals; std::vector colors; @@ -128,11 +139,11 @@ class DepthmapPrunerWrapper { py::list retn; int n = int(points.size()) / 3; - retn.append(py_array_from_data(&points[0], n, 3)); - retn.append(py_array_from_data(&normals[0], n, 3)); - retn.append(py_array_from_data(&colors[0], n, 3)); - retn.append(py_array_from_data(&labels[0], n)); - return std::move(retn); + retn.append(foundation::py_array_from_data(points.data(), n, 3)); + retn.append(foundation::py_array_from_data(normals.data(), n, 3)); + retn.append(foundation::py_array_from_data(colors.data(), n, 3)); + retn.append(foundation::py_array_from_data(labels.data(), n)); + return retn; } private: diff --git a/opensfm/src/dense/openmvs_exporter.h b/opensfm/src/dense/openmvs_exporter.h index 088223f7b..cdd1ba6ff 100644 --- a/opensfm/src/dense/openmvs_exporter.h +++ b/opensfm/src/dense/openmvs_exporter.h @@ -6,7 +6,8 @@ namespace dense { class OpenMVSExporter { public: - void AddCamera(const std::string &camera_id, pyarray_d K, uint32_t width, uint32_t height) { + void AddCamera(const std::string& camera_id, foundation::pyarray_d K, + uint32_t width, uint32_t height) { MVS::Interface::Platform platform; platform.name = camera_id; MVS::Interface::Platform::Camera camera; @@ -21,12 +22,13 @@ class OpenMVSExporter { scene_.platforms.push_back(platform); } - void AddShot(const std::string &path, const std::string &maskPath, const std::string &shot_id, - const std::string &camera_id, pyarray_d R, pyarray_d C) { - const double *C_data = C.data(); + void AddShot(const std::string& path, const std::string& maskPath, + const std::string& shot_id, const std::string& camera_id, + foundation::pyarray_d R, foundation::pyarray_d C) { + const double* C_data = C.data(); int platform_id = platform_ids_[camera_id]; - MVS::Interface::Platform &platform = scene_.platforms[platform_id]; + MVS::Interface::Platform& platform = scene_.platforms[platform_id]; MVS::Interface::Platform::Pose pose; pose.R = cv::Matx33d(R.data()); @@ -45,8 +47,8 @@ class OpenMVSExporter { scene_.images.push_back(image); } - void AddPoint(pyarray_d coordinates, py::list shot_ids) { - const double *x = coordinates.data(); + void AddPoint(foundation::pyarray_d coordinates, py::list shot_ids) { + const double* x = coordinates.data(); MVS::Interface::Vertex vertex; vertex.X = cv::Point3_(x[0], x[1], x[2]); diff --git a/opensfm/src/dense/pydense.pyi b/opensfm/src/dense/pydense.pyi index 13a398277..5f37f9447 100644 --- a/opensfm/src/dense/pydense.pyi +++ b/opensfm/src/dense/pydense.pyi @@ -2,41 +2,87 @@ # Do not manually edit # To regenerate: # $ buck run //mapillary/opensfm/opensfm/src/dense:pydense_stubgen -# Use proper mode, e.g. @arvr/mode/linux/dev for arvr # @generated +# +# Tip: Be sure to run this with the build mode you use for your project, e.g., +# @//arvr/mode/linux/opt (or dev) in arvr. +# +# Ignore errors for [24] untyped generics. +# pyre-ignore-all-errors[24] import numpy from typing import * -__all__ = [ -"DepthmapCleaner", -"DepthmapEstimator", -"DepthmapPruner", -"OpenMVSExporter" + +__all__ = [ + "DepthmapCleaner", + "DepthmapEstimator", + "DepthmapPruner", + "OpenMVSExporter", + "StaticExtensionLoader", ] + class DepthmapCleaner: def __init__(self) -> None: ... - def add_view(self, arg0: numpy.ndarray, arg1: numpy.ndarray, arg2: numpy.ndarray, arg3: numpy.ndarray) -> None: ... - def clean(self) -> object: ... + def add_view( + self, + arg0: numpy.typing.NDArray, + arg1: numpy.typing.NDArray, + arg2: numpy.typing.NDArray, + arg3: numpy.typing.NDArray, + ) -> None: ... + def clean(self) -> numpy.typing.NDArray: ... def set_min_consistent_views(self, arg0: int) -> None: ... def set_same_depth_threshold(self, arg0: float) -> None: ... + class DepthmapEstimator: def __init__(self) -> None: ... - def add_view(self, arg0: numpy.ndarray, arg1: numpy.ndarray, arg2: numpy.ndarray, arg3: numpy.ndarray, arg4: numpy.ndarray) -> None: ... - def compute_brute_force(self) -> object: ... - def compute_patch_match(self) -> object: ... - def compute_patch_match_sample(self) -> object: ... + def add_view( + self, + arg0: numpy.typing.NDArray, + arg1: numpy.typing.NDArray, + arg2: numpy.typing.NDArray, + arg3: numpy.typing.NDArray, + arg4: numpy.typing.NDArray, + ) -> None: ... + def compute_brute_force(self) -> list: ... + def compute_patch_match(self) -> list: ... + def compute_patch_match_sample(self) -> list: ... def set_depth_range(self, arg0: float, arg1: float, arg2: int) -> None: ... def set_min_patch_sd(self, arg0: float) -> None: ... def set_patch_size(self, arg0: int) -> None: ... def set_patchmatch_iterations(self, arg0: int) -> None: ... + class DepthmapPruner: def __init__(self) -> None: ... - def add_view(self, arg0: numpy.ndarray, arg1: numpy.ndarray, arg2: numpy.ndarray, arg3: numpy.ndarray, arg4: numpy.ndarray, arg5: numpy.ndarray, arg6: numpy.ndarray) -> None: ... - def prune(self) -> object: ... + def add_view( + self, + arg0: numpy.typing.NDArray, + arg1: numpy.typing.NDArray, + arg2: numpy.typing.NDArray, + arg3: numpy.typing.NDArray, + arg4: numpy.typing.NDArray, + arg5: numpy.typing.NDArray, + arg6: numpy.typing.NDArray, + ) -> None: ... + def prune(self) -> list: ... def set_same_depth_threshold(self, arg0: float) -> None: ... + class OpenMVSExporter: def __init__(self) -> None: ... - def add_camera(self, arg0: str, arg1: numpy.ndarray, arg2: int, arg3: int) -> None: ... - def add_point(self, arg0: numpy.ndarray, arg1: list) -> None: ... - def add_shot(self, arg0: str, arg1: str, arg2: str, arg3: numpy.ndarray, arg4: numpy.ndarray) -> None: ... + def add_camera( + self, arg0: str, arg1: numpy.typing.NDArray, arg2: int, arg3: int + ) -> None: ... + def add_point(self, arg0: numpy.typing.NDArray, arg1: list) -> None: ... + def add_shot( + self, + arg0: str, + arg1: str, + arg2: str, + arg3: str, + arg4: numpy.typing.NDArray, + arg5: numpy.typing.NDArray, + ) -> None: ... def export(self, arg0: str) -> None: ... + +class StaticExtensionLoader: + pass diff --git a/opensfm/src/dense/python/pybind.cc b/opensfm/src/dense/python/pybind.cc index c85f8157f..da65c3668 100644 --- a/opensfm/src/dense/python/pybind.cc +++ b/opensfm/src/dense/python/pybind.cc @@ -1,10 +1,8 @@ -#include -#include -#include - #include #include #include +#include +#include PYBIND11_MODULE(pydense, m) { py::class_(m, "OpenMVSExporter") diff --git a/opensfm/src/dense/src/depthmap.cc b/opensfm/src/dense/src/depthmap.cc index 696634d91..bdde080d6 100644 --- a/opensfm/src/dense/src/depthmap.cc +++ b/opensfm/src/dense/src/depthmap.cc @@ -1,19 +1,18 @@ #include "../depthmap.h" #include -#include #include namespace dense { static const double z_epsilon = 1e-8; -bool IsInsideImage(const cv::Mat &image, int i, int j) { +bool IsInsideImage(const cv::Mat& image, int i, int j) { return i >= 0 && i < image.rows && j >= 0 && j < image.cols; } template -float LinearInterpolation(const cv::Mat &image, float y, float x) { +float LinearInterpolation(const cv::Mat& image, float y, float x) { if (x < 0.0f || x >= image.cols - 1 || y < 0.0f || y >= image.rows - 1) { return 0.0f; } @@ -30,7 +29,7 @@ float LinearInterpolation(const cv::Mat &image, float y, float x) { return (1 - dy) * im0 + dy * im1; } -float Variance(float *x, int n) { +float Variance(float* x, int n) { float sum = 0; for (int i = 0; i < n; ++i) { sum += x[i]; @@ -74,8 +73,8 @@ float NCCEstimator::Get() { } } -void ApplyHomography(const cv::Matx33f &H, float x1, float y1, float *x2, - float *y2) { +void ApplyHomography(const cv::Matx33f& H, float x1, float y1, float* x2, + float* y2) { float w = H(2, 0) * x1 + H(2, 1) * y1 + H(2, 2); if (w == 0.0) { *x2 = 0.0f; @@ -86,47 +85,50 @@ void ApplyHomography(const cv::Matx33f &H, float x1, float y1, float *x2, *y2 = (H(1, 0) * x1 + H(1, 1) * y1 + H(1, 2)) / w; } -cv::Matx33d PlaneInducedHomography(const cv::Matx33d &K1, const cv::Matx33d &R1, - const cv::Vec3d &t1, const cv::Matx33d &K2, - const cv::Matx33d &R2, const cv::Vec3d &t2, - const cv::Vec3d &v) { +cv::Matx33d PlaneInducedHomography(const cv::Matx33d& K1, const cv::Matx33d& R1, + const cv::Vec3d& t1, const cv::Matx33d& K2, + const cv::Matx33d& R2, const cv::Vec3d& t2, + const cv::Vec3d& v) { cv::Matx33d R2R1 = R2 * R1.t(); return K2 * (R2R1 + (R2R1 * t1 - t2) * v.t()) * K1.inv(); } -cv::Matx33f PlaneInducedHomographyBaked(const cv::Matx33d &K1inv, - const cv::Matx33d &Q2, - const cv::Vec3d &a2, - const cv::Matx33d &K2, - const cv::Vec3d &v) { +cv::Matx33f PlaneInducedHomographyBaked(const cv::Matx33d& K1inv, + const cv::Matx33d& Q2, + const cv::Vec3d& a2, + const cv::Matx33d& K2, + const cv::Vec3d& v) { return K2 * (Q2 + a2 * v.t()) * K1inv; } -cv::Vec3d Project(const cv::Vec3d &x, const cv::Matx33d &K, - const cv::Matx33d &R, const cv::Vec3d &t) { +cv::Vec3d Project(const cv::Vec3d& x, const cv::Matx33d& K, + const cv::Matx33d& R, const cv::Vec3d& t) { return K * (R * x + t); } -cv::Vec3d Backproject(double x, double y, double depth, const cv::Matx33d &K, - const cv::Matx33d &R, const cv::Vec3d &t) { +cv::Vec3d Backproject(double x, double y, double depth, const cv::Matx33d& K, + const cv::Matx33d& R, const cv::Vec3d& t) { return R.t() * (depth * K.inv() * cv::Vec3d(x, y, 1) - t); } -float DepthOfPlaneBackprojection(double x, double y, const cv::Matx33d &K, - const cv::Vec3d &plane) { +float DepthOfPlaneBackprojection(double x, double y, const cv::Matx33d& K, + const cv::Vec3d& plane) { float denom = -(plane.t() * K.inv() * cv::Vec3d(x, y, 1))(0); return 1.0f / std::max(1e-6f, denom); } -cv::Vec3f PlaneFromDepthAndNormal(float x, float y, const cv::Matx33d &K, - float depth, const cv::Vec3f &normal) { +cv::Vec3f PlaneFromDepthAndNormal(float x, float y, const cv::Matx33d& K, + float depth, const cv::Vec3f& normal) { cv::Vec3f point = depth * K.inv() * cv::Vec3d(x, y, 1); float denom = -normal.dot(point); return normal / std::max(1e-6f, denom); } float UniformRand(float a, float b) { - return a + (b - a) * float(rand()) / RAND_MAX; + // Note that float(RAND_MAX) cannot be exactly represented as a float. We + // ignore the small inaccuracy here; this is already a bad way to get random + // numbers. + return a + (b - a) * float(rand()) / static_cast(RAND_MAX); } DepthmapEstimator::DepthmapEstimator() @@ -141,9 +143,9 @@ DepthmapEstimator::DepthmapEstimator() unit_normal_(0, 1), patch_variance_buffer_(patch_size_ * patch_size_) {} -void DepthmapEstimator::AddView(const double *pK, const double *pR, - const double *pt, const unsigned char *pimage, - const unsigned char *pmask, int width, +void DepthmapEstimator::AddView(const double* pK, const double* pR, + const double* pt, const unsigned char* pimage, + const unsigned char* pmask, int width, int height) { Ks_.emplace_back(pK); Rs_.emplace_back(pR); @@ -151,8 +153,8 @@ void DepthmapEstimator::AddView(const double *pK, const double *pR, Kinvs_.emplace_back(Ks_.back().inv()); Qs_.emplace_back(Rs_.back() * Rs_.front().t()); as_.emplace_back(Qs_.back() * ts_.front() - ts_.back()); - images_.emplace_back(cv::Mat(height, width, CV_8U, (void *)pimage).clone()); - masks_.emplace_back(cv::Mat(height, width, CV_8U, (void *)pmask).clone()); + images_.emplace_back(cv::Mat(height, width, CV_8U, (void*)pimage).clone()); + masks_.emplace_back(cv::Mat(height, width, CV_8U, (void*)pmask).clone()); std::size_t size = images_.size(); int a = (size > 1) ? 1 : 0; int b = (size > 1) ? size - 1 : 0; @@ -179,7 +181,7 @@ void DepthmapEstimator::SetMinPatchSD(float sd) { min_patch_variance_ = sd * sd; } -void DepthmapEstimator::ComputeBruteForce(DepthmapEstimatorResult *result) { +void DepthmapEstimator::ComputeBruteForce(DepthmapEstimatorResult* result) { AssignMatrices(result); int hpz = (patch_size_ - 1) / 2; @@ -197,7 +199,7 @@ void DepthmapEstimator::ComputeBruteForce(DepthmapEstimatorResult *result) { } } -void DepthmapEstimator::ComputePatchMatch(DepthmapEstimatorResult *result) { +void DepthmapEstimator::ComputePatchMatch(DepthmapEstimatorResult* result) { AssignMatrices(result); RandomInitialization(result, false); ComputeIgnoreMask(result); @@ -211,7 +213,7 @@ void DepthmapEstimator::ComputePatchMatch(DepthmapEstimatorResult *result) { } void DepthmapEstimator::ComputePatchMatchSample( - DepthmapEstimatorResult *result) { + DepthmapEstimatorResult* result) { AssignMatrices(result); RandomInitialization(result, true); ComputeIgnoreMask(result); @@ -224,7 +226,7 @@ void DepthmapEstimator::ComputePatchMatchSample( PostProcess(result); } -void DepthmapEstimator::AssignMatrices(DepthmapEstimatorResult *result) { +void DepthmapEstimator::AssignMatrices(DepthmapEstimatorResult* result) { result->depth = cv::Mat(images_[0].rows, images_[0].cols, CV_32F, 0.0f); result->plane = cv::Mat(images_[0].rows, images_[0].cols, CV_32FC3, 0.0f); result->score = cv::Mat(images_[0].rows, images_[0].cols, CV_32F, 0.0f); @@ -232,7 +234,7 @@ void DepthmapEstimator::AssignMatrices(DepthmapEstimatorResult *result) { cv::Mat(images_[0].rows, images_[0].cols, CV_32S, cv::Scalar(0)); } -void DepthmapEstimator::RandomInitialization(DepthmapEstimatorResult *result, +void DepthmapEstimator::RandomInitialization(DepthmapEstimatorResult* result, bool sample) { int hpz = (patch_size_ - 1) / 2; for (int i = hpz; i < result->depth.rows - hpz; ++i) { @@ -253,7 +255,7 @@ void DepthmapEstimator::RandomInitialization(DepthmapEstimatorResult *result, } } -void DepthmapEstimator::ComputeIgnoreMask(DepthmapEstimatorResult *result) { +void DepthmapEstimator::ComputeIgnoreMask(DepthmapEstimatorResult* result) { int hpz = (patch_size_ - 1) / 2; for (int i = hpz; i < result->depth.rows - hpz; ++i) { for (int j = hpz; j < result->depth.cols - hpz; ++j) { @@ -267,7 +269,7 @@ void DepthmapEstimator::ComputeIgnoreMask(DepthmapEstimatorResult *result) { } float DepthmapEstimator::PatchVariance(int i, int j) { - float *patch = patch_variance_buffer_.data(); + float* patch = patch_variance_buffer_.data(); int hpz = (patch_size_ - 1) / 2; int counter = 0; for (int u = -hpz; u <= hpz; ++u) { @@ -278,7 +280,7 @@ float DepthmapEstimator::PatchVariance(int i, int j) { return Variance(patch, patch_size_ * patch_size_); } -void DepthmapEstimator::PatchMatchForwardPass(DepthmapEstimatorResult *result, +void DepthmapEstimator::PatchMatchForwardPass(DepthmapEstimatorResult* result, bool sample) { int adjacent[2][2] = {{-1, 0}, {0, -1}}; int hpz = (patch_size_ - 1) / 2; @@ -289,7 +291,7 @@ void DepthmapEstimator::PatchMatchForwardPass(DepthmapEstimatorResult *result, } } -void DepthmapEstimator::PatchMatchBackwardPass(DepthmapEstimatorResult *result, +void DepthmapEstimator::PatchMatchBackwardPass(DepthmapEstimatorResult* result, bool sample) { int adjacent[2][2] = {{0, 1}, {1, 0}}; int hpz = (patch_size_ - 1) / 2; @@ -300,7 +302,7 @@ void DepthmapEstimator::PatchMatchBackwardPass(DepthmapEstimatorResult *result, } } -void DepthmapEstimator::PatchMatchUpdatePixel(DepthmapEstimatorResult *result, +void DepthmapEstimator::PatchMatchUpdatePixel(DepthmapEstimatorResult* result, int i, int j, int adjacent[2][2], bool sample) { // Ignore pixels with depth == 0. @@ -371,9 +373,9 @@ void DepthmapEstimator::PatchMatchUpdatePixel(DepthmapEstimatorResult *result, CheckPlaneImageCandidate(result, i, j, plane, other_nghbr); } -void DepthmapEstimator::CheckPlaneCandidate(DepthmapEstimatorResult *result, +void DepthmapEstimator::CheckPlaneCandidate(DepthmapEstimatorResult* result, int i, int j, - const cv::Vec3f &plane) { + const cv::Vec3f& plane) { float score; int nghbr; ComputePlaneScore(i, j, plane, &score, &nghbr); @@ -384,7 +386,7 @@ void DepthmapEstimator::CheckPlaneCandidate(DepthmapEstimatorResult *result, } void DepthmapEstimator::CheckPlaneImageCandidate( - DepthmapEstimatorResult *result, int i, int j, const cv::Vec3f &plane, + DepthmapEstimatorResult* result, int i, int j, const cv::Vec3f& plane, int nghbr) { float score = ComputePlaneImageScore(i, j, plane, nghbr); if (score > result->score.at(i, j)) { @@ -393,9 +395,9 @@ void DepthmapEstimator::CheckPlaneImageCandidate( } } -void DepthmapEstimator::AssignPixel(DepthmapEstimatorResult *result, int i, +void DepthmapEstimator::AssignPixel(DepthmapEstimatorResult* result, int i, int j, const float depth, - const cv::Vec3f &plane, const float score, + const cv::Vec3f& plane, const float score, const int nghbr) { result->depth.at(i, j) = depth; result->plane.at(i, j) = plane; @@ -403,8 +405,8 @@ void DepthmapEstimator::AssignPixel(DepthmapEstimatorResult *result, int i, result->nghbr.at(i, j) = nghbr; } -void DepthmapEstimator::ComputePlaneScore(int i, int j, const cv::Vec3f &plane, - float *score, int *nghbr) { +void DepthmapEstimator::ComputePlaneScore(int i, int j, const cv::Vec3f& plane, + float* score, int* nghbr) { *score = -1.0f; *nghbr = 0; for (int other = 1; other < images_.size(); ++other) { @@ -417,7 +419,7 @@ void DepthmapEstimator::ComputePlaneScore(int i, int j, const cv::Vec3f &plane, } float DepthmapEstimator::ComputePlaneImageScoreUnoptimized( - int i, int j, const cv::Vec3f &plane, int other) { + int i, int j, const cv::Vec3f& plane, int other) { cv::Matx33f H = PlaneInducedHomographyBaked(Kinvs_[0], Qs_[other], as_[other], Ks_[other], plane); int hpz = (patch_size_ - 1) / 2; @@ -437,7 +439,7 @@ float DepthmapEstimator::ComputePlaneImageScoreUnoptimized( } float DepthmapEstimator::ComputePlaneImageScore(int i, int j, - const cv::Vec3f &plane, + const cv::Vec3f& plane, int other) { cv::Matx33f H = PlaneInducedHomographyBaked(Kinvs_[0], Qs_[other], as_[other], Ks_[other], plane); @@ -484,7 +486,7 @@ float DepthmapEstimator::BilateralWeight(float dcolor, float dx, float dy) { (dx * dx + dy * dy) * dx_factor); } -void DepthmapEstimator::PostProcess(DepthmapEstimatorResult *result) { +void DepthmapEstimator::PostProcess(DepthmapEstimatorResult* result) { cv::Mat depth_filtered; cv::medianBlur(result->depth, depth_filtered, 5); @@ -510,16 +512,16 @@ void DepthmapCleaner::SetMinConsistentViews(int n) { min_consistent_views_ = n; } -void DepthmapCleaner::AddView(const double *pK, const double *pR, - const double *pt, const float *pdepth, int width, +void DepthmapCleaner::AddView(const double* pK, const double* pR, + const double* pt, const float* pdepth, int width, int height) { Ks_.emplace_back(pK); Rs_.emplace_back(pR); ts_.emplace_back(pt); - depths_.emplace_back(cv::Mat(height, width, CV_32F, (void *)pdepth).clone()); + depths_.emplace_back(cv::Mat(height, width, CV_32F, (void*)pdepth).clone()); } -void DepthmapCleaner::Clean(cv::Mat *clean_depth) { +void DepthmapCleaner::Clean(cv::Mat* clean_depth) { *clean_depth = cv::Mat(depths_[0].rows, depths_[0].cols, CV_32F, 0.0f); for (int i = 0; i < depths_[0].rows; ++i) { @@ -558,25 +560,24 @@ void DepthmapPruner::SetSameDepthThreshold(float t) { same_depth_threshold_ = t; } -void DepthmapPruner::AddView(const double *pK, const double *pR, - const double *pt, const float *pdepth, - const float *pplane, const unsigned char *pcolor, - const unsigned char *plabel, int width, +void DepthmapPruner::AddView(const double* pK, const double* pR, + const double* pt, const float* pdepth, + const float* pplane, const unsigned char* pcolor, + const unsigned char* plabel, int width, int height) { Ks_.emplace_back(pK); Rs_.emplace_back(pR); ts_.emplace_back(pt); - depths_.emplace_back(cv::Mat(height, width, CV_32F, (void *)pdepth).clone()); - planes_.emplace_back( - cv::Mat(height, width, CV_32FC3, (void *)pplane).clone()); - colors_.emplace_back(cv::Mat(height, width, CV_8UC3, (void *)pcolor).clone()); - labels_.emplace_back(cv::Mat(height, width, CV_8U, (void *)plabel).clone()); + depths_.emplace_back(cv::Mat(height, width, CV_32F, (void*)pdepth).clone()); + planes_.emplace_back(cv::Mat(height, width, CV_32FC3, (void*)pplane).clone()); + colors_.emplace_back(cv::Mat(height, width, CV_8UC3, (void*)pcolor).clone()); + labels_.emplace_back(cv::Mat(height, width, CV_8U, (void*)plabel).clone()); } -void DepthmapPruner::Prune(std::vector *merged_points, - std::vector *merged_normals, - std::vector *merged_colors, - std::vector *merged_labels) { +void DepthmapPruner::Prune(std::vector* merged_points, + std::vector* merged_normals, + std::vector* merged_colors, + std::vector* merged_labels) { cv::Matx33f Rinv = Rs_[0].t(); for (int i = 0; i < depths_[0].rows; ++i) { for (int j = 0; j < depths_[0].cols; ++j) { diff --git a/opensfm/src/dense/test/depthmap_test.cc b/opensfm/src/dense/test/depthmap_test.cc index 27b7d8651..660c60052 100644 --- a/opensfm/src/dense/test/depthmap_test.cc +++ b/opensfm/src/dense/test/depthmap_test.cc @@ -1,5 +1,6 @@ #include #include + #include namespace { diff --git a/opensfm/src/features/CMakeLists.txt b/opensfm/src/features/CMakeLists.txt index e776a12f5..ba5e72ac7 100644 --- a/opensfm/src/features/CMakeLists.txt +++ b/opensfm/src/features/CMakeLists.txt @@ -28,3 +28,4 @@ target_link_libraries(pyfeatures set_target_properties(pyfeatures PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${opensfm_SOURCE_DIR}/.." ) +install(TARGETS pyfeatures LIBRARY DESTINATION .) diff --git a/opensfm/src/features/matching.h b/opensfm/src/features/matching.h index 65a2ddbcd..0903ee981 100644 --- a/opensfm/src/features/matching.h +++ b/opensfm/src/features/matching.h @@ -13,9 +13,9 @@ py::array_t match_using_words(foundation::pyarray_f features1, foundation::pyarray_int words2, float lowes_ratio, int max_checks); -VecXf compute_vlad_descriptor(const MatXf &features, const MatXf &vlad_centers); +VecXf compute_vlad_descriptor(const MatXf& features, const MatXf& vlad_centers); std::pair, std::vector> compute_vlad_distances( - const std::map &vlad_descriptors, - const std::string &image, std::set &other_images); + const std::map& vlad_descriptors, + const std::string& image, std::set& other_images); } // namespace features diff --git a/opensfm/src/features/pyfeatures.pyi b/opensfm/src/features/pyfeatures.pyi index 755d7c89d..543e1feee 100644 --- a/opensfm/src/features/pyfeatures.pyi +++ b/opensfm/src/features/pyfeatures.pyi @@ -2,8 +2,13 @@ # Do not manually edit # To regenerate: # $ buck run //mapillary/opensfm/opensfm/src/features:pyfeatures_stubgen -# Use proper mode, e.g. @arvr/mode/linux/dev for arvr # @generated +# +# Tip: Be sure to run this with the build mode you use for your project, e.g., +# @//arvr/mode/linux/opt (or dev) in arvr. +# +# Ignore errors for [5] global variable types and [24] untyped generics. +# pyre-ignore-all-errors[5,24] import numpy from typing import * diff --git a/opensfm/src/features/src/akaze_bind.cc b/opensfm/src/features/src/akaze_bind.cc index 1c4d05656..bd86bd378 100644 --- a/opensfm/src/features/src/akaze_bind.cc +++ b/opensfm/src/features/src/akaze_bind.cc @@ -9,8 +9,7 @@ namespace features { py::tuple akaze(foundation::pyarray_uint8 image, AKAZEOptions options) { py::gil_scoped_release release; - const cv::Mat img(image.shape(0), image.shape(1), CV_8U, - (void *)image.data()); + const cv::Mat img(image.shape(0), image.shape(1), CV_8U, (void*)image.data()); cv::Mat img_32; img.convertTo(img_32, CV_32F, 1.0 / 255.0, 0); @@ -48,10 +47,10 @@ py::tuple akaze(foundation::pyarray_uint8 image, AKAZEOptions options) { if (options.descriptor == MLDB_UPRIGHT || options.descriptor == MLDB) { return py::make_tuple( keys_py, foundation::py_array_from_data(desc.ptr(0), - desc.rows, desc.cols)); + desc.rows, desc.cols)); } return py::make_tuple(keys_py, foundation::py_array_from_data( - desc.ptr(0), desc.rows, desc.cols)); + desc.ptr(0), desc.rows, desc.cols)); } } // namespace features diff --git a/opensfm/src/features/src/hahog.cc b/opensfm/src/features/src/hahog.cc index 6f8002c64..5b398e9e5 100644 --- a/opensfm/src/features/src/hahog.cc +++ b/opensfm/src/features/src/hahog.cc @@ -12,14 +12,14 @@ extern "C" { namespace features { // from VLFeat implementation of _vl_compare_scores -static int vlfeat_compare_scores(const void *a, const void *b) { - float fa = ((VlCovDetFeature *)a)->peakScore; - float fb = ((VlCovDetFeature *)b)->peakScore; +static int vlfeat_compare_scores(const void* a, const void* b) { + float fa = ((VlCovDetFeature*)a)->peakScore; + float fb = ((VlCovDetFeature*)b)->peakScore; return (fb > fa) - (fb < fa); } // select 'target_num_features' for using feature's scores -vl_size select_best_features(VlCovDet *covdet, vl_size num_features, +vl_size select_best_features(VlCovDet* covdet, vl_size num_features, vl_size target_num_features) { if (num_features > target_num_features) { qsort(vl_covdet_get_features(covdet), num_features, sizeof(VlCovDetFeature), @@ -33,11 +33,11 @@ vl_size select_best_features(VlCovDet *covdet, vl_size num_features, // select 'target_num_features' that have a maximum score in their neighbhood. // The neighborhood is computing using the feature's scale and // 'non_extrema_suppression' as : neighborhood = non_extrema_suppression * scale -vl_size run_non_maxima_suppression(VlCovDet *covdet, vl_size num_features, +vl_size run_non_maxima_suppression(VlCovDet* covdet, vl_size num_features, double non_extrema_suppression) { vl_index i, j; double tol = non_extrema_suppression; - VlCovDetFeature *features = (VlCovDetFeature *)vl_covdet_get_features(covdet); + VlCovDetFeature* features = (VlCovDetFeature*)vl_covdet_get_features(covdet); for (i = 0; i < (signed)num_features; ++i) { double x = features[i].frame.x; double y = features[i].frame.y; @@ -49,7 +49,9 @@ vl_size run_non_maxima_suppression(VlCovDet *covdet, vl_size num_features, double dy_ = features[j].frame.y - y; double sigma_ = features[j].frame.a11; double score_ = features[j].peakScore; - if (score_ == 0) continue; + if (score_ == 0) { + continue; + } if (sigma < (1 + tol) * sigma_ && sigma_ < (1 + tol) * sigma && vl_abs_d(dx_) < tol * sigma && vl_abs_d(dy_) < tol * sigma && vl_abs_d(score) > vl_abs_d(score_)) { @@ -67,7 +69,7 @@ vl_size run_non_maxima_suppression(VlCovDet *covdet, vl_size num_features, return j; } -vl_size run_features_selection(VlCovDet *covdet, vl_size target_num_features) { +vl_size run_features_selection(VlCovDet* covdet, vl_size target_num_features) { vl_size numFeaturesKept = vl_covdet_get_num_features(covdet); // keep only 1.5 x targetNumFeatures for speeding-up duplicate detection @@ -89,15 +91,15 @@ vl_size run_features_selection(VlCovDet *covdet, vl_size target_num_features) { } std::vector vlfeat_covdet_extract_orientations( - VlCovDet *covdet, vl_size num_features) { - VlCovDetFeature *features = (VlCovDetFeature *)vl_covdet_get_features(covdet); + VlCovDet* covdet, vl_size num_features) { + VlCovDetFeature* features = (VlCovDetFeature*)vl_covdet_get_features(covdet); std::vector vecFeatures; vecFeatures.reserve(num_features); vl_index i, j; for (i = 0; i < (signed)num_features; ++i) { vl_size numOrientations; VlCovDetFeature feature = features[i]; - VlCovDetFeatureOrientation *orientations = + VlCovDetFeatureOrientation* orientations = vl_covdet_extract_orientations_for_frame(covdet, &numOrientations, feature.frame); @@ -108,7 +110,7 @@ std::vector vlfeat_covdet_extract_orientations( double r2 = sin(orientations[j].angle); vecFeatures.emplace_back(features[i]); - VlCovDetFeature &oriented = vecFeatures.back(); + VlCovDetFeature& oriented = vecFeatures.back(); oriented.orientationScore = orientations[j].score; oriented.frame.a11 = +A[0] * r1 + A[2] * r2; @@ -135,7 +137,7 @@ py::tuple hahog(foundation::pyarray_f image, float peak_threshold, py::gil_scoped_release release; // create a detector object - VlCovDet *covdet = vl_covdet_new(VL_COVDET_METHOD_HESSIAN); + VlCovDet* covdet = vl_covdet_new(VL_COVDET_METHOD_HESSIAN); // set various parameters (optional) vl_covdet_set_first_octave(covdet, 0); vl_covdet_set_peak_threshold(covdet, peak_threshold); @@ -155,7 +157,7 @@ py::tuple hahog(foundation::pyarray_f image, float peak_threshold, numFeatures = vecFeatures.size(); // get feature descriptors - VlSiftFilt *sift = vl_sift_new(16, 16, 1, 3, 0); + VlSiftFilt* sift = vl_sift_new(16, 16, 1, 3, 0); vl_index i; vl_index patchResolution = 15; double patchRelativeExtent = 7.5; @@ -169,7 +171,7 @@ py::tuple hahog(foundation::pyarray_f image, float peak_threshold, vl_sift_set_magnif(sift, 3.0); for (i = 0; i < (signed)numFeatures; ++i) { - const VlFrameOrientedEllipse &frame = vecFeatures.at(i).frame; + const VlFrameOrientedEllipse& frame = vecFeatures.at(i).frame; float det = frame.a11 * frame.a22 - frame.a12 * frame.a21; float size = sqrt(fabs(det)); float angle = atan2(frame.a21, frame.a11) * 180.0f / M_PI; @@ -178,15 +180,15 @@ py::tuple hahog(foundation::pyarray_f image, float peak_threshold, points[4 * i + 2] = size; points[4 * i + 3] = angle; - vl_covdet_extract_patch_for_frame(covdet, &patch[0], patchResolution, + vl_covdet_extract_patch_for_frame(covdet, patch.data(), patchResolution, patchRelativeExtent, patchRelativeSmoothing, frame); - vl_imgradient_polar_f(&patchXY[0], &patchXY[1], 2, 2 * patchSide, - &patch[0], patchSide, patchSide, patchSide); + vl_imgradient_polar_f(patchXY.data(), &patchXY[1], 2, 2 * patchSide, + patch.data(), patchSide, patchSide, patchSide); vl_sift_calc_raw_descriptor( - sift, &patchXY[0], &desc[dimension * i], (int)patchSide, + sift, patchXY.data(), &desc[dimension * i], (int)patchSide, (int)patchSide, (double)(patchSide - 1) / 2, (double)(patchSide - 1) / 2, (double)patchRelativeExtent / (3.0 * (4 + 1) / 2) / patchStep, diff --git a/opensfm/src/features/src/matching.cc b/opensfm/src/features/src/matching.cc index d759b7647..4b17b6900 100644 --- a/opensfm/src/features/src/matching.cc +++ b/opensfm/src/features/src/matching.cc @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -14,7 +15,7 @@ namespace py = pybind11; namespace features { -float DistanceL1(const float *pa, const float *pb, int n) { +float DistanceL1(const float* pa, const float* pb, int n) { float distance = 0; for (int i = 0; i < n; ++i) { distance += fabs(pa[i] - pb[i]); @@ -22,7 +23,7 @@ float DistanceL1(const float *pa, const float *pb, int n) { return distance; } -float DistanceL2(const float *pa, const float *pb, int n) { +float DistanceL2(const float* pa, const float* pb, int n) { float distance = 0; for (int i = 0; i < n; ++i) { distance += (pa[i] - pb[i]) * (pa[i] - pb[i]); @@ -30,19 +31,21 @@ float DistanceL2(const float *pa, const float *pb, int n) { return sqrt(distance); } -void MatchUsingWords(const cv::Mat &f1, const cv::Mat &w1, const cv::Mat &f2, - const cv::Mat &w2, float lowes_ratio, int max_checks, - cv::Mat *matches) { +void MatchUsingWords(const cv::Mat& f1, const cv::Mat& w1, const cv::Mat& f2, + const cv::Mat& w2, float lowes_ratio, int max_checks, + cv::Mat* matches) { // Index features on the second image. std::multimap index2; - const int *pw2 = &w2.at(0, 0); + const int* pw2 = &w2.at(0, 0); for (unsigned int i = 0; i < w2.rows * w2.cols; ++i) { index2.insert(std::pair(pw2[i], i)); } std::vector best_match(f1.rows, -1), second_best_match(f1.rows, -1); - std::vector best_distance(f1.rows, 99999999), - second_best_distance(f1.rows, 99999999); + std::vector best_distance(f1.rows, + std::numeric_limits::infinity()); + std::vector second_best_distance( + f1.rows, std::numeric_limits::infinity()); *matches = cv::Mat(0, 2, CV_32S); cv::Mat tmp_match(1, 2, CV_32S); for (unsigned int i = 0; i < w1.rows; ++i) { @@ -52,8 +55,8 @@ void MatchUsingWords(const cv::Mat &f1, const cv::Mat &w1, const cv::Mat &f2, auto range = index2.equal_range(word); for (auto it = range.first; it != range.second; ++it) { int match = it->second; - const float *pa = f1.ptr(i); - const float *pb = f2.ptr(match); + const float* pa = f1.ptr(i); + const float* pb = f2.ptr(match); float distance = DistanceL2(pa, pb, f1.cols); if (distance < best_distance[i]) { second_best_distance[i] = best_distance[i]; @@ -66,7 +69,9 @@ void MatchUsingWords(const cv::Mat &f1, const cv::Mat &w1, const cv::Mat &f2, } checks++; } - if (checks >= max_checks) break; + if (checks >= max_checks) { + break; + } } if (best_distance[i] < lowes_ratio * second_best_distance[i]) { tmp_match.at(0, 0) = i; @@ -93,8 +98,8 @@ py::array_t match_using_words(foundation::pyarray_f features1, return foundation::py_array_from_cvmat(matches); } -VecXf compute_vlad_descriptor(const MatXf &features, - const MatXf &vlad_centers) { +VecXf compute_vlad_descriptor(const MatXf& features, + const MatXf& vlad_centers) { const auto vlad_center_size = vlad_centers.cols(); const auto vlad_center_count = vlad_centers.rows(); @@ -106,7 +111,7 @@ VecXf compute_vlad_descriptor(const MatXf &features, vlad_descriptor.setZero(); for (int i = 0; i < features.rows(); ++i) { - const auto &feature = features.row(i); + const auto& feature = features.row(i); float best_distance = std::numeric_limits::max(); int best_center = -1; @@ -125,16 +130,16 @@ VecXf compute_vlad_descriptor(const MatXf &features, } std::pair, std::vector> compute_vlad_distances( - const std::map &vlad_descriptors, - const std::string &image, std::set &other_images) { + const std::map& vlad_descriptors, + const std::string& image, std::set& other_images) { if (vlad_descriptors.find(image) == vlad_descriptors.end()) { return std::make_pair(std::vector(), std::vector()); } std::vector distances; std::vector others; - const auto &reference = vlad_descriptors.at(image); - for (const auto &candidate : other_images) { + const auto& reference = vlad_descriptors.at(image); + for (const auto& candidate : other_images) { if (candidate == image) { continue; } diff --git a/opensfm/src/foundation/CMakeLists.txt b/opensfm/src/foundation/CMakeLists.txt index a1fc39583..ee4056ac7 100644 --- a/opensfm/src/foundation/CMakeLists.txt +++ b/opensfm/src/foundation/CMakeLists.txt @@ -1,10 +1,13 @@ set(FOUNDATION_FILES + memory.h stl_extensions.h + threading.h types.h newton_raphson.h numeric.h optional.h union_find.h + src/memory.cc src/types.cc src/newton_raphson.cc src/numeric.cc @@ -15,6 +18,9 @@ target_link_libraries(foundation ${OpenCV_LIBS} ${OpenMP_libomp_LIBRARY} Eigen3::Eigen + PRIVATE + ${GFLAGS_LIBRARY} + glog::glog ) target_include_directories(foundation PUBLIC diff --git a/opensfm/src/foundation/memory.h b/opensfm/src/foundation/memory.h new file mode 100644 index 000000000..7e355429d --- /dev/null +++ b/opensfm/src/foundation/memory.h @@ -0,0 +1,15 @@ +#pragma once + +namespace foundation { + +// RAII class to limit the number of malloc arenas on Linux/glibc +// This helps reduce memory fragmentation when using many OpenMP threads. +// Does nothing on non-Linux platforms as their allocation are supposedly +// smarter than glibc's malloc. +class ScopedMallocArena { + public: + ScopedMallocArena(); + ~ScopedMallocArena(); +}; + +} // namespace foundation diff --git a/opensfm/src/foundation/newton_raphson.h b/opensfm/src/foundation/newton_raphson.h index d418905f3..63b44e976 100644 --- a/opensfm/src/foundation/newton_raphson.h +++ b/opensfm/src/foundation/newton_raphson.h @@ -44,7 +44,6 @@ template struct FiniteDiff { static typename TypeTraits<1, 1>::Jacobian Derivative( const F& func, typename TypeTraits<1, 1>::Values& x) { - typename TypeTraits<1, 1>::Jacobian jacobian; constexpr auto eps = 1e-15; return (func(x + eps) - func(x)) / eps; } @@ -65,7 +64,7 @@ typename TypeTraits::Values SolveDecr( const typename TypeTraits::Jacobian& d, const typename TypeTraits::Values& f) { return (d.transpose() * d).inverse() * d.transpose() * f; -}; +} template <> typename TypeTraits<1, 1>::Values SolveDecr<1, 1>( @@ -77,7 +76,6 @@ template > typename TypeTraits::Values NewtonRaphson( const F& func, const typename TypeTraits::Values& initial_value, int iterations, double tol = 1e-6) { - constexpr auto eps = std::numeric_limits::epsilon(); auto current_value = initial_value; for (int i = 0; i < iterations; ++i) { const auto at_current_value = func(current_value); diff --git a/opensfm/src/foundation/numeric.h b/opensfm/src/foundation/numeric.h index 19cdb8bef..dd1550791 100644 --- a/opensfm/src/foundation/numeric.h +++ b/opensfm/src/foundation/numeric.h @@ -35,10 +35,11 @@ bool SolveAX0(const MAT& A, VEC* solution) { // Some nullspace will make a solution const bool some_nullspace = ratio > minimum_ratio; - if (some_nullspace) + if (some_nullspace) { return true; - else + } else { return false; + } } Eigen::Matrix3d SkewMatrix(const Eigen::Vector3d& v); diff --git a/opensfm/src/foundation/python_types.h b/opensfm/src/foundation/python_types.h index d9cd7e517..650b3f477 100644 --- a/opensfm/src/foundation/python_types.h +++ b/opensfm/src/foundation/python_types.h @@ -3,37 +3,38 @@ #include #include -#include +#include #include + #include "opencv2/core/core.hpp" namespace py = pybind11; namespace foundation { -typedef py::array_t pyarray_f; -typedef py::array_t - pyarray_d; -typedef py::array_t pyarray_int; -typedef py::array_t - pyarray_uint8; +using pyarray_f = py::array_t; +using pyarray_d = + py::array_t; +using pyarray_int = py::array_t; +using pyarray_uint8 = + py::array_t; template -py::array_t py_array_from_data(const T *data, size_t shape0) { +py::array_t py_array_from_data(const T* data, size_t shape0) { py::array_t res(shape0); std::copy(data, data + shape0, res.mutable_data()); return res; } template -py::array_t py_array_from_data(const T *data, size_t shape0, size_t shape1) { +py::array_t py_array_from_data(const T* data, size_t shape0, size_t shape1) { py::array_t res({shape0, shape1}); std::copy(data, data + shape0 * shape1, res.mutable_data()); return res; } template -py::array_t py_array_from_data(const T *data, size_t shape0, size_t shape1, +py::array_t py_array_from_data(const T* data, size_t shape0, size_t shape1, size_t shape2) { py::array_t res({shape0, shape1, shape2}); std::copy(data, data + shape0 * shape1 * shape2, res.mutable_data()); @@ -41,19 +42,19 @@ py::array_t py_array_from_data(const T *data, size_t shape0, size_t shape1, } template -py::array_t py_array_from_vector(const std::vector &v) { - const T *data = v.size() ? &v[0] : NULL; +py::array_t py_array_from_vector(const std::vector& v) { + const T* data = v.size() ? v.data() : NULL; return py_array_from_data(data, v.size()); } template -py::array_t py_array_from_cvmat(const cv::Mat &m) { - const T *data = m.rows ? m.ptr(0) : NULL; +py::array_t py_array_from_cvmat(const cv::Mat& m) { + const T* data = m.rows ? m.ptr(0) : NULL; return py_array_from_data(data, m.rows, m.cols); } template -cv::Mat pyarray_cv_mat_view_typed(T &array, int type) { +cv::Mat pyarray_cv_mat_view_typed(T& array, int type) { int height = 1; int width = 1; @@ -67,8 +68,8 @@ cv::Mat pyarray_cv_mat_view_typed(T &array, int type) { return cv::Mat(height, width, type, array.mutable_data()); } -cv::Mat pyarray_cv_mat_view(pyarray_f &array); -cv::Mat pyarray_cv_mat_view(pyarray_d &array); -cv::Mat pyarray_cv_mat_view(pyarray_int &array); -cv::Mat pyarray_cv_mat_view(pyarray_uint8 &array); +cv::Mat pyarray_cv_mat_view(pyarray_f& array); +cv::Mat pyarray_cv_mat_view(pyarray_d& array); +cv::Mat pyarray_cv_mat_view(pyarray_int& array); +cv::Mat pyarray_cv_mat_view(pyarray_uint8& array); } // namespace foundation diff --git a/opensfm/src/foundation/src/memory.cc b/opensfm/src/foundation/src/memory.cc new file mode 100644 index 000000000..bf989efe4 --- /dev/null +++ b/opensfm/src/foundation/src/memory.cc @@ -0,0 +1,23 @@ +#include + +#ifdef __linux__ +#include +#endif + +namespace foundation { + +ScopedMallocArena::ScopedMallocArena() { +#ifdef __linux__ + // Limit to 2 arenas to prevent excessive fragmentation with many threads + mallopt(M_ARENA_MAX, 2); +#endif +} + +ScopedMallocArena::~ScopedMallocArena() { +#ifdef __linux__ + // 0 means default behavior (based M_ARENA_TEST) + mallopt(M_ARENA_MAX, 0); +#endif +} + +} // namespace foundation diff --git a/opensfm/src/foundation/src/types.cc b/opensfm/src/foundation/src/types.cc index fe4581566..dd7755a81 100644 --- a/opensfm/src/foundation/src/types.cc +++ b/opensfm/src/foundation/src/types.cc @@ -2,19 +2,19 @@ namespace foundation { -cv::Mat pyarray_cv_mat_view(pyarray_f &array) { +cv::Mat pyarray_cv_mat_view(pyarray_f& array) { return pyarray_cv_mat_view_typed(array, CV_32F); } -cv::Mat pyarray_cv_mat_view(pyarray_d &array) { +cv::Mat pyarray_cv_mat_view(pyarray_d& array) { return pyarray_cv_mat_view_typed(array, CV_64F); } -cv::Mat pyarray_cv_mat_view(pyarray_int &array) { +cv::Mat pyarray_cv_mat_view(pyarray_int& array) { return pyarray_cv_mat_view_typed(array, CV_32S); } -cv::Mat pyarray_cv_mat_view(pyarray_uint8 &array) { +cv::Mat pyarray_cv_mat_view(pyarray_uint8& array) { return pyarray_cv_mat_view_typed(array, CV_8U); } diff --git a/opensfm/src/foundation/test/union_find_test.cc b/opensfm/src/foundation/test/union_find_test.cc index 0b6af1e0d..481d37aff 100644 --- a/opensfm/src/foundation/test/union_find_test.cc +++ b/opensfm/src/foundation/test/union_find_test.cc @@ -1,14 +1,13 @@ +#include #include #include -#include - class UnionFindFixture : public ::testing::Test { public: UnionFindFixture() { for (int i = 0; i < count; ++i) { - elements.emplace_back(std::unique_ptr>( - new UnionFindElement(i))); + elements.emplace_back( + std::unique_ptr>(new UnionFindElement(i))); } } diff --git a/opensfm/src/foundation/threading.h b/opensfm/src/foundation/threading.h new file mode 100644 index 000000000..0cdbdbba3 --- /dev/null +++ b/opensfm/src/foundation/threading.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include + +namespace foundation { + +template +class ConcurrentQueue { + public: + void Push(T result) { + std::lock_guard lock(mutex_); + queue_.push(std::move(result)); + cv_.notify_one(); + } + + bool Pop(T& result) { + std::unique_lock lock(mutex_); + cv_.wait(lock, [this] { return !queue_.empty() || finished_; }); + if (queue_.empty()) { + return false; + } + result = std::move(queue_.front()); + queue_.pop(); + return true; + } + + void Finish() { + std::lock_guard lock(mutex_); + finished_ = true; + cv_.notify_all(); + } + + size_t Size() const { + std::lock_guard lock(mutex_); + return queue_.size(); + } + + private: + std::queue queue_; + mutable std::mutex mutex_; + std::condition_variable cv_; + bool finished_ = false; +}; + +} // namespace foundation diff --git a/opensfm/src/foundation/union_find.h b/opensfm/src/foundation/union_find.h index a4fb69410..929b7c532 100644 --- a/opensfm/src/foundation/union_find.h +++ b/opensfm/src/foundation/union_find.h @@ -50,7 +50,7 @@ std::vector*>> GetUnionFindClusters( std::vector*>> clusters; clusters.reserve(aggregations.size()); - for (const auto agg : aggregations) { + for (const auto& agg : aggregations) { clusters.emplace_back(agg.second); } diff --git a/opensfm/src/geo/CMakeLists.txt b/opensfm/src/geo/CMakeLists.txt index 75620d069..ad62e5853 100644 --- a/opensfm/src/geo/CMakeLists.txt +++ b/opensfm/src/geo/CMakeLists.txt @@ -33,3 +33,4 @@ target_link_libraries(pygeo set_target_properties(pygeo PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${opensfm_SOURCE_DIR}/.." ) +install(TARGETS pygeo LIBRARY DESTINATION .) diff --git a/opensfm/src/geo/geo.h b/opensfm/src/geo/geo.h index 053249d72..cf3c7e63e 100644 --- a/opensfm/src/geo/geo.h +++ b/opensfm/src/geo/geo.h @@ -47,4 +47,4 @@ struct TopocentricConverter { Vec3d ToLla(const Vec3d& xyz) const; Vec3d GetLlaRef() const; }; -}; // namespace geo +} // namespace geo diff --git a/opensfm/src/geo/pygeo.pyi b/opensfm/src/geo/pygeo.pyi index 0e5f2774f..809b80dda 100644 --- a/opensfm/src/geo/pygeo.pyi +++ b/opensfm/src/geo/pygeo.pyi @@ -2,8 +2,13 @@ # Do not manually edit # To regenerate: # $ buck run //mapillary/opensfm/opensfm/src/geo:pygeo_stubgen -# Use proper mode, e.g. @arvr/mode/linux/dev for arvr # @generated +# +# Tip: Be sure to run this with the build mode you use for your project, e.g., +# @//arvr/mode/linux/opt (or dev) in arvr. +# +# Ignore errors for [5] global variable types and [24] untyped generics. +# pyre-ignore-all-errors[5,24] import numpy from typing import * @@ -37,19 +42,19 @@ def ecef_from_topocentric_transform(arg0: float, arg1: float, arg2: float) -> nu @overload def ecef_from_topocentric_transform(arg0: numpy.ndarray) -> numpy.ndarray:... @overload -def ecef_from_topocentric_transform_finite_diff(arg0: numpy.ndarray) -> numpy.ndarray:... -@overload def ecef_from_topocentric_transform_finite_diff(arg0: float, arg1: float, arg2: float) -> numpy.ndarray:... +@overload +def ecef_from_topocentric_transform_finite_diff(arg0: numpy.ndarray) -> numpy.ndarray:... def gps_distance(arg0: numpy.ndarray, arg1: numpy.ndarray) -> float:... @overload def lla_from_ecef(arg0: numpy.ndarray) -> numpy.ndarray:... @overload def lla_from_ecef(arg0: float, arg1: float, arg2: float) -> numpy.ndarray:... @overload -def lla_from_topocentric(arg0: numpy.ndarray, arg1: numpy.ndarray) -> numpy.ndarray:... -@overload def lla_from_topocentric(arg0: float, arg1: float, arg2: float, arg3: float, arg4: float, arg5: float) -> numpy.ndarray:... @overload -def topocentric_from_lla(arg0: numpy.ndarray, arg1: numpy.ndarray) -> numpy.ndarray:... +def lla_from_topocentric(arg0: numpy.ndarray, arg1: numpy.ndarray) -> numpy.ndarray:... @overload def topocentric_from_lla(arg0: float, arg1: float, arg2: float, arg3: float, arg4: float, arg5: float) -> numpy.ndarray:... +@overload +def topocentric_from_lla(arg0: numpy.ndarray, arg1: numpy.ndarray) -> numpy.ndarray:... diff --git a/opensfm/src/geo/python/pybind.cc b/opensfm/src/geo/python/pybind.cc index f54c5f008..9610ba8f6 100644 --- a/opensfm/src/geo/python/pybind.cc +++ b/opensfm/src/geo/python/pybind.cc @@ -1,38 +1,39 @@ +#include #include #include #include namespace py = pybind11; PYBIND11_MODULE(pygeo, m) { - m.def("ecef_from_lla", (Vec3d(*)(const Vec3d&))geo::EcefFromLla); + m.def("ecef_from_lla", (Vec3d (*)(const Vec3d&))geo::EcefFromLla); m.def("ecef_from_lla", - (Vec3d(*)(const double, const double, const double))geo::EcefFromLla); - m.def("lla_from_ecef", (Vec3d(*)(const Vec3d&))geo::LlaFromEcef); + (Vec3d (*)(const double, const double, const double))geo::EcefFromLla); + m.def("lla_from_ecef", (Vec3d (*)(const Vec3d&))geo::LlaFromEcef); m.def("lla_from_ecef", - (Vec3d(*)(const double, const double, const double))geo::LlaFromEcef); + (Vec3d (*)(const double, const double, const double))geo::LlaFromEcef); m.def("ecef_from_topocentric_transform", - (Mat4d(*)(const Vec3d&))geo::EcefFromTopocentricTransform); + (Mat4d (*)(const Vec3d&))geo::EcefFromTopocentricTransform); m.def("ecef_from_topocentric_transform", - (Mat4d(*)(const double, const double, - const double))geo::EcefFromTopocentricTransform); + (Mat4d (*)(const double, const double, + const double))geo::EcefFromTopocentricTransform); m.def("ecef_from_topocentric_transform_finite_diff", - (Mat4d(*)(const Vec3d&))geo::EcefFromTopocentricTransformFiniteDiff); + (Mat4d (*)(const Vec3d&))geo::EcefFromTopocentricTransformFiniteDiff); m.def("ecef_from_topocentric_transform_finite_diff", - (Mat4d(*)(const double, const double, - const double))geo::EcefFromTopocentricTransformFiniteDiff); + (Mat4d (*)(const double, const double, + const double))geo::EcefFromTopocentricTransformFiniteDiff); m.def("topocentric_from_lla", - (Vec3d(*)(const Vec3d&, const Vec3d&))geo::TopocentricFromLla); + (Vec3d (*)(const Vec3d&, const Vec3d&))geo::TopocentricFromLla); m.def("topocentric_from_lla", - (Vec3d(*)(const double, const double, const double, const double, - const double, const double))geo::TopocentricFromLla); + (Vec3d (*)(const double, const double, const double, const double, + const double, const double))geo::TopocentricFromLla); m.def("lla_from_topocentric", - (Vec3d(*)(const Vec3d&, const Vec3d&))geo::LlaFromTopocentric); + (Vec3d (*)(const Vec3d&, const Vec3d&))geo::LlaFromTopocentric); m.def("lla_from_topocentric", - (Vec3d(*)(const double, const double, const double, const double, - const double, const double))geo::LlaFromTopocentric); + (Vec3d (*)(const double, const double, const double, const double, + const double, const double))geo::LlaFromTopocentric); m.def("gps_distance", geo::GpsDistance); py::class_(m, "TopocentricConverter") diff --git a/opensfm/src/geo/src/geo.cc b/opensfm/src/geo/src/geo.cc index 2f07b4ad2..5ed9487fe 100644 --- a/opensfm/src/geo/src/geo.cc +++ b/opensfm/src/geo/src/geo.cc @@ -165,7 +165,7 @@ TopocentricConverter::TopocentricConverter(const double lat, : lat_(lat), long_(longitude), alt_(alt) {} TopocentricConverter::TopocentricConverter(const Vec3d& lla) - : TopocentricConverter(lla[0], lla[1], lla[2]){}; + : TopocentricConverter(lla[0], lla[1], lla[2]) {} Vec3d TopocentricConverter::ToTopocentric(const double lat, const double lon, const double alt) const { diff --git a/opensfm/src/geometry/CMakeLists.txt b/opensfm/src/geometry/CMakeLists.txt index 702c209f8..7e182f87b 100644 --- a/opensfm/src/geometry/CMakeLists.txt +++ b/opensfm/src/geometry/CMakeLists.txt @@ -32,6 +32,7 @@ if (OPENSFM_BUILD_TESTS) test/camera_functions_test.cc test/covariance_test.cc test/point_test.cc + test/triangulation_test.cc ) add_executable(geometry_test ${GEOMETRY_TEST_FILES}) target_include_directories(geometry_test PRIVATE ${CMAKE_SOURCE_DIR}) @@ -52,3 +53,4 @@ target_link_libraries(pygeometry set_target_properties(pygeometry PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${opensfm_SOURCE_DIR}/.." ) +install(TARGETS pygeometry LIBRARY DESTINATION .) diff --git a/opensfm/src/geometry/absolute_pose.h b/opensfm/src/geometry/absolute_pose.h index faa7a2633..72a0c9e82 100644 --- a/opensfm/src/geometry/absolute_pose.h +++ b/opensfm/src/geometry/absolute_pose.h @@ -9,13 +9,13 @@ Eigen::Matrix3d RotationMatrixAroundAxis(const double cos_theta, const double sin_theta, - const Eigen::Vector3d &v); + const Eigen::Vector3d& v); // Implements "An Efficient Algebraic Solution to the // Perspective-Three-Point Problem" from Ke and al. template std::vector> AbsolutePoseThreePoints(IT begin, - IT end) { + IT /* end */) { std::vector> RTs; const Eigen::Vector3d b1 = begin->first; @@ -28,7 +28,6 @@ std::vector> AbsolutePoseThreePoints(IT begin, // Compute k1, k2 and k3 const Eigen::Vector3d k1 = (p1 - p2).normalized(); const Eigen::Vector3d k3 = (b1.cross(b2)).normalized(); - const Eigen::Vector3d k2 = (k1.cross(k3)).normalized(); // Compute ui and vi for i = 1, 2 const Eigen::Vector3d u1 = p1 - p3; @@ -93,8 +92,7 @@ std::vector> AbsolutePoseThreePoints(IT begin, e1 << 1, 0, 0; e2 << 0, 1, 0; - constexpr double eps = 1e-20; - for (const auto &root : roots) { + for (const auto& root : roots) { const auto cos_theta_1 = root; const auto sin_theta_1 = foundation::Sign(k3_b3) * std::sqrt(1.0 - SQUARE(cos_theta_1)); @@ -125,7 +123,7 @@ std::vector> AbsolutePoseThreePoints(IT begin, template Eigen::Vector3d TranslationBetweenPoints(IT begin, IT end, - const Eigen::Matrix3d &rotation) { + const Eigen::Matrix3d& rotation) { Eigen::Matrix3d F1 = Eigen::Matrix3d::Zero(); Eigen::Vector3d F2 = Eigen::Vector3d::Zero(); const Eigen::Matrix3d identity = Eigen::Matrix3d::Identity(); @@ -195,14 +193,14 @@ Eigen::Vector3d AbsolutePoseNPointsKnownRotation(IT begin, IT end) { namespace geometry { std::vector> AbsolutePoseThreePoints( - const Eigen::Matrix &bearings, - const Eigen::Matrix &points); + const Eigen::Matrix& bearings, + const Eigen::Matrix& points); Eigen::Matrix AbsolutePoseNPoints( - const Eigen::Matrix &bearings, - const Eigen::Matrix &points); + const Eigen::Matrix& bearings, + const Eigen::Matrix& points); Eigen::Vector3d AbsolutePoseNPointsKnownRotation( - const Eigen::Matrix &bearings, - const Eigen::Matrix &points); + const Eigen::Matrix& bearings, + const Eigen::Matrix& points); } // namespace geometry diff --git a/opensfm/src/geometry/camera.h b/opensfm/src/geometry/camera.h index 348219599..bce1affa0 100644 --- a/opensfm/src/geometry/camera.h +++ b/opensfm/src/geometry/camera.h @@ -100,14 +100,17 @@ class Camera { static Vec2d PixelToNormalizedCoordinates(const Vec2d& px_coord, const int width, const int height); static MatX2d PixelToNormalizedCoordinatesMany(const MatX2d& px_coord, - const int width, const int height); + const int width, + const int height); Vec2d NormalizedToPixelCoordinates(const Vec2d& norm_coord) const; MatX2d NormalizedToPixelCoordinatesMany(const MatX2d& norm_coords) const; static Vec2d NormalizedToPixelCoordinates(const Vec2d& norm_coord, const int width, const int height); - static MatX2d NormalizedToPixelCoordinatesMany(const MatX2d& norm_coords, const int width, const int height); + static MatX2d NormalizedToPixelCoordinatesMany(const MatX2d& norm_coords, + const int width, + const int height); private: ProjectionType type_{ProjectionType::NONE}; @@ -118,4 +121,4 @@ class Camera { std::pair ComputeCameraMapping(const Camera& from, const Camera& to, int width, int height); -}; // namespace geometry +} // namespace geometry diff --git a/opensfm/src/geometry/camera_distortions_functions.h b/opensfm/src/geometry/camera_distortions_functions.h index f0b86df1d..7abce119d 100644 --- a/opensfm/src/geometry/camera_distortions_functions.h +++ b/opensfm/src/geometry/camera_distortions_functions.h @@ -490,33 +490,45 @@ struct Disto62 : Functor<2, 8, 2> { /* Parameters are : k1, k2, k3, k4, k5, k6, p1, p2, s0, s1, s2, s3 */ struct Disto624 : Functor<2, 12, 2> { - enum { K1 = 0, K2 = 1, K3 = 2, K4 = 3, K5 = 4, K6 = 5, P1 = 6, P2 = 7, S0 = 8, S1 = 9, S2 = 10, S3 = 11 }; + enum { + K1 = 0, + K2 = 1, + K3 = 2, + K4 = 3, + K5 = 4, + K6 = 5, + P1 = 6, + P2 = 7, + S0 = 8, + S1 = 9, + S2 = 10, + S3 = 11 + }; template static void Forward(const T* point, const T* k, T* distorted) { const auto r2 = SquaredNorm(point); // Radial - const auto distortion_radial = - Disto62::RadialDistortion(r2, k[static_cast(K1)], k[static_cast(K2)], - k[static_cast(K3)], k[static_cast(K4)], - k[static_cast(K5)], k[static_cast(K6)]); + const auto distortion_radial = Disto62::RadialDistortion( + r2, k[static_cast(K1)], k[static_cast(K2)], + k[static_cast(K3)], k[static_cast(K4)], + k[static_cast(K5)], k[static_cast(K6)]); // Tangential - const auto distortion_tangential = - Disto62::TangentialDistortion(r2, point[0], point[1], k[static_cast(P1)], - k[static_cast(P2)]); + const auto distortion_tangential = Disto62::TangentialDistortion( + r2, point[0], point[1], k[static_cast(P1)], + k[static_cast(P2)]); // Thin prism - const auto distortion_thin_prism = - ThinPrismDistortion(r2, - k[static_cast(S0)], - k[static_cast(S1)], - k[static_cast(S2)], - k[static_cast(S3)]); - - distorted[0] = point[0] * distortion_radial + distortion_tangential[0] + distortion_thin_prism[0]; - distorted[1] = point[1] * distortion_radial + distortion_tangential[1] + distortion_thin_prism[1]; + const auto distortion_thin_prism = ThinPrismDistortion( + r2, k[static_cast(S0)], k[static_cast(S1)], + k[static_cast(S2)], k[static_cast(S3)]); + + distorted[0] = point[0] * distortion_radial + distortion_tangential[0] + + distortion_thin_prism[0]; + distorted[1] = point[1] * distortion_radial + distortion_tangential[1] + + distortion_thin_prism[1]; } template @@ -665,8 +677,8 @@ struct Disto624 : Functor<2, 12, 2> { const auto distortion_thin_prism = ThinPrismDistortion(r2, s0, s1, s2, s3); - return point * distortion_radial + distortion_tangential + distortion_thin_prism - - point_distorted; + return point * distortion_radial + distortion_tangential + + distortion_thin_prism - point_distorted; } Mat2 derivative(const Vec2& point) const { @@ -680,11 +692,9 @@ struct Disto624 : Functor<2, 12, 2> { }; template - static Vec2 ThinPrismDistortion(const T& r2, - const T& s0, const T& s1, + static Vec2 ThinPrismDistortion(const T& r2, const T& s0, const T& s1, const T& s2, const T& s3) { - return Vec2(s0 * r2 + s1 * r2 * r2, - s2 * r2 + s3 * r2 * r2); + return Vec2(s0 * r2 + s1 * r2 * r2, s2 * r2 + s3 * r2 * r2); } }; diff --git a/opensfm/src/geometry/camera_instances.h b/opensfm/src/geometry/camera_instances.h index e5acf475e..ba89e6407 100644 --- a/opensfm/src/geometry/camera_instances.h +++ b/opensfm/src/geometry/camera_instances.h @@ -232,5 +232,5 @@ void Dispatch(const ProjectionType& type, IN&&... args) { default: throw std::runtime_error("Invalid ProjectionType"); } -}; +} } // namespace geometry diff --git a/opensfm/src/geometry/covariance.h b/opensfm/src/geometry/covariance.h index 8c6a0b52f..4effe1c23 100644 --- a/opensfm/src/geometry/covariance.h +++ b/opensfm/src/geometry/covariance.h @@ -3,8 +3,7 @@ #include #include -namespace geometry { -namespace covariance { +namespace geometry::covariance { using PointJacobian = Eigen::Matrix; std::pair ComputeJacobianReprojectionError( const Camera& camera, const Pose& pose, const Vec2d& observation, @@ -12,5 +11,4 @@ std::pair ComputeJacobianReprojectionError( std::pair ComputePointInverseCovariance( const std::vector& cameras, const std::vector& poses, const std::vector& observations, const Vec3d& point); -} // namespace covariance -} // namespace geometry +} // namespace geometry::covariance diff --git a/opensfm/src/geometry/essential.h b/opensfm/src/geometry/essential.h index a45aeb565..d8bcea6a1 100644 --- a/opensfm/src/geometry/essential.h +++ b/opensfm/src/geometry/essential.h @@ -21,6 +21,7 @@ #pragma once #include + #include #include @@ -61,7 +62,7 @@ enum { }; template -inline void EncodeEpipolarEquation(IT begin, IT end, MAT *A) { +inline void EncodeEpipolarEquation(IT begin, IT end, MAT* A) { for (IT it = begin; it != end; ++it) { int i = (it - begin); const auto x1 = it->first; @@ -82,18 +83,18 @@ Eigen::MatrixXd FivePointsNullspaceBasis(IT begin, IT end) { } // Multiply two polynomials of degree 1. -Eigen::Matrix o1(const Eigen::Matrix &a, - const Eigen::Matrix &b); +Eigen::Matrix o1(const Eigen::Matrix& a, + const Eigen::Matrix& b); // Multiply a polynomial of degree 2, a, by a polynomial of degree 1, b. -Eigen::Matrix o2(const Eigen::Matrix &a, - const Eigen::Matrix &b); +Eigen::Matrix o2(const Eigen::Matrix& a, + const Eigen::Matrix& b); // Builds the polynomial constraint matrix M. -Eigen::MatrixXd FivePointsPolynomialConstraints(const Eigen::MatrixXd &E_basis); +Eigen::MatrixXd FivePointsPolynomialConstraints(const Eigen::MatrixXd& E_basis); // Gauss--Jordan elimination for the constraint matrix. -bool FivePointsGaussJordan(Eigen::MatrixXd *Mp); +bool FivePointsGaussJordan(Eigen::MatrixXd* Mp); template std::vector> EssentialFivePoints(IT begin, IT end) { @@ -126,7 +127,7 @@ std::vector> EssentialFivePoints(IT begin, IT end) { // Compute the solutions from action matrix's eigenvectors. Eigen::EigenSolver es(At); - typedef Eigen::EigenSolver::EigenvectorsType Matc; + using Matc = Eigen::EigenSolver::EigenvectorsType; Matc V = es.eigenvectors(); Matc solutions(4, 10); solutions.row(0) = V.row(6).array() / V.row(9).array(); @@ -192,10 +193,10 @@ std::vector> EssentialNPoints(IT begin, IT end) { namespace geometry { std::vector> EssentialFivePoints( - const Eigen::Matrix &x1, - const Eigen::Matrix &x2); + const Eigen::Matrix& x1, + const Eigen::Matrix& x2); std::vector> EssentialNPoints( - const Eigen::Matrix &x1, - const Eigen::Matrix &x2); + const Eigen::Matrix& x1, + const Eigen::Matrix& x2); } // namespace geometry diff --git a/opensfm/src/geometry/functions.h b/opensfm/src/geometry/functions.h index 4beb40784..58c8e8db1 100644 --- a/opensfm/src/geometry/functions.h +++ b/opensfm/src/geometry/functions.h @@ -109,4 +109,4 @@ static void ComposeFunctions(const T* in, const T* parameters, T* out) { constexpr int Index = ComposeIndex(); FUNC1::template Apply(&tmp[0], parameters + Index, out); } -}; // namespace geometry +} // namespace geometry diff --git a/opensfm/src/geometry/pose.h b/opensfm/src/geometry/pose.h index c015e7b7f..69f95488f 100644 --- a/opensfm/src/geometry/pose.h +++ b/opensfm/src/geometry/pose.h @@ -17,7 +17,6 @@ class Pose { virtual ~Pose() = default; Pose(const Vec3d& R, const Vec3d& t = Vec3d::Zero()) { - Mat4d T_cw = Mat4d::Identity(); SetFromWorldToCamera(R, t); } Pose(const Mat3d& R, const Vec3d& t = Vec3d::Zero()) { @@ -50,7 +49,7 @@ class Pose { Vec3d TranslationCameraToWorld() const { return cam_to_world_.block<3, 1>(0, 3); - }; + } Vec3d GetOrigin() const { return TranslationCameraToWorld(); } virtual void SetOrigin(const Vec3d& origin) { diff --git a/opensfm/src/geometry/pygeometry.pyi b/opensfm/src/geometry/pygeometry.pyi index 8b34429fa..2c125ae9e 100644 --- a/opensfm/src/geometry/pygeometry.pyi +++ b/opensfm/src/geometry/pygeometry.pyi @@ -7,170 +7,217 @@ # Tip: Be sure to run this with the build mode you use for your project, e.g., # @//arvr/mode/linux/opt (or dev) in arvr. # -# Ignore errors for [5] global variable types and [24] untyped generics. -# pyre-ignore-all-errors[5,24] +# Ignore errors for [24] untyped generics. +# pyre-ignore-all-errors[24] import numpy from typing import * -__all__ = [ -"Camera", -"CameraParameters", -"Pose", -"ProjectionType", -"Similarity", -"absolute_pose_n_points", -"absolute_pose_n_points_known_rotation", -"absolute_pose_three_points", -"compute_camera_mapping", -"epipolar_angle_two_bearings_many", -"essential_five_points", -"essential_n_points", -"point_refinement", -"relative_pose_from_essential", -"relative_pose_refinement", -"relative_rotation_n_points", -"triangulate_bearings_dlt", -"triangulate_bearings_midpoint", -"triangulate_two_bearings_midpoint", -"triangulate_two_bearings_midpoint_many", -"BROWN", -"DUAL", -"FISHEYE", -"FISHEYE62", -"FISHEYE624", -"FISHEYE_OPENCV", -"PERSPECTIVE", -"RADIAL", -"SIMPLE_RADIAL", -"SPHERICAL", -"aspect_ratio", -"cx", -"cy", -"focal", -"k1", -"k2", -"k3", -"k4", -"k5", -"k6", -"none", -"p1", -"p2", -"s0", -"s1", -"s2", -"s3", -"transition" + +__all__ = [ + "Camera", + "CameraParameters", + "Pose", + "ProjectionType", + "Similarity", + "absolute_pose_n_points", + "absolute_pose_n_points_known_rotation", + "absolute_pose_three_points", + "compute_camera_mapping", + "epipolar_angle_two_bearings_many", + "essential_five_points", + "essential_n_points", + "point_refinement", + "relative_pose_from_essential", + "relative_pose_refinement", + "relative_rotation_n_points", + "triangulate_bearings_dlt", + "triangulate_bearings_midpoint", + "triangulate_two_bearings_midpoint", + "triangulate_two_bearings_midpoint_many", + "BROWN", + "DUAL", + "FISHEYE", + "FISHEYE62", + "FISHEYE624", + "FISHEYE_OPENCV", + "PERSPECTIVE", + "RADIAL", + "SIMPLE_RADIAL", + "SPHERICAL", + "aspect_ratio", + "cx", + "cy", + "focal", + "k1", + "k2", + "k3", + "k4", + "k5", + "k6", + "none", + "p1", + "p2", + "s0", + "s1", + "s2", + "s3", + "transition", ] + class Camera: def __copy__(self) -> Camera: ... def __deepcopy__(self, arg0: dict) -> Camera: ... def __getstate__(self) -> tuple: ... def __setstate__(self, arg0: tuple) -> None: ... @staticmethod - def create_brown(arg0: float, arg1: float, arg2: numpy.ndarray, arg3: numpy.ndarray) -> Camera: ... + def create_brown( + arg0: float, arg1: float, arg2: numpy.typing.NDArray, arg3: numpy.typing.NDArray + ) -> Camera: ... @staticmethod def create_dual(arg0: float, arg1: float, arg2: float, arg3: float) -> Camera: ... @staticmethod def create_fisheye(arg0: float, arg1: float, arg2: float) -> Camera: ... @staticmethod - def create_fisheye62(arg0: float, arg1: float, arg2: numpy.ndarray, arg3: numpy.ndarray) -> Camera: ... + def create_fisheye62( + arg0: float, arg1: float, arg2: numpy.typing.NDArray, arg3: numpy.typing.NDArray + ) -> Camera: ... @staticmethod - def create_fisheye624(arg0: float, arg1: float, arg2: numpy.ndarray, arg3: numpy.ndarray) -> Camera: ... + def create_fisheye624( + arg0: float, arg1: float, arg2: numpy.typing.NDArray, arg3: numpy.typing.NDArray + ) -> Camera: ... @staticmethod - def create_fisheye_opencv(arg0: float, arg1: float, arg2: numpy.ndarray, arg3: numpy.ndarray) -> Camera: ... + def create_fisheye_opencv( + arg0: float, arg1: float, arg2: numpy.typing.NDArray, arg3: numpy.typing.NDArray + ) -> Camera: ... @staticmethod def create_perspective(arg0: float, arg1: float, arg2: float) -> Camera: ... @staticmethod - def create_radial(arg0: float, arg1: float, arg2: numpy.ndarray, arg3: numpy.ndarray) -> Camera: ... + def create_radial( + arg0: float, arg1: float, arg2: numpy.typing.NDArray, arg3: numpy.typing.NDArray + ) -> Camera: ... @staticmethod - def create_simple_radial(arg0: float, arg1: float, arg2: numpy.ndarray, arg3: float) -> Camera: ... + def create_simple_radial( + arg0: float, arg1: float, arg2: numpy.typing.NDArray, arg3: float + ) -> Camera: ... @staticmethod def create_spherical() -> Camera: ... - def get_K(self) -> numpy.ndarray: ... - def get_K_in_pixel_coordinates(self, arg0: int, arg1: int) -> numpy.ndarray: ... - def get_parameters_map(self) -> Dict[CameraParameters, float]: ... - def get_parameters_types(self) -> List[CameraParameters]: ... - def get_parameters_values(self) -> numpy.ndarray: ... + def get_K(self) -> numpy.typing.NDArray: ... + def get_K_in_pixel_coordinates( + self, arg0: int, arg1: int + ) -> numpy.typing.NDArray: ... + def get_parameters_map(self) -> dict[CameraParameters, float]: ... + def get_parameters_types(self) -> list[CameraParameters]: ... + def get_parameters_values(self) -> numpy.typing.NDArray: ... @staticmethod def is_panorama(arg0: str) -> bool: ... - def normalized_to_pixel_coordinates(self, arg0: numpy.ndarray) -> numpy.ndarray: ... + def normalized_to_pixel_coordinates( + self, arg0: numpy.typing.NDArray + ) -> numpy.typing.NDArray: ... @staticmethod - def normalized_to_pixel_coordinates_common(arg0: numpy.ndarray, arg1: int, arg2: int) -> numpy.ndarray: ... - def normalized_to_pixel_coordinates_many(self, arg0: numpy.ndarray) -> numpy.ndarray: ... + def normalized_to_pixel_coordinates_common( + arg0: numpy.typing.NDArray, arg1: int, arg2: int + ) -> numpy.typing.NDArray: ... + def normalized_to_pixel_coordinates_many( + self, arg0: numpy.typing.NDArray + ) -> numpy.typing.NDArray: ... @staticmethod - def normalized_to_pixel_coordinates_many_common(arg0: numpy.ndarray, arg1: int, arg2: int) -> numpy.ndarray: ... - def pixel_bearing(self, arg0: numpy.ndarray) -> numpy.ndarray: ... - def pixel_bearing_many(self, arg0: numpy.ndarray) -> numpy.ndarray: ... - def pixel_to_normalized_coordinates(self, arg0: numpy.ndarray) -> numpy.ndarray: ... + def normalized_to_pixel_coordinates_many_common( + arg0: numpy.typing.NDArray, arg1: int, arg2: int + ) -> numpy.typing.NDArray: ... + def pixel_bearing(self, arg0: numpy.typing.NDArray) -> numpy.typing.NDArray: ... + def pixel_bearing_many( + self, arg0: numpy.typing.NDArray + ) -> numpy.typing.NDArray: ... + def pixel_to_normalized_coordinates( + self, arg0: numpy.typing.NDArray + ) -> numpy.typing.NDArray: ... @staticmethod - def pixel_to_normalized_coordinates_common(arg0: numpy.ndarray, arg1: int, arg2: int) -> numpy.ndarray: ... - def pixel_to_normalized_coordinates_many(self, arg0: numpy.ndarray) -> numpy.ndarray: ... + def pixel_to_normalized_coordinates_common( + arg0: numpy.typing.NDArray, arg1: int, arg2: int + ) -> numpy.typing.NDArray: ... + def pixel_to_normalized_coordinates_many( + self, arg0: numpy.typing.NDArray + ) -> numpy.typing.NDArray: ... @staticmethod - def pixel_to_normalized_coordinates_many_common(arg0: numpy.ndarray, arg1: int, arg2: int) -> numpy.ndarray: ... - def project(self, arg0: numpy.ndarray) -> numpy.ndarray: ... - def project_many(self, arg0: numpy.ndarray) -> numpy.ndarray: ... + def pixel_to_normalized_coordinates_many_common( + arg0: numpy.typing.NDArray, arg1: int, arg2: int + ) -> numpy.typing.NDArray: ... + def project(self, arg0: numpy.typing.NDArray) -> numpy.typing.NDArray: ... + def project_many(self, arg0: numpy.typing.NDArray) -> numpy.typing.NDArray: ... def set_parameter_value(self, arg0: CameraParameters, arg1: float) -> None: ... - def set_parameters_values(self, arg0: numpy.ndarray) -> None: ... + def set_parameters_values(self, arg0: numpy.typing.NDArray) -> None: ... @property - def aspect_ratio(self) -> float:... + def aspect_ratio(self) -> float: ... @aspect_ratio.setter - def aspect_ratio(self, arg1: float) -> None:... + def aspect_ratio(self, arg1: float) -> None: ... @property - def distortion(self) -> numpy.ndarray:... + def distortion(self) -> numpy.typing.NDArray: ... @distortion.setter - def distortion(self, arg1: numpy.ndarray) -> None:... + def distortion(self, arg1: numpy.typing.NDArray) -> None: ... @property - def focal(self) -> float:... + def focal(self) -> float: ... @focal.setter - def focal(self, arg1: float) -> None:... + def focal(self, arg1: float) -> None: ... @property - def height(self) -> int:... + def height(self) -> int: ... @height.setter - def height(self, arg0: int) -> None:... + def height(self, arg0: int) -> None: ... @property - def id(self) -> str:... + def id(self) -> str: ... @id.setter - def id(self, arg0: str) -> None:... + def id(self, arg0: str) -> None: ... @property - def k1(self) -> float:... + def k1(self) -> float: ... @property - def k2(self) -> float:... + def k2(self) -> float: ... @property - def k3(self) -> float:... + def k3(self) -> float: ... @property - def k4(self) -> float:... + def k4(self) -> float: ... @property - def k5(self) -> float:... + def k5(self) -> float: ... @property - def k6(self) -> float:... + def k6(self) -> float: ... @property - def p1(self) -> float:... + def p1(self) -> float: ... @property - def p2(self) -> float:... + def p2(self) -> float: ... @property - def principal_point(self) -> numpy.ndarray:... + def principal_point(self) -> numpy.typing.NDArray: ... @principal_point.setter - def principal_point(self, arg1: numpy.ndarray) -> None:... + def principal_point(self, arg1: numpy.typing.NDArray) -> None: ... @property - def projection_type(self) -> str:... + def projection_type(self) -> str: ... @property - def s0(self) -> float:... + def s0(self) -> float: ... @property - def s1(self) -> float:... + def s1(self) -> float: ... @property - def s2(self) -> float:... + def s2(self) -> float: ... @property - def s3(self) -> float:... + def s3(self) -> float: ... @property - def transition(self) -> float:... + def transition(self) -> float: ... @transition.setter - def transition(self, arg1: float) -> None:... + def transition(self, arg1: float) -> None: ... @property - def width(self) -> int:... + def width(self) -> int: ... @width.setter - def width(self, arg0: int) -> None:... + def width(self, arg0: int) -> None: ... + class CameraParameters: + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + def __str__(self) -> str: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... focal: "CameraParameters" aspect_ratio: "CameraParameters" k1: "CameraParameters" @@ -190,8 +237,8 @@ class CameraParameters: transition: "CameraParameters" none: "CameraParameters" __members__: Dict[str, "CameraParameters"] - @property - def name(self) -> str: ... + __entries: "dict" + class Pose: def __copy__(self) -> Pose: ... def __deepcopy__(self, arg0: dict) -> Pose: ... @@ -199,56 +246,71 @@ class Pose: @overload def __init__(self) -> None: ... @overload - def __init__(self, arg0: numpy.ndarray) -> None: ... + def __init__(self, arg0: numpy.typing.NDArray) -> None: ... @overload - def __init__(self, rotation: numpy.ndarray) -> None: ... + def __init__(self, rotation: numpy.typing.NDArray) -> None: ... @overload - def __init__(self, rotation: numpy.ndarray, translation: numpy.ndarray) -> None: ... - @overload - def __init__(self, rotation: numpy.ndarray, translation: numpy.ndarray) -> None: ... + def __init__( + self, rotation: numpy.typing.NDArray, translation: numpy.typing.NDArray + ) -> Union[None, None]: ... def __setstate__(self, arg0: tuple) -> None: ... def compose(self, arg0: Pose) -> Pose: ... - def get_R_cam_to_world(self) -> numpy.ndarray: ... - def get_R_cam_to_world_min(self) -> numpy.ndarray: ... - def get_R_world_to_cam(self) -> numpy.ndarray: ... - def get_R_world_to_cam_min(self) -> numpy.ndarray: ... - def get_Rt(self) -> numpy.ndarray: ... - def get_cam_to_world(self) -> numpy.ndarray: ... - def get_origin(self) -> numpy.ndarray: ... - def get_rotation_matrix(self) -> numpy.ndarray: ... - def get_t_cam_to_world(self) -> numpy.ndarray: ... - def get_t_world_to_cam(self) -> numpy.ndarray: ... - def get_world_to_cam(self) -> numpy.ndarray: ... + def get_R_cam_to_world(self) -> numpy.typing.NDArray: ... + def get_R_cam_to_world_min(self) -> numpy.typing.NDArray: ... + def get_R_world_to_cam(self) -> numpy.typing.NDArray: ... + def get_R_world_to_cam_min(self) -> numpy.typing.NDArray: ... + def get_Rt(self) -> numpy.typing.NDArray: ... + def get_cam_to_world(self) -> numpy.typing.NDArray: ... + def get_origin(self) -> numpy.typing.NDArray: ... + def get_rotation_matrix(self) -> numpy.typing.NDArray: ... + def get_t_cam_to_world(self) -> numpy.typing.NDArray: ... + def get_t_world_to_cam(self) -> numpy.typing.NDArray: ... + def get_world_to_cam(self) -> numpy.typing.NDArray: ... def inverse(self) -> Pose: ... def is_identity(self, arg0: float) -> bool: ... def relative_to(self, arg0: Pose) -> Pose: ... @overload - def set_from_cam_to_world(self, arg0: numpy.ndarray) -> None: ... - @overload - def set_from_cam_to_world(self, arg0: numpy.ndarray, arg1: numpy.ndarray) -> None: ... + def set_from_cam_to_world(self, arg0: numpy.typing.NDArray) -> None: ... @overload - def set_from_cam_to_world(self, arg0: numpy.ndarray, arg1: numpy.ndarray) -> None: ... + def set_from_cam_to_world( + self, arg0: numpy.typing.NDArray, arg1: numpy.typing.NDArray + ) -> Union[None, None]: ... @overload - def set_from_world_to_cam(self, arg0: numpy.ndarray) -> None: ... + def set_from_world_to_cam(self, arg0: numpy.typing.NDArray) -> None: ... @overload - def set_from_world_to_cam(self, arg0: numpy.ndarray, arg1: numpy.ndarray) -> None: ... - @overload - def set_from_world_to_cam(self, arg0: numpy.ndarray, arg1: numpy.ndarray) -> None: ... - def set_origin(self, arg0: numpy.ndarray) -> None: ... - def set_rotation_matrix(self, arg0: numpy.ndarray) -> None: ... - def transform(self, arg0: numpy.ndarray) -> numpy.ndarray: ... - def transform_inverse(self, arg0: numpy.ndarray) -> numpy.ndarray: ... - def transform_inverse_many(self, arg0: numpy.ndarray) -> numpy.ndarray: ... - def transform_many(self, arg0: numpy.ndarray) -> numpy.ndarray: ... + def set_from_world_to_cam( + self, arg0: numpy.typing.NDArray, arg1: numpy.typing.NDArray + ) -> Union[None, None]: ... + def set_origin(self, arg0: numpy.typing.NDArray) -> None: ... + def set_rotation_matrix(self, arg0: numpy.typing.NDArray) -> None: ... + def transform(self, arg0: numpy.typing.NDArray) -> numpy.typing.NDArray: ... + def transform_inverse(self, arg0: numpy.typing.NDArray) -> numpy.typing.NDArray: ... + def transform_inverse_many( + self, arg0: numpy.typing.NDArray + ) -> numpy.typing.NDArray: ... + def transform_many(self, arg0: numpy.typing.NDArray) -> numpy.typing.NDArray: ... @property - def rotation(self) -> numpy.ndarray:... + def rotation(self) -> numpy.typing.NDArray: ... @rotation.setter - def rotation(self, arg1: numpy.ndarray) -> None:... + def rotation(self, arg1: numpy.typing.NDArray) -> None: ... @property - def translation(self) -> numpy.ndarray:... + def translation(self) -> numpy.typing.NDArray: ... @translation.setter - def translation(self, arg1: numpy.ndarray) -> None:... + def translation(self, arg1: numpy.typing.NDArray) -> None: ... + class ProjectionType: + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + def __str__(self) -> str: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... PERSPECTIVE: "ProjectionType" BROWN: "ProjectionType" FISHEYE: "ProjectionType" @@ -260,65 +322,119 @@ class ProjectionType: RADIAL: "ProjectionType" SIMPLE_RADIAL: "ProjectionType" __members__: Dict[str, "ProjectionType"] - @property - def name(self) -> str: ... + __entries: "dict" + class Similarity: - def __init__(self, arg0: numpy.ndarray, arg1: numpy.ndarray, arg2: float) -> None: ... - def get_rotation_matrix(self) -> numpy.ndarray: ... + def __init__( + self, arg0: numpy.typing.NDArray, arg1: numpy.typing.NDArray, arg2: float + ) -> None: ... + def get_rotation_matrix(self) -> numpy.typing.NDArray: ... def inverse(self) -> Similarity: ... - def transform(self, arg0: numpy.ndarray) -> numpy.ndarray: ... + def transform(self, arg0: numpy.typing.NDArray) -> numpy.typing.NDArray: ... @property - def rotation(self) -> numpy.ndarray:... + def rotation(self) -> numpy.typing.NDArray: ... @rotation.setter - def rotation(self, arg1: numpy.ndarray) -> None:... + def rotation(self, arg1: numpy.typing.NDArray) -> None: ... @property - def scale(self) -> float:... + def scale(self) -> float: ... @scale.setter - def scale(self, arg1: float) -> None:... + def scale(self, arg1: float) -> None: ... @property - def translation(self) -> numpy.ndarray:... + def translation(self) -> numpy.typing.NDArray: ... @translation.setter - def translation(self, arg1: numpy.ndarray) -> None:... -def absolute_pose_n_points(arg0: numpy.ndarray, arg1: numpy.ndarray) -> numpy.ndarray:... -def absolute_pose_n_points_known_rotation(arg0: numpy.ndarray, arg1: numpy.ndarray) -> numpy.ndarray:... -def absolute_pose_three_points(arg0: numpy.ndarray, arg1: numpy.ndarray) -> List[numpy.ndarray]:... -def compute_camera_mapping(arg0: Camera, arg1: Camera, arg2: int, arg3: int) -> Tuple[numpy.ndarray, numpy.ndarray]:... -def epipolar_angle_two_bearings_many(arg0: numpy.ndarray, arg1: numpy.ndarray, arg2: numpy.ndarray, arg3: numpy.ndarray) -> numpy.ndarray:... -def essential_five_points(arg0: numpy.ndarray, arg1: numpy.ndarray) -> List[numpy.ndarray]:... -def essential_n_points(arg0: numpy.ndarray, arg1: numpy.ndarray) -> List[numpy.ndarray]:... -def point_refinement(arg0: numpy.ndarray, arg1: numpy.ndarray, arg2: numpy.ndarray, arg3: int) -> numpy.ndarray:... -def relative_pose_from_essential(arg0: numpy.ndarray, arg1: numpy.ndarray, arg2: numpy.ndarray) -> numpy.ndarray:... -def relative_pose_refinement(arg0: numpy.ndarray, arg1: numpy.ndarray, arg2: numpy.ndarray, arg3: int) -> numpy.ndarray:... -def relative_rotation_n_points(arg0: numpy.ndarray, arg1: numpy.ndarray) -> numpy.ndarray:... -def triangulate_bearings_dlt(arg0: List[numpy.ndarray], arg1: numpy.ndarray, arg2: float, arg3: float) -> Tuple[bool, numpy.ndarray]:... -def triangulate_bearings_midpoint(arg0: numpy.ndarray, arg1: numpy.ndarray, arg2: List[float], arg3: float, arg4: float) -> Tuple[bool, numpy.ndarray]:... -def triangulate_two_bearings_midpoint(arg0: numpy.ndarray, arg1: numpy.ndarray) -> Tuple[bool, numpy.ndarray]:... -def triangulate_two_bearings_midpoint_many(arg0: numpy.ndarray, arg1: numpy.ndarray, arg2: numpy.ndarray, arg3: numpy.ndarray) -> List[Tuple[bool, numpy.ndarray]]:... -BROWN = ... -DUAL = ... -FISHEYE = ... -FISHEYE62 = ... -FISHEYE624 = ... -FISHEYE_OPENCV = ... -PERSPECTIVE = ... -RADIAL = ... -SIMPLE_RADIAL = ... -SPHERICAL = ... -aspect_ratio = ... -cx = ... -cy = ... -focal = ... -k1 = ... -k2 = ... -k3 = ... -k4 = ... -k5 = ... -k6 = ... -none = ... -p1 = ... -p2 = ... -s0 = ... -s1 = ... -s2 = ... -s3 = ... -transition = ... \ No newline at end of file + def translation(self, arg1: numpy.typing.NDArray) -> None: ... + +def absolute_pose_n_points( + arg0: numpy.typing.NDArray, arg1: numpy.typing.NDArray +) -> numpy.typing.NDArray: ... +def absolute_pose_n_points_known_rotation( + arg0: numpy.typing.NDArray, arg1: numpy.typing.NDArray +) -> numpy.typing.NDArray: ... +def absolute_pose_three_points( + arg0: numpy.typing.NDArray, arg1: numpy.typing.NDArray +) -> list[numpy.typing.NDArray]: ... +def compute_camera_mapping( + arg0: Camera, arg1: Camera, arg2: int, arg3: int +) -> tuple[numpy.typing.NDArray, numpy.typing.NDArray]: ... +def epipolar_angle_two_bearings_many( + arg0: numpy.typing.NDArray, + arg1: numpy.typing.NDArray, + arg2: numpy.typing.NDArray, + arg3: numpy.typing.NDArray, +) -> numpy.typing.NDArray: ... +def essential_five_points( + arg0: numpy.typing.NDArray, arg1: numpy.typing.NDArray +) -> list[numpy.typing.NDArray]: ... +def essential_n_points( + arg0: numpy.typing.NDArray, arg1: numpy.typing.NDArray +) -> list[numpy.typing.NDArray]: ... +def point_refinement( + arg0: numpy.typing.NDArray, + arg1: numpy.typing.NDArray, + arg2: numpy.typing.NDArray, + arg3: int, +) -> numpy.typing.NDArray: ... +def relative_pose_from_essential( + arg0: numpy.typing.NDArray, arg1: numpy.typing.NDArray, arg2: numpy.typing.NDArray +) -> numpy.typing.NDArray: ... +def relative_pose_refinement( + arg0: numpy.typing.NDArray, + arg1: numpy.typing.NDArray, + arg2: numpy.typing.NDArray, + arg3: int, +) -> numpy.typing.NDArray: ... +def relative_rotation_n_points( + arg0: numpy.typing.NDArray, arg1: numpy.typing.NDArray +) -> numpy.typing.NDArray: ... +def triangulate_bearings_dlt( + arg0: list[numpy.typing.NDArray], + arg1: numpy.typing.NDArray, + arg2: float, + arg3: float, + arg4: float, +) -> tuple[bool, numpy.typing.NDArray]: ... +def triangulate_bearings_midpoint( + arg0: numpy.typing.NDArray, + arg1: numpy.typing.NDArray, + arg2: list[float], + arg3: float, + arg4: float, +) -> tuple[bool, numpy.typing.NDArray]: ... +def triangulate_two_bearings_midpoint( + arg0: numpy.typing.NDArray, arg1: numpy.typing.NDArray +) -> tuple[bool, numpy.typing.NDArray]: ... +def triangulate_two_bearings_midpoint_many( + arg0: numpy.typing.NDArray, + arg1: numpy.typing.NDArray, + arg2: numpy.typing.NDArray, + arg3: numpy.typing.NDArray, +) -> list[tuple[bool, numpy.typing.NDArray]]: ... + +BROWN: "ProjectionType" +DUAL: "ProjectionType" +FISHEYE: "ProjectionType" +FISHEYE62: "ProjectionType" +FISHEYE624: "ProjectionType" +FISHEYE_OPENCV: "ProjectionType" +PERSPECTIVE: "ProjectionType" +RADIAL: "ProjectionType" +SIMPLE_RADIAL: "ProjectionType" +SPHERICAL: "ProjectionType" +aspect_ratio: "CameraParameters" +cx: "CameraParameters" +cy: "CameraParameters" +focal: "CameraParameters" +k1: "CameraParameters" +k2: "CameraParameters" +k3: "CameraParameters" +k4: "CameraParameters" +k5: "CameraParameters" +k6: "CameraParameters" +none: "CameraParameters" +p1: "CameraParameters" +p2: "CameraParameters" +s0: "CameraParameters" +s1: "CameraParameters" +s2: "CameraParameters" +s3: "CameraParameters" +transition: "CameraParameters" diff --git a/opensfm/src/geometry/python/pybind.cc b/opensfm/src/geometry/python/pybind.cc index 9412cd790..907e1bc11 100644 --- a/opensfm/src/geometry/python/pybind.cc +++ b/opensfm/src/geometry/python/pybind.cc @@ -1,3 +1,4 @@ +#include #include #include #include @@ -76,29 +77,35 @@ PYBIND11_MODULE(pygeometry, m) { .def("get_parameters_values", &geometry::Camera::GetParametersValues) .def("get_parameters_types", &geometry::Camera::GetParametersTypes) .def("get_parameters_map", &geometry::Camera::GetParametersMap) - .def_static("pixel_to_normalized_coordinates_common", - (Vec2d(*)(const Vec2d&, const int, const int)) & - geometry::Camera::PixelToNormalizedCoordinates) - .def_static("pixel_to_normalized_coordinates_many_common", - (MatX2d(*)(const MatX2d&, const int, const int)) & - geometry::Camera::PixelToNormalizedCoordinatesMany) + .def_static( + "pixel_to_normalized_coordinates_common", + (Vec2d (*)(const Vec2d&, const int, + const int))&geometry::Camera::PixelToNormalizedCoordinates) + .def_static( + "pixel_to_normalized_coordinates_many_common", + (MatX2d (*)( + const MatX2d&, const int, + const int))&geometry::Camera::PixelToNormalizedCoordinatesMany) .def("pixel_to_normalized_coordinates", - (Vec2d(geometry::Camera::*)(const Vec2d&) const) & + (Vec2d (geometry::Camera::*)(const Vec2d&) const) & geometry::Camera::PixelToNormalizedCoordinates) .def("pixel_to_normalized_coordinates_many", - (MatX2d(geometry::Camera::*)(const MatX2d&) const) & + (MatX2d (geometry::Camera::*)(const MatX2d&) const) & geometry::Camera::PixelToNormalizedCoordinatesMany) - .def_static("normalized_to_pixel_coordinates_common", - (Vec2d(*)(const Vec2d&, const int, const int)) & - geometry::Camera::NormalizedToPixelCoordinates) - .def_static("normalized_to_pixel_coordinates_many_common", - (MatX2d(*)(const MatX2d&, const int, const int)) & - geometry::Camera::NormalizedToPixelCoordinatesMany) + .def_static( + "normalized_to_pixel_coordinates_common", + (Vec2d (*)(const Vec2d&, const int, + const int))&geometry::Camera::NormalizedToPixelCoordinates) + .def_static( + "normalized_to_pixel_coordinates_many_common", + (MatX2d (*)( + const MatX2d&, const int, + const int))&geometry::Camera::NormalizedToPixelCoordinatesMany) .def("normalized_to_pixel_coordinates", - (Vec2d(geometry::Camera::*)(const Vec2d&) const) & + (Vec2d (geometry::Camera::*)(const Vec2d&) const) & geometry::Camera::NormalizedToPixelCoordinates) .def("normalized_to_pixel_coordinates_many", - (MatX2d(geometry::Camera::*)(const MatX2d&) const) & + (MatX2d (geometry::Camera::*)(const MatX2d&) const) & geometry::Camera::NormalizedToPixelCoordinatesMany) .def_readwrite("width", &geometry::Camera::width) .def_readwrite("height", &geometry::Camera::height) @@ -187,7 +194,7 @@ PYBIND11_MODULE(pygeometry, m) { principal_point[1]); }) .def_property_readonly("projection_type", - (std::string(geometry::Camera::*)() const) & + (std::string (geometry::Camera::*)() const) & geometry::Camera::GetProjectionString) .def_static("is_panorama", [](const std::string& s) { @@ -281,7 +288,7 @@ PYBIND11_MODULE(pygeometry, m) { py::return_value_policy::copy) .def( "__deepcopy__", - [](const geometry::Camera& c, const py::dict& d) { return c; }, + [](const geometry::Camera& c, const py::dict& /* d */) { return c; }, py::return_value_policy::copy); m.def("compute_camera_mapping", geometry::ComputeCameraMapping, py::call_guard()); @@ -321,22 +328,28 @@ PYBIND11_MODULE(pygeometry, m) { .def("get_cam_to_world", &geometry::Pose::CameraToWorld) .def("get_world_to_cam", &geometry::Pose::WorldToCamera) // C++11 - .def("set_from_world_to_cam", (void (geometry::Pose::*)(const Mat4d&)) & - geometry::Pose::SetFromWorldToCamera) - .def("set_from_world_to_cam", - (void (geometry::Pose::*)(const Mat3d&, const Vec3d&)) & - geometry::Pose::SetFromWorldToCamera) .def("set_from_world_to_cam", - (void (geometry::Pose::*)(const Vec3d&, const Vec3d&)) & - geometry::Pose::SetFromWorldToCamera) - .def("set_from_cam_to_world", (void (geometry::Pose::*)(const Mat4d&)) & - geometry::Pose::SetFromCameraToWorld) - .def("set_from_cam_to_world", - (void (geometry::Pose::*)(const Mat3d&, const Vec3d&)) & - geometry::Pose::SetFromCameraToWorld) + (void (geometry::Pose::*)( + const Mat4d&))&geometry::Pose::SetFromWorldToCamera) + .def( + "set_from_world_to_cam", + (void (geometry::Pose::*)( + const Mat3d&, const Vec3d&))&geometry::Pose::SetFromWorldToCamera) + .def( + "set_from_world_to_cam", + (void (geometry::Pose::*)( + const Vec3d&, const Vec3d&))&geometry::Pose::SetFromWorldToCamera) .def("set_from_cam_to_world", - (void (geometry::Pose::*)(const Vec3d&, const Vec3d&)) & - geometry::Pose::SetFromCameraToWorld) + (void (geometry::Pose::*)( + const Mat4d&))&geometry::Pose::SetFromCameraToWorld) + .def( + "set_from_cam_to_world", + (void (geometry::Pose::*)( + const Mat3d&, const Vec3d&))&geometry::Pose::SetFromCameraToWorld) + .def( + "set_from_cam_to_world", + (void (geometry::Pose::*)( + const Vec3d&, const Vec3d&))&geometry::Pose::SetFromCameraToWorld) .def("get_origin", &geometry::Pose::GetOrigin) .def("set_origin", &geometry::Pose::SetOrigin) .def("get_R_cam_to_world", &geometry::Pose::RotationCameraToWorld) @@ -374,7 +387,7 @@ PYBIND11_MODULE(pygeometry, m) { py::return_value_policy::copy) .def( "__deepcopy__", - [](const geometry::Pose& p, const py::dict& d) { return p; }, + [](const geometry::Pose& p, const py::dict& /* d */) { return p; }, py::return_value_policy::copy) .def("inverse", [](const geometry::Pose& p) { geometry::Pose new_pose; diff --git a/opensfm/src/geometry/relative_pose.h b/opensfm/src/geometry/relative_pose.h index 997875764..febf958da 100644 --- a/opensfm/src/geometry/relative_pose.h +++ b/opensfm/src/geometry/relative_pose.h @@ -3,8 +3,10 @@ #include #include #include + #include #include + #include "triangulation.h" template @@ -87,7 +89,11 @@ struct RelativePoseCost { std::srand(42); const int count = end_ - begin_; for (int i = 0; i < MAX_ERRORS; ++i) { - const int index = (float(std::rand()) / RAND_MAX) * count; + // Note that float(RAND_MAX) cannot be exactly represented as a float. We + // ignore the small inaccuracy here; this is already a bad way to get + // random numbers. + const int index = + (float(std::rand()) / static_cast(RAND_MAX)) * count; picked_errors_.push_back(begin_ + index); } } diff --git a/opensfm/src/geometry/src/absolute_pose.cc b/opensfm/src/geometry/src/absolute_pose.cc index 116cbd9ca..9041139c7 100644 --- a/opensfm/src/geometry/src/absolute_pose.cc +++ b/opensfm/src/geometry/src/absolute_pose.cc @@ -2,7 +2,7 @@ Eigen::Matrix3d RotationMatrixAroundAxis(const double cos_theta, const double sin_theta, - const Eigen::Vector3d &v) { + const Eigen::Vector3d& v) { Eigen::Matrix3d R; const auto one_minus_cos_theta = 1.0 - cos_theta; R(0, 0) = cos_theta + v[0] * v[0] * one_minus_cos_theta; @@ -19,8 +19,8 @@ Eigen::Matrix3d RotationMatrixAroundAxis(const double cos_theta, namespace geometry { std::vector> AbsolutePoseThreePoints( - const Eigen::Matrix &bearings, - const Eigen::Matrix &points) { + const Eigen::Matrix& bearings, + const Eigen::Matrix& points) { std::vector> samples( bearings.rows()); for (int i = 0; i < bearings.rows(); ++i) { @@ -31,8 +31,8 @@ std::vector> AbsolutePoseThreePoints( } Eigen::Matrix AbsolutePoseNPoints( - const Eigen::Matrix &bearings, - const Eigen::Matrix &points) { + const Eigen::Matrix& bearings, + const Eigen::Matrix& points) { std::vector> samples( bearings.rows()); for (int i = 0; i < bearings.rows(); ++i) { @@ -43,8 +43,8 @@ Eigen::Matrix AbsolutePoseNPoints( } Eigen::Vector3d AbsolutePoseNPointsKnownRotation( - const Eigen::Matrix &bearings, - const Eigen::Matrix &points) { + const Eigen::Matrix& bearings, + const Eigen::Matrix& points) { std::vector> samples( bearings.rows()); for (int i = 0; i < bearings.rows(); ++i) { diff --git a/opensfm/src/geometry/src/camera.cc b/opensfm/src/geometry/src/camera.cc index 45619f6cf..0fd656de7 100644 --- a/opensfm/src/geometry/src/camera.cc +++ b/opensfm/src/geometry/src/camera.cc @@ -95,9 +95,9 @@ Camera Camera::CreateFisheye62Camera(double focal, double aspect_ratio, /** Create a Fisheye624 camera with 15 parameters: - - params: f, cx, cy, (radial) k1, k2, k3, k4, k5, k6 (tangential) p1, p2 (thin prism) s0, s1, s2, s3 - Note that in arvr, the parameters start at 0 and p1/p2 are reversed - See: https://fburl.com/diffusion/xnhraa2z + - params: f, cx, cy, (radial) k1, k2, k3, k4, k5, k6 (tangential) p1, p2 (thin + prism) s0, s1, s2, s3 Note that in arvr, the parameters start at 0 and p1/p2 + are reversed See: https://fburl.com/diffusion/xnhraa2z */ Camera Camera::CreateFisheye624Camera(double focal, double aspect_ratio, const Vec2d& principal_point, @@ -117,9 +117,9 @@ Camera Camera::CreateFisheye624Camera(double focal, double aspect_ratio, Camera::Parameters::Cx, Camera::Parameters::Cy}; camera.values_.resize(16); camera.values_ << distortion[0], distortion[1], distortion[2], distortion[3], - distortion[4], distortion[5], distortion[6], distortion[7], - distortion[8], distortion[9], distortion[10], distortion[11], - focal, aspect_ratio, principal_point[0], principal_point[1]; + distortion[4], distortion[5], distortion[6], distortion[7], distortion[8], + distortion[9], distortion[10], distortion[11], focal, aspect_ratio, + principal_point[0], principal_point[1]; return camera; } @@ -276,15 +276,15 @@ Mat3d Camera::GetProjectionMatrix() const { return unnormalized; } -Mat3d Camera::GetProjectionMatrixScaled(int width, int height) const { - const auto unnormalizer = std::max(width, height); +Mat3d Camera::GetProjectionMatrixScaled(int width_2, int height_2) const { + const auto unnormalizer = std::max(width_2, height_2); Mat3d unnormalized = Mat3d::Zero(); const auto projection_matrix = GetProjectionMatrix(); unnormalized.block<2, 2>(0, 0) << unnormalizer * projection_matrix.block<2, 2>(0, 0); - unnormalized.col(2) << projection_matrix(0, 2) * unnormalizer + 0.5 * width, - projection_matrix(1, 2) * unnormalizer + 0.5 * height, 1.0; + unnormalized.col(2) << projection_matrix(0, 2) * unnormalizer + 0.5 * width_2, + projection_matrix(1, 2) * unnormalizer + 0.5 * height_2, 1.0; return unnormalized; } @@ -366,7 +366,7 @@ MatX2d Camera::PixelToNormalizedCoordinatesMany(const MatX2d& px_coords, norm_coords.row(i) = PixelToNormalizedCoordinates(px_coords.row(i), width, height); } - return px_coords; + return norm_coords; } Vec2d Camera::NormalizedToPixelCoordinates(const Vec2d& norm_coord) const { diff --git a/opensfm/src/geometry/src/covariance.cc b/opensfm/src/geometry/src/covariance.cc index c88942b61..e50b9eb11 100644 --- a/opensfm/src/geometry/src/covariance.cc +++ b/opensfm/src/geometry/src/covariance.cc @@ -1,7 +1,6 @@ #include -namespace geometry { -namespace covariance { +namespace geometry::covariance { using PointJacobian = Eigen::Matrix; std::pair ComputeJacobianReprojectionError( const Camera& camera, const Pose& pose, const Vec2d& observation, @@ -27,7 +26,7 @@ std::pair ComputePointInverseCovariance( const std::vector& observations, const Vec3d& point) { double sigma2 = 0; // Assume centered errors Mat3d covariance = Mat3d::Zero(); - for (int i = 0; i < cameras.size(); ++i) { + for (int i = 0; i < observations.size(); ++i) { const auto result = ComputeJacobianReprojectionError( cameras[i], poses[i], observations[i], point); const auto& jacobian = result.first; @@ -39,5 +38,4 @@ std::pair ComputePointInverseCovariance( return std::make_pair(covariance, sigma2); } -} // namespace covariance -} // namespace geometry +} // namespace geometry::covariance diff --git a/opensfm/src/geometry/src/essential.cc b/opensfm/src/geometry/src/essential.cc index 52c336f14..50c2dfa7e 100644 --- a/opensfm/src/geometry/src/essential.cc +++ b/opensfm/src/geometry/src/essential.cc @@ -1,8 +1,8 @@ #include "../essential.h" // Multiply two polynomials of degree 1. -Eigen::Matrix o1(const Eigen::Matrix &a, - const Eigen::Matrix &b) { +Eigen::Matrix o1(const Eigen::Matrix& a, + const Eigen::Matrix& b) { Eigen::Matrix res = Eigen::Matrix::Zero(20); res(coef_xx) = a(coef_x) * b(coef_x); @@ -20,8 +20,8 @@ Eigen::Matrix o1(const Eigen::Matrix &a, } // Multiply a polynomial of degree 2, a, by a polynomial of degree 1, b. -Eigen::Matrix o2(const Eigen::Matrix &a, - const Eigen::Matrix &b) { +Eigen::Matrix o2(const Eigen::Matrix& a, + const Eigen::Matrix& b) { Eigen::Matrix res(20); res(coef_xxx) = a(coef_xx) * b(coef_x); @@ -54,7 +54,7 @@ Eigen::Matrix o2(const Eigen::Matrix &a, // Builds the polynomial constraint matrix M. Eigen::MatrixXd FivePointsPolynomialConstraints( - const Eigen::MatrixXd &E_basis) { + const Eigen::MatrixXd& E_basis) { // Build the polynomial form of E (equation (8) in Stewenius et al. [1]) Eigen::Matrix E[3][3]; for (int i = 0; i < 3; ++i) { @@ -111,8 +111,8 @@ Eigen::MatrixXd FivePointsPolynomialConstraints( } // Gauss--Jordan elimination for the constraint matrix. -bool FivePointsGaussJordan(Eigen::MatrixXd *Mp) { - Eigen::MatrixXd &M = *Mp; +bool FivePointsGaussJordan(Eigen::MatrixXd* Mp) { + Eigen::MatrixXd& M = *Mp; // Gauss Elimination. for (int i = 0; i < 10; ++i) { @@ -141,8 +141,8 @@ bool FivePointsGaussJordan(Eigen::MatrixXd *Mp) { namespace geometry { std::vector> EssentialFivePoints( - const Eigen::Matrix &x1, - const Eigen::Matrix &x2) { + const Eigen::Matrix& x1, + const Eigen::Matrix& x2) { if ((x1.cols() != x2.cols()) || (x1.rows() != x2.rows())) { throw std::runtime_error("Features matrices have different sizes."); } @@ -155,8 +155,8 @@ std::vector> EssentialFivePoints( } std::vector> EssentialNPoints( - const Eigen::Matrix &x1, - const Eigen::Matrix &x2) { + const Eigen::Matrix& x1, + const Eigen::Matrix& x2) { if ((x1.cols() != x2.cols()) || (x1.rows() != x2.rows())) { throw std::runtime_error("Features matrices have different sizes."); } diff --git a/opensfm/src/geometry/src/relative_pose.cc b/opensfm/src/geometry/src/relative_pose.cc index 2a7cf5bbd..00a7ef5da 100644 --- a/opensfm/src/geometry/src/relative_pose.cc +++ b/opensfm/src/geometry/src/relative_pose.cc @@ -3,8 +3,8 @@ namespace geometry { Eigen::Matrix RelativePoseFromEssential( - const Eigen::Matrix3d &essential, const Eigen::Matrix &x1, - const Eigen::Matrix &x2) { + const Eigen::Matrix3d& essential, const Eigen::Matrix& x1, + const Eigen::Matrix& x2) { if ((x1.cols() != x2.cols()) || (x1.rows() != x2.rows())) { throw std::runtime_error("Features matrices have different sizes."); } @@ -17,8 +17,8 @@ Eigen::Matrix RelativePoseFromEssential( } Eigen::Matrix3d RelativeRotationNPoints( - const Eigen::Matrix &x1, - const Eigen::Matrix &x2) { + const Eigen::Matrix& x1, + const Eigen::Matrix& x2) { if ((x1.cols() != x2.cols()) || (x1.rows() != x2.rows())) { throw std::runtime_error("Features matrices have different sizes."); } @@ -31,9 +31,9 @@ Eigen::Matrix3d RelativeRotationNPoints( } Eigen::Matrix RelativePoseRefinement( - const Eigen::Matrix &relative_pose, - const Eigen::Matrix &x1, - const Eigen::Matrix &x2, int iterations) { + const Eigen::Matrix& relative_pose, + const Eigen::Matrix& x1, + const Eigen::Matrix& x2, int iterations) { if ((x1.cols() != x2.cols()) || (x1.rows() != x2.rows())) { throw std::runtime_error("Features matrices have different sizes."); } diff --git a/opensfm/src/geometry/src/triangulation.cc b/opensfm/src/geometry/src/triangulation.cc index 1723731fa..3424a4e1b 100644 --- a/opensfm/src/geometry/src/triangulation.cc +++ b/opensfm/src/geometry/src/triangulation.cc @@ -5,115 +5,160 @@ #include #include #include -#include -double AngleBetweenVectors(const Eigen::Vector3d &u, const Eigen::Vector3d &v) { - double c = (u.dot(v)) / sqrt(u.dot(u) * v.dot(v)); - if (std::fabs(c) >= 1.0) - return 0.0; - else - return acos(c); -} +#include -Eigen::Vector4d TriangulateBearingsDLTSolve( - const Eigen::Matrix &bearings, - const std::vector> &Rts) { - const int nviews = bearings.rows(); - assert(nviews == Rts.size()); +namespace { - Eigen::MatrixXd A(2 * nviews, 4); - for (int i = 0; i < nviews; i++) { - A.row(2 * i) = - bearings(i, 0) * Rts[i].row(2) - bearings(i, 2) * Rts[i].row(0); - A.row(2 * i + 1) = - bearings(i, 1) * Rts[i].row(2) - bearings(i, 2) * Rts[i].row(1); +struct BearingErrorCost : public ceres::CostFunction { + constexpr static int Size = 3; + + BearingErrorCost(const MatX3d& centers, const MatX3d& bearings, + const Vec3d& point) + : centers_(centers), bearings_(bearings), point_(point) { + mutable_parameter_block_sizes()->push_back(Size); + set_num_residuals(bearings_.rows() * 3); } + bool Evaluate(double const* const* parameters, double* residuals, + double** jacobians) const override { + const double* point = parameters[0]; + for (int i = 0; i < bearings_.rows(); ++i) { + const Vec3d& center = centers_.row(i); + const Vec3d& bearing = bearings_.row(i); - Eigen::JacobiSVD mySVD(A, Eigen::ComputeFullV); - Eigen::Vector4d worldPoint; - worldPoint[0] = mySVD.matrixV()(0, 3); - worldPoint[1] = mySVD.matrixV()(1, 3); - worldPoint[2] = mySVD.matrixV()(2, 3); - worldPoint[3] = mySVD.matrixV()(3, 3); + /* Error only */ + double* dummy = nullptr; + double projected[] = {point[0] - center(0), point[1] - center(1), + point[2] - center(2)}; + if (!jacobians) { + geometry::Normalize::Forward(&projected[0], dummy, &projected[0]); + } else { + constexpr int JacobianSize = Size * Size; + double jacobian[JacobianSize]; + geometry::Normalize::ForwardDerivatives( + &projected[0], dummy, &projected[0], &jacobian[0]); + double* jac_point = jacobians[0]; + if (jac_point) { + for (int j = 0; j < Size; ++j) { + for (int k = 0; k < Size; ++k) { + jac_point[i * JacobianSize + j * Size + k] = + jacobian[j * Size + k]; + } + } + } + } - return worldPoint; -} + // The error is the difference between the predicted and observed position + for (int j = 0; j < Size; ++j) { + residuals[i * 3 + j] = (projected[j] - bearing[j]); + } + } + return true; + } + + const MatX3d& centers_; + const MatX3d& bearings_; + const Vec3d& point_; +}; + +} // namespace namespace geometry { -std::pair TriangulateBearingsDLT( - const std::vector> &Rts, - const Eigen::Matrix &bearings, double threshold, - double min_angle) { +double AngleBetweenVectors(const Vec3d& u, const Vec3d& v) { + double c = (u.dot(v)) / std::sqrt(u.dot(u) * v.dot(v)); + if (std::fabs(c) >= 1.0) { + return 0.0; + } else { + return acos(c); + } +} +std::pair TriangulateBearingsDLT(const std::vector& Rts, + const MatX3d& bearings, + double threshold, + double min_angle, + double min_depth) { const int count = Rts.size(); - Eigen::MatrixXd world_bearings(count, 3); + MatXd world_bearings(count, 3); bool angle_ok = false; for (int i = 0; i < count && !angle_ok; ++i) { - const Eigen::Matrix Rt = Rts[i]; + const Mat34d& Rt = Rts[i]; world_bearings.row(i) = Rt.block<3, 3>(0, 0).transpose() * bearings.row(i).transpose(); for (int j = 0; j < i && !angle_ok; ++j) { const double angle = AngleBetweenVectors(world_bearings.row(i), world_bearings.row(j)); - if (angle >= min_angle) { + if (angle >= min_angle && angle <= M_PI - min_angle) { angle_ok = true; } } } if (!angle_ok) { - return std::make_pair(false, Eigen::Vector3d()); + return std::make_pair(false, Vec3d()); } - Eigen::Vector4d X = TriangulateBearingsDLTSolve(bearings, Rts); + Vec4d X = TriangulateBearingsDLTSolve(bearings, Rts); X /= X(3); for (int i = 0; i < count; ++i) { - const Eigen::Vector3d projected = Rts[i] * X; - if (AngleBetweenVectors(projected, bearings.row(i)) > threshold) { - return std::make_pair(false, Eigen::Vector3d()); + const Vec3d projected = Rts[i] * X; + const Vec3d measured = bearings.row(i); + if (AngleBetweenVectors(projected, measured) > threshold) { + return std::make_pair(false, Vec3d()); + } + if (projected.dot(measured) < min_depth) { + return std::make_pair(false, Vec3d()); } } return std::make_pair(true, X.head<3>()); } -std::vector> -TriangulateTwoBearingsMidpointMany( - const Eigen::Matrix &bearings1, - const Eigen::Matrix &bearings2, - const Eigen::Matrix3d &rotation, const Eigen::Vector3d &translation) { - std::vector> triangulated(bearings1.rows()); - Eigen::Matrix os, bs; - os.row(0) = Eigen::Vector3d::Zero(); - os.row(1) = translation; - for (int i = 0; i < bearings1.rows(); ++i) { - bs.row(0) = bearings1.row(i); - bs.row(1) = rotation * bearings2.row(i).transpose(); - triangulated[i] = TriangulateTwoBearingsMidpointSolve(os, bs); +Vec4d TriangulateBearingsDLTSolve(const MatX3d& bearings, + const std::vector& Rts) { + const int nviews = bearings.rows(); + assert(nviews == Rts.size()); + + MatXd A(2 * nviews, 4); + for (int i = 0; i < nviews; i++) { + A.row(2 * i) = + bearings(i, 0) * Rts[i].row(2) - bearings(i, 2) * Rts[i].row(0); + A.row(2 * i + 1) = + bearings(i, 1) * Rts[i].row(2) - bearings(i, 2) * Rts[i].row(1); } - return triangulated; + + Eigen::JacobiSVD mySVD(A, Eigen::ComputeFullV); + Vec4d worldPoint; + worldPoint[0] = mySVD.matrixV()(0, 3); + worldPoint[1] = mySVD.matrixV()(1, 3); + worldPoint[2] = mySVD.matrixV()(2, 3); + worldPoint[3] = mySVD.matrixV()(3, 3); + + return worldPoint; } -std::pair TriangulateBearingsMidpoint( - const Eigen::Matrix ¢ers, - const Eigen::Matrix &bearings, - const std::vector &threshold_list, - double min_angle, double max_angle) { +std::pair TriangulateBearingsMidpoint( + const MatX3d& centers, const MatX3d& bearings, + const std::vector& threshold_list, double min_angle, + double min_depth) { const int count = centers.rows(); + if (threshold_list.size() < static_cast(count)) { + return std::make_pair(false, Vec3d()); + } // Check angle between rays bool angle_ok = false; for (int i = 0; i < count && !angle_ok; ++i) { for (int j = 0; j < i && !angle_ok; ++j) { const auto angle = AngleBetweenVectors(bearings.row(i), bearings.row(j)); - if (angle >= min_angle && angle <= max_angle) { + if (angle >= min_angle && angle <= M_PI - min_angle) { angle_ok = true; } } } if (!angle_ok) { - return std::make_pair(false, Eigen::Vector3d()); + return std::make_pair(false, Vec3d()); } // Triangulate @@ -121,99 +166,62 @@ std::pair TriangulateBearingsMidpoint( // Check reprojection error for (int i = 0; i < count; ++i) { - const Eigen::Vector3d projected = X - centers.row(i).transpose(); - const Eigen::Vector3d measured = bearings.row(i); + const Vec3d projected = X - centers.row(i).transpose(); + const Vec3d measured = bearings.row(i); if (AngleBetweenVectors(projected, measured) > threshold_list[i]) { - return std::make_pair(false, Eigen::Vector3d()); + return std::make_pair(false, Vec3d()); + } + if (projected.dot(measured) < min_depth) { + return std::make_pair(false, Vec3d()); } } return std::make_pair(true, X.head<3>()); } -Eigen::Matrix -EpipolarAngleTwoBearingsMany( - const Eigen::Matrix &bearings1, - const Eigen::Matrix &bearings2, - const Eigen::Matrix3f &rotation, const Eigen::Vector3f &translation) { +std::vector> TriangulateTwoBearingsMidpointMany( + const MatX3d& bearings1, const MatX3d& bearings2, const Mat3d& rotation, + const Vec3d& translation) { + std::vector> triangulated(bearings1.rows()); + Eigen::Matrix os, bs; + os.row(0) = Vec3d::Zero(); + os.row(1) = translation; + for (int i = 0; i < bearings1.rows(); ++i) { + bs.row(0) = bearings1.row(i); + bs.row(1) = rotation * bearings2.row(i).transpose(); + triangulated[i] = TriangulateTwoBearingsMidpointSolve(os, bs); + } + return triangulated; +} + +MatXd EpipolarAngleTwoBearingsMany(const MatX3d& bearings1, + const MatX3d& bearings2, + const Mat3d& rotation, + const Vec3d& translation) { const auto translation_normalized = translation.normalized(); const auto bearings2_world = bearings2 * rotation.transpose(); const auto count1 = bearings1.rows(); - Eigen::Matrix epi1(count1, 3); + MatX3d epi1(count1, 3); for (int i = 0; i < count1; ++i) { - const Vec3f bearing = bearings1.row(i); + const Vec3d bearing = bearings1.row(i); epi1.row(i) = translation_normalized.cross(bearing).normalized(); } const auto count2 = bearings2.rows(); - Eigen::Matrix epi2(count2, 3); + MatX3d epi2(count2, 3); for (int i = 0; i < count2; ++i) { - const Vec3f bearing = bearings2_world.row(i); + const Vec3d bearing = bearings2_world.row(i); epi2.row(i) = translation_normalized.cross(bearing).normalized(); } - Eigen::Matrix symmetric_epi = - (((epi1 * bearings2_world.transpose()).array().abs() + - (bearings1 * epi2.transpose()).array().abs()) / - 2.0); + MatXd symmetric_epi = (((epi1 * bearings2_world.transpose()).array().abs() + + (bearings1 * epi2.transpose()).array().abs()) / + 2.0); return M_PI / 2.0 - symmetric_epi.array().acos(); } -struct BearingErrorCost : public ceres::CostFunction { - constexpr static int Size = 3; - - BearingErrorCost(const MatX3d ¢ers, const MatX3d &bearings, - const Vec3d &point) - : centers_(centers), bearings_(bearings), point_(point) { - mutable_parameter_block_sizes()->push_back(Size); - set_num_residuals(bearings_.rows() * 3); - } - bool Evaluate(double const *const *parameters, double *residuals, - double **jacobians) const override { - const double *point = parameters[0]; - for (int i = 0; i < bearings_.rows(); ++i) { - const Vec3d ¢er = centers_.row(i); - const Vec3d &bearing = bearings_.row(i); - - /* Error only */ - double *dummy = nullptr; - double projected[] = {point[0] - center(0), point[1] - center(1), - point[2] - center(2)}; - if (!jacobians) { - geometry::Normalize::Forward(&projected[0], dummy, &projected[0]); - } else { - constexpr int JacobianSize = Size * Size; - double jacobian[JacobianSize]; - geometry::Normalize::ForwardDerivatives( - &projected[0], dummy, &projected[0], &jacobian[0]); - double *jac_point = jacobians[0]; - if (jac_point) { - for (int j = 0; j < Size; ++j) { - for (int k = 0; k < Size; ++k) { - jac_point[i * JacobianSize + j * Size + k] = - jacobian[j * Size + k]; - } - } - } - } - - // The error is the difference between the predicted and observed position - for (int j = 0; j < Size; ++j) { - residuals[i * 3 + j] = (projected[j] - bearing[j]); - } - } - return true; - } - - const MatX3d ¢ers_; - const MatX3d &bearings_; - const Vec3d &point_; -}; - -constexpr int BearingErrorCost::Size; - -Vec3d PointRefinement(const MatX3d ¢ers, const MatX3d &bearings, - const Vec3d &point, int iterations) { +Vec3d PointRefinement(const MatX3d& centers, const MatX3d& bearings, + const Vec3d& point, int iterations) { using BearingCostFunction = ceres::TinySolverCostFunctionAdapter; BearingErrorCost cost(centers, bearings, point); diff --git a/opensfm/src/geometry/test/camera_functions_test.cc b/opensfm/src/geometry/test/camera_functions_test.cc index d851c28dd..bc33584f1 100644 --- a/opensfm/src/geometry/test/camera_functions_test.cc +++ b/opensfm/src/geometry/test/camera_functions_test.cc @@ -5,8 +5,6 @@ #include #include -#include -#include class FunctionFixture : public ::testing::Test { public: @@ -117,7 +115,7 @@ TEST_F(FunctionFixture, EvaluatesDerivativesCorrectly) { ASSERT_NEAR(expected, evaluated, 1e-20); /* Jacobian ordering : d_input | d_parameters */ - typedef Eigen::AutoDiffScalar AScalar; + using AScalar = Eigen::AutoDiffScalar; VecX eval_adiff(1); eval_adiff(0).value() = in[0]; eval_adiff(0).derivatives() = VecXd::Unit(7, 0); @@ -165,7 +163,7 @@ TEST_F(PoseFixture, EvaluatesCorrectly) { } TEST_F(PoseFixture, EvaluatesAllDerivativesCorrectly) { - typedef Eigen::AutoDiffScalar AScalar; + using AScalar = Eigen::AutoDiffScalar; VecX point_adiff(3); constexpr int size = 9; @@ -199,7 +197,7 @@ TEST_F(PoseFixture, EvaluatesAllDerivativesCorrectly) { } TEST_F(PoseFixture, EvaluatesPointDerivativesCorrectly) { - typedef Eigen::AutoDiffScalar AScalar; + using AScalar = Eigen::AutoDiffScalar; VecX point_adiff(3); @@ -286,8 +284,8 @@ class CameraDerivativesFixture : public ::testing::Test { for (int j = 0; j < size_params; ++j) { ASSERT_NEAR(projection_expected[i].derivatives()(j), jacobian(i, j), eps) - << "where (i, j) = (" << testing::PrintToString(i) - << ", " << testing::PrintToString(j) << ')'; + << "where (i, j) = (" << testing::PrintToString(i) << ", " + << testing::PrintToString(j) << ')'; } ASSERT_NEAR(projection_expected[i].value(), projected[i], eps); } @@ -305,7 +303,7 @@ class CameraDerivativesFixture : public ::testing::Test { Vec2d principal_point; double point[3]; - typedef Eigen::AutoDiffScalar AScalar; + using AScalar = Eigen::AutoDiffScalar; AScalar point_adiff[3]; VecX camera_adiff; diff --git a/opensfm/src/geometry/test/camera_test.cc b/opensfm/src/geometry/test/camera_test.cc index 81d8a0032..666a31049 100644 --- a/opensfm/src/geometry/test/camera_test.cc +++ b/opensfm/src/geometry/test/camera_test.cc @@ -118,18 +118,15 @@ TEST_F(CameraFixture, Fisheye624IsConsistent) { TEST_F(CameraFixture, Fisheye624MatchesReference) { Eigen::Matrix points; - points << - 10.0, 0.0, 0.0, - 0.0, 10.0, 0.0, - 1e-6, 1e-6, 10.0; + points << 10.0, 0.0, 0.0, // clang-format off + 0.0, 10.0, 0.0, + 1e-6, 1e-6, 10.0; // clang-format on // This test case is constructed to result in round numbers apart from the // thin prism distortions. Eigen::Matrix reference_pixels; - reference_pixels << - 149.42887062, 48.67088846, - 99.4288738, 98.67088528, - 100.0, 50.0; + reference_pixels << 149.42887062, 48.67088846, 99.4288738, 98.67088528, 100.0, + 50.0; constexpr int width = 200, height = 100; constexpr double f = width / (2 * M_PI); @@ -140,36 +137,38 @@ TEST_F(CameraFixture, Fisheye624MatchesReference) { const MatX2d reference_projected = camera.ProjectMany(points); ASSERT_TRUE(reference_pixels.isApprox(reference_projected, 1e-5)) - << " where reference_pixels = \n" << reference_pixels << '\n' - << " and reference_projected = \n" << reference_projected << '\n'; + << " where reference_pixels = \n" + << reference_pixels << '\n' + << " and reference_projected = \n" + << reference_projected << '\n'; } TEST_F(CameraFixture, Fisheye624WithTanMatchesReference) { Eigen::Matrix points; - points << - 10.0, 0.0, 0.0, - 0.0, 10.0, 0.0, - 1e-6, 1e-6, 10.0; + points << 10.0, 0.0, 0.0, // clang-format off + 0.0, 10.0, 0.0, + 1e-6, 1e-6, 10.0; // clang-format on // This test case is constructed to result in round numbers apart from the // tangential and thin prism distortions. Eigen::Matrix reference_pixels; - reference_pixels << - 151.7850648, 51.02708265, - 100.2142719, 105.7394678, - 100.0, 50.0; - + reference_pixels << 151.7850648, 51.02708265, // clang-format off + 100.2142719, 105.7394678, + 100.0, 50.0; // clang-format on constexpr int width = 200, height = 100; constexpr double f = width / (2 * M_PI); Eigen::Matrix distortion_tan_and_thin_prism; - distortion_tan_and_thin_prism << 0, 0, 0, 0, 0, 0, 0.03, 0.01, 0.01, -0.007, -0.03, 0.0053; + distortion_tan_and_thin_prism << 0, 0, 0, 0, 0, 0, 0.03, 0.01, 0.01, -0.007, + -0.03, 0.0053; const geometry::Camera camera = geometry::Camera::CreateFisheye624Camera( f, 1.0, {width / 2, height / 2}, distortion_tan_and_thin_prism); const MatX2d reference_projected = camera.ProjectMany(points); ASSERT_TRUE(reference_pixels.isApprox(reference_projected, 1e-5)) - << " where reference_pixels = \n" << reference_pixels << '\n' - << " and reference_projected = \n" << reference_projected << '\n'; + << " where reference_pixels = \n" + << reference_pixels << '\n' + << " and reference_projected = \n" + << reference_projected << '\n'; } TEST_F(CameraFixture, DualIsConsistent) { @@ -549,7 +548,8 @@ TEST(Camera, TestPixelNormalizedCoordinatesConversion) { ASSERT_EQ(norm_coord_comp[1], (px_coord_def[1] - (height - 1) / 2.0) * inv_normalizer); const Vec2d px_coord_comp = cam.NormalizedToPixelCoordinates(norm_coord_comp); - ASSERT_EQ(px_coord_comp, px_coord_def); + ASSERT_FLOAT_EQ(px_coord_comp[0], px_coord_def[0]); + ASSERT_FLOAT_EQ(px_coord_comp[1], px_coord_def[1]); const Vec2d norm_coord_static = geometry::Camera::PixelToNormalizedCoordinates(px_coord_def, width, diff --git a/opensfm/src/geometry/test/covariance_test.cc b/opensfm/src/geometry/test/covariance_test.cc index 9a235bfac..95519b91b 100644 --- a/opensfm/src/geometry/test/covariance_test.cc +++ b/opensfm/src/geometry/test/covariance_test.cc @@ -8,8 +8,6 @@ #include #include -#include -#include #include "geometry/transformations_functions.h" @@ -24,23 +22,22 @@ class CovarianceFixture : public ::testing::Test { observation << 0.0, 0.0; } - void RunAutodiffEval(const geometry::Camera& camera, - const geometry::Pose& pose) { + void RunAutodiffEval(const geometry::Camera& cam, const geometry::Pose& p) { // Prepare Eigen's Autodiff structures for (int i = 0; i < 3; ++i) { point_adiff[i].derivatives() = VecXd::Unit(3, i); } VecX pose_adiff(6); - pose_adiff.segment<3>(3) = pose.TranslationCameraToWorld(); - pose_adiff.segment<3>(0) = pose.RotationCameraToWorldMin(); + pose_adiff.segment<3>(3) = p.TranslationCameraToWorld(); + pose_adiff.segment<3>(0) = p.RotationCameraToWorldMin(); for (int i = 0; i < 6; ++i) { pose_adiff(i).derivatives().resize(3); pose_adiff(i).derivatives().setZero(); } VecX camera_adiff(3); - const VecXd camera_params = camera.GetParametersValues(); + const VecXd camera_params = cam.GetParametersValues(); for (int i = 0; i < camera_params.size(); ++i) { camera_adiff(i).value() = camera_params(i); camera_adiff(i).derivatives().resize(3); @@ -52,7 +49,7 @@ class CovarianceFixture : public ::testing::Test { geometry::PoseFunctor::Forward(point_adiff, pose_adiff.data(), &transformed[0]); geometry::Dispatch( - camera.GetProjectionType(), transformed, camera_adiff.data(), + cam.GetProjectionType(), transformed, camera_adiff.data(), projection_expected); } @@ -70,7 +67,7 @@ class CovarianceFixture : public ::testing::Test { const double focal{1.0}; double point[3]; - typedef Eigen::AutoDiffScalar AScalar; + using AScalar = Eigen::AutoDiffScalar; AScalar point_adiff[3]; AScalar projection_expected[2]; Vec2d observation; diff --git a/opensfm/src/geometry/test/triangulation_test.cc b/opensfm/src/geometry/test/triangulation_test.cc new file mode 100644 index 000000000..50a0da8f9 --- /dev/null +++ b/opensfm/src/geometry/test/triangulation_test.cc @@ -0,0 +1,348 @@ +#include +#include +#include + +#include + +MatX3d generateNoisyBearings(const MatX3d& bearings, double maxNoise) { + MatX3d bearings_noisy = MatX3d::Zero(bearings.rows(), 3); + for (int i = 0; i < bearings.rows(); ++i) { + const Vec3d bearing = bearings.row(i); + const Vec3d noise = maxNoise * Vec3d::Random(); + bearings_noisy.row(i) = (bearing + noise).normalized(); + } + return bearings_noisy; +} + +std::vector generateRts(const MatX3d& centers) { + const int num_cameras = centers.rows(); + std::vector Rts(num_cameras); + for (int i = 0; i < num_cameras; ++i) { + const Vec3d center = centers.row(i); + Rts[i] << 1.0, 0.0, 0.0, -center(0), // clang-format off + 0.0, 1.0, 0.0, -center(1), + 0.0, 0.0, 1.0, -center(2); // clang-format on + } + return Rts; +} + +void generate_triangulation_data(const MatX3d& centers, MatX3d& bearings, + MatX3d& bearings_noisy, + std::vector& Rts, Vec3d& gt_point) { + const int num_cameras = centers.rows(); + + gt_point = Vec3d(0.0, 0.0, 1.0); + + bearings = MatX3d::Zero(num_cameras, 3); + for (int i = 0; i < num_cameras; ++i) { + const Vec3d center = centers.row(i); + bearings.row(i) = (gt_point - center).normalized(); + } + + bearings_noisy = generateNoisyBearings(bearings, 0.001); + + Rts = generateRts(centers); +} + +class TwoCamsFixture : public ::testing::Test { + public: + TwoCamsFixture() { + centers = MatX3d::Zero(2, 3); + centers << 0.0, 0.0, 0.0, 1.0, 0.0, 0.0; + + generate_triangulation_data(centers, bearings, bearings_noisy, Rts, + gt_point); + } + + Vec3d gt_point; + MatX3d centers; + MatX3d bearings; + MatX3d bearings_noisy; + std::vector Rts; + + const double threshold = 0.01; + const std::vector thresholds = {0.01, 0.01}; + const double min_angle = 2.0 * M_PI / 180.0; + const double min_depth = 1e-6; +}; + +class FiveCamsFixture : public ::testing::Test { + public: + FiveCamsFixture() { + const int num_cameras = 5; + centers = MatX3d::Zero(num_cameras, 3); + for (int i = 0; i < num_cameras; ++i) { + centers.row(i) << 0.5 * i / num_cameras, 0.1 * i / num_cameras, 0.0; + } + generate_triangulation_data(centers, bearings, bearings_noisy, Rts, + gt_point); + } + + Vec3d gt_point; + MatX3d centers; + MatX3d bearings; + MatX3d bearings_noisy; + std::vector Rts; + + const double threshold = 0.01; + const std::vector thresholds = {0.01, 0.01, 0.01, 0.01, 0.01}; + const double min_angle = 2.0 * M_PI / 180.0; + const double min_depth = 1e-6; +}; + +// This fixture is designed to test a specific failure case: +// When two cameras have the same center, but a third one has a +// different center. This would happen for example when two images +// are taken by the front and back cameras of a 360 camera and +// another image with a different camera or timestamp. As long as +// the bearings are consistent, we want triangulation to succeed +// in that case. +class ThreeCamsSameCenterFixture : public ::testing::Test { + public: + ThreeCamsSameCenterFixture() { + centers = MatX3d::Zero(3, 3); + centers << 0.0, 0.0, 0.0, // clang-format off + 0.0, 0.0, 0.0, + 1.0, 0.0, 0.0; // clang-format on + + generate_triangulation_data(centers, bearings, bearings_noisy, Rts, + gt_point); + } + + Vec3d gt_point; + MatX3d centers; + MatX3d bearings; + MatX3d bearings_noisy; + std::vector Rts; + + const double threshold = 0.01; + const std::vector thresholds = {0.01, 0.01, 0.01}; + const double min_angle = 2.0 * M_PI / 180.0; + const double min_depth = 1e-6; +}; + +// This fixture is designed to test a specific failure case: +// When two cameras have the same center, we want the triangulation +// to fail. However, if the bearings are different, the triangulation +// may find a solution by putting the point at the center of the +// cameras. This is a bad solution and we want the functions to +// return non-success in this case. +class TwoCamsSameCenterFixture : public ::testing::Test { + public: + TwoCamsSameCenterFixture() { + centers = MatX3d::Zero(2, 3); + centers << 1.0, 0.0, 0.0, // clang-format off + 1.0, 0.0, 0.0; // clang-format on + + bearings = MatX3d::Zero(2, 3); + bearings << 0.0, 0.0, 1.0, // clang-format off + 1.0, 0.0, 0.0; // clang-format on + + bearings_noisy = generateNoisyBearings(bearings, 0.001); + + Rts = generateRts(centers); + } + + MatX3d centers; + MatX3d bearings; + MatX3d bearings_noisy; + std::vector Rts; + + const double threshold = 0.01; + const std::vector thresholds = {0.01, 0.01}; + const double min_angle = 2.0 * M_PI / 180.0; + const double min_depth = 1e-6; +}; + +class TwoCamsManyPointsFixture : public ::testing::Test { + public: + TwoCamsManyPointsFixture() { + gt_points.emplace_back(0.0, 0.0, 1.0); + gt_points.emplace_back(1.0, 2.0, 3.0); + + rotation_1_2 = Eigen::AngleAxisd(0.1, Vec3d::UnitY()).toRotationMatrix(); + translation_1_2 << -1.0, 2.0, 0.2; + + bearings1 = MatX3d(gt_points.size(), 3); + bearings2 = MatX3d(gt_points.size(), 3); + for (int i = 0; i < gt_points.size(); ++i) { + const Vec3d& gt_point = gt_points[i]; + bearings1.row(i) = gt_point.normalized(); + bearings2.row(i) = + (rotation_1_2.transpose() * (gt_point - translation_1_2)) + .normalized(); + } + + bearings1_noisy = generateNoisyBearings(bearings1, 0.001); + bearings2_noisy = generateNoisyBearings(bearings2, 0.001); + } + + std::vector gt_points; + Mat3d rotation_1_2; + Vec3d translation_1_2; + MatX3d bearings1; + MatX3d bearings2; + MatX3d bearings1_noisy; + MatX3d bearings2_noisy; +}; + +TEST_F(TwoCamsFixture, TriangulateBearingsDLT) { + const auto [success, point] = geometry::TriangulateBearingsDLT( + Rts, bearings, threshold, min_angle, min_depth); + ASSERT_TRUE(success); + ASSERT_LT((point - gt_point).norm(), 1e-6); + + const auto [success_noisy, point_noisy] = geometry::TriangulateBearingsDLT( + Rts, bearings_noisy, threshold, min_angle, min_depth); + ASSERT_TRUE(success_noisy); + ASSERT_LT((point_noisy - gt_point).norm(), 0.01); +} + +TEST_F(FiveCamsFixture, TriangulateBearingsDLT) { + const auto [success, point] = geometry::TriangulateBearingsDLT( + Rts, bearings, threshold, min_angle, min_depth); + ASSERT_TRUE(success); + ASSERT_LT((point - gt_point).norm(), 1e-6); + + const auto [success_noisy, point_noisy] = geometry::TriangulateBearingsDLT( + Rts, bearings_noisy, threshold, min_angle, min_depth); + ASSERT_TRUE(success_noisy); + ASSERT_LT((point_noisy - gt_point).norm(), 0.01); +} + +TEST_F(ThreeCamsSameCenterFixture, TriangulateBearingsDLT) { + const auto [success, point] = geometry::TriangulateBearingsDLT( + Rts, bearings, threshold, min_angle, min_depth); + ASSERT_TRUE(success); + ASSERT_LT((point - gt_point).norm(), 1e-6); + + const auto [success_noisy, point_noisy] = geometry::TriangulateBearingsDLT( + Rts, bearings_noisy, threshold, min_angle, min_depth); + ASSERT_TRUE(success_noisy); + ASSERT_LT((point_noisy - gt_point).norm(), 0.01); +} + +TEST_F(TwoCamsSameCenterFixture, TriangulateBearingsDLT) { + const auto [success, point] = geometry::TriangulateBearingsDLT( + Rts, bearings, threshold, min_angle, min_depth); + ASSERT_FALSE(success); // Expect failure due to coincident camera centers + + const auto [success_noisy, point_noisy] = geometry::TriangulateBearingsDLT( + Rts, bearings_noisy, threshold, min_angle, min_depth); + ASSERT_FALSE( + success_noisy); // Expect failure due to coincident camera centers + + // Test that without the positive depth constraint, triangulation succeeds + // and returns the center of the cameras. + const double negative_min_depth = -1e-6; + const auto [success_no_depth_check, point_no_depth_check] = + geometry::TriangulateBearingsMidpoint(centers, bearings, thresholds, + min_angle, negative_min_depth); + ASSERT_TRUE(success_no_depth_check); + const Vec3d expected_point = centers.row(0); + ASSERT_LT((point_no_depth_check - expected_point).norm(), 1e-6); +} + +TEST_F(TwoCamsFixture, TriangulateBearingsMidpoint) { + const auto [success, point] = geometry::TriangulateBearingsMidpoint( + centers, bearings, thresholds, min_angle, min_depth); + ASSERT_TRUE(success); + ASSERT_LT((point - gt_point).norm(), 1e-6); + + const auto [success_noisy, point_noisy] = + geometry::TriangulateBearingsMidpoint(centers, bearings_noisy, thresholds, + min_angle, min_depth); + ASSERT_TRUE(success_noisy); + ASSERT_LT((point_noisy - gt_point).norm(), 0.01); +} + +TEST_F(FiveCamsFixture, TriangulateBearingsMidpoint) { + const auto [success, point] = geometry::TriangulateBearingsMidpoint( + centers, bearings, thresholds, min_angle, min_depth); + ASSERT_TRUE(success); + ASSERT_LT((point - gt_point).norm(), 1e-6); + + const auto [success_noisy, point_noisy] = + geometry::TriangulateBearingsMidpoint(centers, bearings_noisy, thresholds, + min_angle, min_depth); + ASSERT_TRUE(success_noisy); + ASSERT_LT((point_noisy - gt_point).norm(), 0.01); +} + +TEST_F(ThreeCamsSameCenterFixture, TriangulateBearingsMidpoint) { + const auto [success, point] = geometry::TriangulateBearingsMidpoint( + centers, bearings, thresholds, min_angle, min_depth); + ASSERT_TRUE(success); + ASSERT_LT((point - gt_point).norm(), 1e-6); + + const auto [success_noisy, point_noisy] = + geometry::TriangulateBearingsMidpoint(centers, bearings_noisy, thresholds, + min_angle, min_depth); + ASSERT_TRUE(success_noisy); + ASSERT_LT((point_noisy - gt_point).norm(), 0.01); +} + +TEST_F(TwoCamsSameCenterFixture, TriangulateBearingsMidpoint) { + const auto [success, point] = geometry::TriangulateBearingsMidpoint( + centers, bearings, thresholds, min_angle, min_depth); + ASSERT_FALSE(success); // Expect failure due to coincident camera centers + + const auto [success_noisy, point_noisy] = + geometry::TriangulateBearingsMidpoint(centers, bearings, thresholds, + min_angle, min_depth); + ASSERT_FALSE( + success_noisy); // Expect failure due to coincident camera centers + + // Test that without the positive depth constraint, triangulation succeeds + // and returns the center of the cameras. + const double negative_min_depth = -1e-6; + const auto [success_no_depth_check, point_no_depth_check] = + geometry::TriangulateBearingsMidpoint(centers, bearings, thresholds, + min_angle, negative_min_depth); + ASSERT_TRUE(success_no_depth_check); + const Vec3d expected_point = centers.row(0); + ASSERT_LT((point_no_depth_check - expected_point).norm(), 1e-6); +} + +TEST_F(TwoCamsManyPointsFixture, TriangulateTwoBearingsMidpointMany) { + const auto results = geometry::TriangulateTwoBearingsMidpointMany( + bearings1, bearings2, rotation_1_2, translation_1_2); + + for (int i = 0; i < gt_points.size(); ++i) { + const auto [success, point] = results[i]; + ASSERT_TRUE(success); + ASSERT_LT((point - gt_points[i]).norm(), 1e-6); + } + + const auto results_noisy = geometry::TriangulateTwoBearingsMidpointMany( + bearings1_noisy, bearings2_noisy, rotation_1_2, translation_1_2); + + for (int i = 0; i < gt_points.size(); ++i) { + const auto [success, point] = results_noisy[i]; + ASSERT_TRUE(success); + ASSERT_LT((point - gt_points[i]).norm(), 0.01); + } +} + +TEST_F(TwoCamsManyPointsFixture, EpipolarAngleTwoBearingsMany) { + MatXd angles = geometry::EpipolarAngleTwoBearingsMany( + bearings1, bearings2, rotation_1_2, translation_1_2); + ASSERT_EQ(angles.rows(), gt_points.size()); + ASSERT_EQ(angles.cols(), gt_points.size()); + for (int i = 0; i < gt_points.size(); ++i) { + for (int j = 0; j < gt_points.size(); ++j) { + if (i == j) { + ASSERT_LT(angles(i, j), 1e-6); + } else { + ASSERT_GT(angles(i, j), 1e-6); + } + } + } +} + +TEST_F(TwoCamsFixture, PointRefinement) { + const Vec3d initial_point = gt_point + Vec3d(0.1, 0.2, 0.3); + const Vec3d refined = + geometry::PointRefinement(centers, bearings, initial_point, 10); + ASSERT_LT((refined - gt_point).norm(), 1e-6); +} diff --git a/opensfm/src/geometry/transformations_functions.h b/opensfm/src/geometry/transformations_functions.h index d2d2b8261..efb81530b 100644 --- a/opensfm/src/geometry/transformations_functions.h +++ b/opensfm/src/geometry/transformations_functions.h @@ -304,7 +304,7 @@ struct Normalize : Functor<3, 0, 3> { } }; -static Mat3d VectorToRotationMatrix(const Vec3d& r) { +inline Mat3d VectorToRotationMatrix(const Vec3d& r) { const auto n = r.norm(); if (n == 0) // avoid division by 0 { @@ -313,7 +313,7 @@ static Mat3d VectorToRotationMatrix(const Vec3d& r) { return Eigen::AngleAxisd(n, r / n).toRotationMatrix(); } } -static Vec3d RotationMatrixToVector(const Mat3d& R) { +inline Vec3d RotationMatrixToVector(const Mat3d& R) { Eigen::AngleAxisd tmp(R); return tmp.axis() * tmp.angle(); } diff --git a/opensfm/src/geometry/triangulation.h b/opensfm/src/geometry/triangulation.h index 325885157..70447f7ad 100644 --- a/opensfm/src/geometry/triangulation.h +++ b/opensfm/src/geometry/triangulation.h @@ -6,15 +6,49 @@ #include #include #include -#include -#include -#include +#include -double AngleBetweenVectors(const Eigen::Vector3d &u, const Eigen::Vector3d &v); +namespace geometry { + +double AngleBetweenVectors(const Vec3d& u, const Vec3d& v); + +std::pair TriangulateBearingsDLT(const std::vector& Rts, + const MatX3d& bearings, + double threshold, + double min_angle, + double min_depth); + +Vec4d TriangulateBearingsDLTSolve(const MatX3d& bs, + const std::vector& Rts); + +std::pair TriangulateBearingsMidpoint( + const MatX3d& centers, const MatX3d& bearings, + const std::vector& threshold_list, double min_angle, + double min_depth); -Eigen::Vector4d TriangulateBearingsDLTSolve( - const Eigen::Matrix &bs, - const std::vector> &Rts); +template +Eigen::Matrix TriangulateBearingsMidpointSolve( + const Eigen::Matrix& centers, + const Eigen::Matrix& bearings); + +std::vector> TriangulateTwoBearingsMidpointMany( + const MatX3d& bearings1, const MatX3d& bearings2, const Mat3d& rotation, + const Vec3d& translation); + +template +std::pair> TriangulateTwoBearingsMidpointSolve( + const Eigen::Matrix& centers, + const Eigen::Matrix& bearings); + +MatXd EpipolarAngleTwoBearingsMany(const MatX3d& bearings1, + const MatX3d& bearings2, + const Mat3d& rotation, + const Vec3d& translation); + +Vec3d PointRefinement(const MatX3d& centers, const MatX3d& bearings, + const Vec3d& point, int iterations); + +// Template implementations // Point minimizing the squared distance to all rays // Closed for solution from @@ -23,8 +57,8 @@ Eigen::Vector4d TriangulateBearingsDLTSolve( // CVIU 2006 template Eigen::Matrix TriangulateBearingsMidpointSolve( - const Eigen::Matrix ¢ers, - const Eigen::Matrix &bearings) { + const Eigen::Matrix& centers, + const Eigen::Matrix& bearings) { int nviews = bearings.rows(); assert(nviews == centers.rows()); assert(nviews >= 2); @@ -47,17 +81,10 @@ Eigen::Matrix TriangulateBearingsMidpointSolve( Cinv * BBtA; } -namespace geometry { - -std::pair TriangulateBearingsDLT( - const std::vector> &Rts, - const Eigen::Matrix &bearings, double threshold, - double min_angle); - template std::pair> TriangulateTwoBearingsMidpointSolve( - const Eigen::Matrix ¢ers, - const Eigen::Matrix &bearings) { + const Eigen::Matrix& centers, + const Eigen::Matrix& bearings) { const auto translation = centers.row(1) - centers.row(0); Eigen::Matrix b; b[0] = translation.dot(bearings.row(0)); @@ -68,40 +95,16 @@ std::pair> TriangulateTwoBearingsMidpointSolve( A(0, 1) = -A(1, 0); A(1, 1) = -bearings.row(1).dot(bearings.row(1)); - const T eps = T(1e-30); + const T eps = T(1e-10); const T det = A.determinant(); -#ifdef __aarch64__ - if (std::abs(det) < eps) { -#else - if (abs(det) < eps) { -#endif + + if (-eps < det && det < eps) { return std::make_pair(false, Eigen::Matrix()); } const auto lambdas = A.inverse() * b; const auto x1 = centers.row(0) + lambdas[0] * bearings.row(0); const auto x2 = centers.row(1) + lambdas[1] * bearings.row(1); return std::make_pair(true, (x1 + x2) / T(2.0)); -}; - -std::vector> -TriangulateTwoBearingsMidpointMany( - const Eigen::Matrix &bearings1, - const Eigen::Matrix &bearings2, - const Eigen::Matrix3d &rotation, const Eigen::Vector3d &translation); - -std::pair TriangulateBearingsMidpoint( - const Eigen::Matrix ¢ers, - const Eigen::Matrix &bearings, - const std::vector &threshold_list, - double min_angle, double max_angle); - -Eigen::Matrix -EpipolarAngleTwoBearingsMany( - const Eigen::Matrix &bearings1, - const Eigen::Matrix &bearings2, - const Eigen::Matrix3f &rotation, const Eigen::Vector3f &translation); - -Vec3d PointRefinement(const MatX3d ¢ers, const MatX3d &bearings, - const Vec3d &point, int iterations); +} } // namespace geometry diff --git a/opensfm/src/map/CMakeLists.txt b/opensfm/src/map/CMakeLists.txt index 74eb67420..58f931fb2 100644 --- a/opensfm/src/map/CMakeLists.txt +++ b/opensfm/src/map/CMakeLists.txt @@ -62,3 +62,4 @@ endif() set_target_properties(pymap PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${opensfm_SOURCE_DIR}/.." ) +install(TARGETS pymap LIBRARY DESTINATION .) diff --git a/opensfm/src/map/ground_control_points.h b/opensfm/src/map/ground_control_points.h index c22c1f82c..e8e41bf18 100644 --- a/opensfm/src/map/ground_control_points.h +++ b/opensfm/src/map/ground_control_points.h @@ -3,6 +3,17 @@ #include #include namespace map { +enum GroundControlPointRole { + /* A ground control point role in SfM cluster merge and bundle adjuster + + Attributes: + OPTIMIZATION: used in the optimization of map chunks and logging + metrics METRICS_ONLY: only used in logging metrics + + **/ + OPTIMIZATION = 0, + METRICS_ONLY = 1 +}; struct GroundControlPointObservation { /* A ground control point observation. @@ -14,7 +25,7 @@ struct GroundControlPointObservation { GroundControlPointObservation() = default; GroundControlPointObservation(const ShotId& shot_id, const Vec2d& proj) : shot_id_(shot_id), projection_(proj) {} - ShotId shot_id_ = ""; + ShotId shot_id_; Vec2d projection_ = Vec2d::Zero(); LandmarkUniqueId uid_ = 0; }; @@ -25,21 +36,25 @@ struct GroundControlPoint { lla: latitude, longitude and altitude has_altitude: true if z coordinate is known observations: list of observations of the point on images - id: a unique id for this point group (survey point + image observations) - survey_point_id: a unique id for the point on the ground + id: a unique id for this point group (survey point + image + observations) survey_point_id: a unique id for the point on the ground + role: OPTIMIZATION if gcp is used in SfM optimization, METRICS_ONLY if + the ground control point is used for computing map merging metrics */ GroundControlPoint() = default; - LandmarkId id_ = ""; + LandmarkId id_; LandmarkUniqueId survey_point_id_ = 0; bool has_altitude_ = false; AlignedVector observations_; std::map lla_; + Vec3d coordinates_ = Vec3d::Zero(); + GroundControlPointRole role_; Vec3d GetLlaVec3d() const { return { - lla_.at("latitude"), - lla_.at("longitude"), - has_altitude_ ? lla_.at("altitude") : 0.0, + lla_.at("latitude"), + lla_.at("longitude"), + has_altitude_ ? lla_.at("altitude") : 0.0, }; } diff --git a/opensfm/src/map/landmark.h b/opensfm/src/map/landmark.h index 0df854431..506db4bd1 100644 --- a/opensfm/src/map/landmark.h +++ b/opensfm/src/map/landmark.h @@ -8,6 +8,7 @@ #include namespace map { class Shot; +struct Observation; class Landmark { public: @@ -21,11 +22,12 @@ class Landmark { void SetColor(const Vec3i& color) { color_ = color; } // Utility functions - void AddObservation(Shot* shot, const FeatureId& feat_id); + void AddObservation(Shot* shot, const Observation* observation); void RemoveObservation(Shot* shot); size_t NumberOfObservations() const; FeatureId GetObservationIdInShot(Shot* shot) const; - const std::map& GetObservations() const; + const Observation& GetObservationInShot(Shot* shot) const; + const std::map& GetObservations() const; void ClearObservations() { observations_.clear(); } // Comparisons @@ -47,7 +49,7 @@ class Landmark { private: Vec3d global_pos_; // point in global - std::map observations_; + std::map observations_; Vec3i color_; std::map reproj_errors_; }; diff --git a/opensfm/src/map/observation.h b/opensfm/src/map/observation.h index c9a48fe9f..ac3c6e7a9 100644 --- a/opensfm/src/map/observation.h +++ b/opensfm/src/map/observation.h @@ -3,19 +3,33 @@ #include #include +#include namespace map { + +struct Depth { + float value; + bool is_radial; + float std_deviation; + + Depth() = default; + Depth(float value_, bool is_radial_, float std_deviation_) + : value(value_), is_radial(is_radial_), std_deviation(std_deviation_) {} +}; + struct Observation { Observation() = default; - Observation(double x, double y, double s, int r, int g, int b, int feature, + Observation(double x, double y, float s, int r, int g, int b, int feature, int segmentation = NO_SEMANTIC_VALUE, - int instance = NO_SEMANTIC_VALUE) + int instance = NO_SEMANTIC_VALUE, + const std::optional& depth = std::nullopt) : point(x, y), scale(s), color(r, g, b), feature_id(feature), segmentation_id(segmentation), - instance_id(instance) {} + instance_id(instance), + depth_prior(depth) {} bool operator==(const Observation& k) const { return point == k.point && scale == k.scale && color == k.color && feature_id == k.feature_id && segmentation_id == k.segmentation_id && @@ -24,13 +38,16 @@ struct Observation { // Mandatory data Eigen::Vector2d point; - double scale{1.}; + float scale{1.}; Eigen::Vector3i color; int feature_id{0}; // Optional data : semantics int segmentation_id; int instance_id; + + // Optional data : depth prior + std::optional depth_prior; static constexpr int NO_SEMANTIC_VALUE = -1; }; } // namespace map diff --git a/opensfm/src/map/pybind_utils.h b/opensfm/src/map/pybind_utils.h index 550ab462d..3667baa05 100644 --- a/opensfm/src/map/pybind_utils.h +++ b/opensfm/src/map/pybind_utils.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -12,23 +13,20 @@ #define PYBIND11_NAMESPACE_END_ NAMESPACE_END #endif - PYBIND11_NAMESPACE_BEGIN_(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN_(detail) // See https://github.com/pybind/pybind11/issues/637 -// Also fbsource/fbcode/caffe2/torch/csrc/jit/python/pybind.h using ListCasterBase = - pybind11::detail::list_caster, - map::Landmark *>; + pybind11::detail::list_caster, map::Landmark*>; template <> -struct type_caster> : ListCasterBase { - static handle cast(const std::vector &src, +struct type_caster> : ListCasterBase { + static handle cast(const std::vector& src, return_value_policy, handle parent) { return ListCasterBase::cast(src, return_value_policy::reference, parent); } - static handle cast(const std::vector *src, + static handle cast(const std::vector* src, return_value_policy pol, handle parent) { return cast(*src, pol, parent); } @@ -52,64 +50,33 @@ struct sfm_iterator_state { }; PYBIND11_NAMESPACE_END_(detail) -/// Makes an python iterator over the keys (`.first`) of a iterator over pairs -/// from a first and past-the-end InputIterator. -template ()).second), - typename... Extra> -iterator make_value_iterator(Iterator first, Sentinel last, Extra &&... extra) { - typedef detail::sfm_iterator_state - state; - - if (!detail::get_type_info(typeid(state), false)) { - class_(handle(), "iterator", pybind11::module_local()) - .def("__iter__", [](state &s) -> state & { return s; }) - .def("__next__", - [](state &s) -> KeyType { - if (!s.first_or_done) - ++s.it; - else - s.first_or_done = false; - if (s.it == s.end) { - s.first_or_done = true; - throw stop_iteration(); - } - return (*s.it).second; - }, - std::forward(extra)..., Policy); - } - - return cast(state{first, last, true}); -} - template ()).second)), typename... Extra> iterator make_ref_value_iterator(Iterator first, Sentinel last, - Extra &&... extra) { - typedef detail::sfm_iterator_state - state; + Extra&&... extra) { + using state = detail::sfm_iterator_state; if (!detail::get_type_info(typeid(state), false)) { class_(handle(), "ref_value_iterator", pybind11::module_local()) - .def("__iter__", [](state &s) -> state & { return s; }) - .def("__next__", - [](state &s) -> KeyType { - if (!s.first_or_done) - ++s.it; - else - s.first_or_done = false; - if (s.it == s.end) { - s.first_or_done = true; - throw stop_iteration(); - } - return &(s.it->second); - }, - std::forward(extra)..., Policy); + .def("__iter__", [](state& s) -> state& { return s; }) + .def( + "__next__", + [](state& s) -> KeyType { + if (!s.first_or_done) { + ++s.it; + } else { + s.first_or_done = false; + } + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return &(s.it->second); + }, + std::forward(extra)..., Policy); } return cast(state{first, last, true}); @@ -121,86 +88,31 @@ template < typename KeyType = pybind11::tuple, // decltype(&((*std::declval()).second)), typename... Extra> -iterator make_ref_iterator(Iterator first, Sentinel last, Extra &&... extra) { - typedef detail::sfm_iterator_state - state; +iterator make_ref_iterator(Iterator first, Sentinel last, Extra&&... extra) { + using state = detail::sfm_iterator_state; if (!detail::get_type_info(typeid(state), false)) { class_(handle(), "ref_iterator", pybind11::module_local()) - .def("__iter__", [](state &s) -> state & { return s; }) - .def("__next__", - [](state &s) -> KeyType { - if (!s.first_or_done) - ++s.it; - else - s.first_or_done = false; - if (s.it == s.end) { - s.first_or_done = true; - throw stop_iteration(); - } - return pybind11::make_tuple(s.it->first, &(s.it->second)); - }, - std::forward(extra)..., Policy); - } - - return cast(state{first, last, true}); -} - -/// Makes a python iterator from a first and past-the-end C++ InputIterator. -template ()), - typename... Extra> -iterator make_ptr_iterator(Iterator first, Sentinel last, Extra &&... extra) { - typedef detail::iterator_state, Policy, Iterator, Sentinel, ValueType> state; - - if (!detail::get_type_info(typeid(state), false)) { - class_(handle(), "iterator", pybind11::module_local()) - .def("__iter__", [](state &s) -> state & { return s; }) - .def("__next__", - [](state &s) -> ValueType { - if (!s.first_or_done) - ++s.it; - else - s.first_or_done = false; - if (s.it == s.end) { - s.first_or_done = true; - throw stop_iteration(); - } - return s.it; - }, - std::forward(extra)..., Policy); + .def("__iter__", [](state& s) -> state& { return s; }) + .def( + "__next__", + [](state& s) -> KeyType { + if (!s.first_or_done) { + ++s.it; + } else { + s.first_or_done = false; + } + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return pybind11::make_tuple(s.it->first, &(s.it->second)); + }, + std::forward(extra)..., Policy); } return cast(state{first, last, true}); } -/// Makes an iterator over the keys (`.first`) of a stl map-like container -/// supporting `std::begin()`/`std::end()` -template -iterator make_value_iterator(Type &value, Extra &&... extra) { - return make_value_iterator(std::begin(value), std::end(value), - extra...); -} -template -iterator make_unique_ptr_value_iterator(Type &value, Extra &&... extra) { - return make_unique_ptr_value_iterator(std::begin(value), - std::end(value), extra...); -} -template -iterator make_unique_ptr_iterator(Type &value, Extra &&... extra) { - return make_unique_ptr_iterator(std::begin(value), std::end(value), - extra...); -} - -template -iterator make_ref_value_iterator(Type &value, Extra &&... extra) { - return make_ref_value_iterator(std::begin(value), std::end(value), - extra...); -} PYBIND11_NAMESPACE_END_(PYBIND11_NAMESPACE) diff --git a/opensfm/src/map/pymap.pyi b/opensfm/src/map/pymap.pyi index 2c2305924..f5f09dcf1 100644 --- a/opensfm/src/map/pymap.pyi +++ b/opensfm/src/map/pymap.pyi @@ -7,41 +7,47 @@ # Tip: Be sure to run this with the build mode you use for your project, e.g., # @//arvr/mode/linux/opt (or dev) in arvr. # -# Ignore errors for [5] global variable types and [24] untyped generics. -# pyre-ignore-all-errors[5,24] +# Ignore errors for [24] untyped generics. +# pyre-ignore-all-errors[24] import numpy import opensfm.pygeo import opensfm.pygeometry from typing import * -__all__ = [ -"BiasView", -"CameraView", -"ErrorType", -"GroundControlPoint", -"GroundControlPointObservation", -"Landmark", -"LandmarkView", -"Map", -"Observation", -"PanoShotView", -"RigCamera", -"RigCameraView", -"RigInstance", -"RigInstanceView", -"Shot", -"ShotMeasurementDouble", -"ShotMeasurementInt", -"ShotMeasurementString", -"ShotMeasurementVec3d", -"ShotMeasurements", -"ShotMesh", -"ShotView", -"TracksManager", -"Angular", -"Normalized", -"Pixel" + +__all__ = [ + "BiasView", + "CameraView", + "Depth", + "ErrorType", + "GroundControlPoint", + "GroundControlPointObservation", + "GroundControlPointRole", + "Landmark", + "LandmarkView", + "Map", + "Observation", + "PanoShotView", + "RigCamera", + "RigCameraView", + "RigInstance", + "RigInstanceView", + "Shot", + "ShotMeasurementDouble", + "ShotMeasurementInt", + "ShotMeasurementString", + "ShotMeasurementVec3d", + "ShotMeasurements", + "ShotMesh", + "ShotView", + "TracksManager", + "Angular", + "METRICS_ONLY", + "Normalized", + "OPTIMIZATION", + "Pixel", ] + class BiasView: def __contains__(self, arg0: str) -> bool: ... def __getitem__(self, arg0: str) -> opensfm.pygeometry.Similarity: ... @@ -52,6 +58,7 @@ class BiasView: def items(self) -> Iterator: ... def keys(self) -> Iterator: ... def values(self) -> Iterator: ... + class CameraView: def __contains__(self, arg0: str) -> bool: ... def __getitem__(self, arg0: str) -> opensfm.pygeometry.Camera: ... @@ -62,75 +69,126 @@ class CameraView: def items(self) -> Iterator: ... def keys(self) -> Iterator: ... def values(self) -> Iterator: ... + +class Depth: + def __init__(self, value: float, is_radial: bool, std_deviation: float) -> None: ... + @property + def is_radial(self) -> bool: ... + @is_radial.setter + def is_radial(self, arg0: bool) -> None: ... + @property + def std_deviation(self) -> float: ... + @std_deviation.setter + def std_deviation(self, arg0: float) -> None: ... + @property + def value(self) -> float: ... + @value.setter + def value(self, arg0: float) -> None: ... + class ErrorType: + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... Pixel: "ErrorType" Normalized: "ErrorType" Angular: "ErrorType" __members__: Dict[str, "ErrorType"] - @property - def name(self) -> str: ... + __entries: "dict" + class GroundControlPoint: def __init__(self) -> None: ... def add_observation(self, arg0: GroundControlPointObservation) -> None: ... @property - def has_altitude(self) -> bool:... + def has_altitude(self) -> bool: ... @has_altitude.setter - def has_altitude(self, arg0: bool) -> None:... + def has_altitude(self, arg0: bool) -> None: ... @property - def id(self) -> str:... + def id(self) -> str: ... @id.setter - def id(self, arg0: str) -> None:... + def id(self, arg0: str) -> None: ... @property - def lla(self) -> Dict[str, float]:... + def lla(self) -> Dict[str, float]: ... @lla.setter - def lla(self, arg0: Dict[str, float]) -> None:... + def lla(self, arg0: Dict[str, float]) -> None: ... @property - def lla_vec(self) -> numpy.ndarray:... + def lla_vec(self) -> numpy.ndarray: ... @lla_vec.setter - def lla_vec(self, arg1: float, arg2: float, arg3: float) -> None:... + def lla_vec(self, arg1: float, arg2: float, arg3: float) -> None: ... @property - def observations(self) -> List[GroundControlPointObservation]:... + def observations(self) -> List[GroundControlPointObservation]: ... @observations.setter - def observations(self, arg1: List[GroundControlPointObservation]) -> None:... + def observations(self, arg1: List[GroundControlPointObservation]) -> None: ... + @property + def role(self) -> GroundControlPointRole: ... + @role.setter + def role(self, arg0: GroundControlPointRole) -> None: ... @property - def survey_point_id(self) -> int:... + def survey_point_id(self) -> int: ... @survey_point_id.setter - def survey_point_id(self, arg0: int) -> None:... + def survey_point_id(self, arg0: int) -> None: ... + class GroundControlPointObservation: @overload def __init__(self) -> None: ... @overload def __init__(self, arg0: str, arg1: numpy.ndarray) -> None: ... @property - def projection(self) -> numpy.ndarray:... + def projection(self) -> numpy.ndarray: ... @projection.setter - def projection(self, arg0: numpy.ndarray) -> None:... + def projection(self, arg0: numpy.ndarray) -> None: ... @property - def shot_id(self) -> str:... + def shot_id(self) -> str: ... @shot_id.setter - def shot_id(self, arg0: str) -> None:... + def shot_id(self, arg0: str) -> None: ... @property - def uid(self) -> int:... + def uid(self) -> int: ... @uid.setter - def uid(self, arg0: int) -> None:... + def uid(self, arg0: int) -> None: ... + +class GroundControlPointRole: + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __init__(self, value: int) -> None: ... + def __int__(self) -> int: ... + def __repr__(self) -> str: ... + def __setstate__(self, state: int) -> None: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + OPTIMIZATION: "GroundControlPointRole" + METRICS_ONLY: "GroundControlPointRole" + __members__: Dict[str, "GroundControlPointRole"] + __entries: "dict" + class Landmark: def __init__(self, arg0: str, arg1: numpy.ndarray) -> None: ... def get_observations(self) -> Dict[Shot, int]: ... def number_of_observations(self) -> int: ... @property - def color(self) -> numpy.ndarray:... + def color(self) -> numpy.ndarray: ... @color.setter - def color(self, arg1: numpy.ndarray) -> None:... + def color(self, arg1: numpy.ndarray) -> None: ... @property - def coordinates(self) -> numpy.ndarray:... + def coordinates(self) -> numpy.ndarray: ... @coordinates.setter - def coordinates(self, arg1: numpy.ndarray) -> None:... + def coordinates(self, arg1: numpy.ndarray) -> None: ... @property - def id(self) -> str:... + def id(self) -> str: ... @property - def reprojection_errors(self) -> Dict[str, numpy.ndarray]:... + def reprojection_errors(self) -> Dict[str, numpy.ndarray]: ... @reprojection_errors.setter - def reprojection_errors(self, arg1: Dict[str, numpy.ndarray]) -> None:... + def reprojection_errors(self, arg1: Dict[str, numpy.ndarray]) -> None: ... + class LandmarkView: def __contains__(self, arg0: str) -> bool: ... def __getitem__(self, arg0: str) -> Landmark: ... @@ -141,24 +199,39 @@ class LandmarkView: def items(self) -> Iterator: ... def keys(self) -> Iterator: ... def values(self) -> Iterator: ... + class Map: def __init__(self) -> None: ... @overload - def add_observation(self, shot: Shot, landmark: Landmark, observation: Observation) -> None: ... + def add_observation( + self, shot: Shot, landmark: Landmark, observation: Observation + ) -> None: ... @overload - def add_observation(self, shot_Id: str, landmark_id: str, observation: Observation) -> None: ... + def add_observation( + self, shot_Id: str, landmark_id: str, observation: Observation + ) -> None: ... def clean_landmarks_below_min_observations(self, arg0: int) -> None: ... def clear_observations_and_landmarks(self) -> None: ... - def compute_reprojection_errors(self, arg0: TracksManager, arg1: ErrorType) -> Dict[str, Dict[str, numpy.ndarray]]: ... - def create_camera(self, camera: opensfm.pygeometry.Camera) -> opensfm.pygeometry.Camera: ... - def create_landmark(self, lm_id: str, global_position: numpy.ndarray) -> Landmark: ... - def create_pano_shot(self, arg0: str, arg1: str, arg2: str, arg3: str, arg4: opensfm.pygeometry.Pose) -> Shot: ... + def compute_reprojection_errors( + self, arg0: TracksManager, arg1: ErrorType + ) -> Dict[str, Dict[str, numpy.ndarray]]: ... + def create_camera( + self, camera: opensfm.pygeometry.Camera + ) -> opensfm.pygeometry.Camera: ... + def create_landmark( + self, lm_id: str, global_position: numpy.ndarray + ) -> Landmark: ... + def create_pano_shot( + self, arg0: str, arg1: str, arg2: str, arg3: str, arg4: opensfm.pygeometry.Pose + ) -> Shot: ... def create_rig_camera(self, arg0: RigCamera) -> RigCamera: ... def create_rig_instance(self, arg0: str) -> RigInstance: ... @overload def create_shot(self, arg0: str, arg1: str, arg2: str, arg3: str) -> Shot: ... @overload - def create_shot(self, arg0: str, arg1: str, arg2: str, arg3: str, arg4: opensfm.pygeometry.Pose) -> Shot: ... + def create_shot( + self, arg0: str, arg1: str, arg2: str, arg3: str, arg4: opensfm.pygeometry.Pose + ) -> Shot: ... @staticmethod def deep_copy(arg0: Map, arg1: bool) -> Map: ... def get_bias(self, arg0: str) -> opensfm.pygeometry.Similarity: ... @@ -174,7 +247,9 @@ class Map: def get_reference(self) -> opensfm.pygeo.TopocentricConverter: ... def get_shot(self, arg0: str) -> Shot: ... def get_shots(self) -> ShotView: ... - def get_valid_observations(self, arg0: TracksManager) -> Dict[str, Dict[str, Observation]]: ... + def get_valid_observations( + self, arg0: TracksManager + ) -> Dict[str, Dict[str, Observation]]: ... def has_landmark(self, arg0: str) -> bool: ... @overload def remove_landmark(self, arg0: Landmark) -> None: ... @@ -190,34 +265,51 @@ class Map: def update_pano_shot(self, arg0: Shot) -> Shot: ... def update_rig_instance(self, arg0: RigInstance) -> RigInstance: ... def update_shot(self, arg0: Shot) -> Shot: ... + class Observation: - def __init__(self, x: float, y: float, s: float, r: int, g: int, b: int, feature: int, segmentation: int = -1, instance: int = -1) -> None: ... + def __init__( + self, + x: float, + y: float, + s: float, + r: int, + g: int, + b: int, + feature: int, + segmentation: int = -1, + instance: int = -1, + ) -> None: ... def copy(self) -> Observation: ... @property - def color(self) -> numpy.ndarray:... + def color(self) -> numpy.ndarray: ... @color.setter - def color(self, arg0: numpy.ndarray) -> None:... + def color(self, arg0: numpy.ndarray) -> None: ... + @property + def depth_prior(self) -> Optional[Depth]: ... + @depth_prior.setter + def depth_prior(self, arg0: Optional[Depth]) -> None: ... @property - def id(self) -> int:... + def id(self) -> int: ... @id.setter - def id(self, arg0: int) -> None:... + def id(self, arg0: int) -> None: ... @property - def instance(self) -> int:... + def instance(self) -> int: ... @instance.setter - def instance(self, arg0: int) -> None:... + def instance(self, arg0: int) -> None: ... @property - def point(self) -> numpy.ndarray:... + def point(self) -> numpy.ndarray: ... @point.setter - def point(self, arg0: numpy.ndarray) -> None:... + def point(self, arg0: numpy.ndarray) -> None: ... @property - def scale(self) -> float:... + def scale(self) -> float: ... @scale.setter - def scale(self, arg0: float) -> None:... + def scale(self, arg0: float) -> None: ... @property - def segmentation(self) -> int:... + def segmentation(self) -> int: ... @segmentation.setter - def segmentation(self, arg0: int) -> None:... + def segmentation(self, arg0: int) -> None: ... NO_SEMANTIC_VALUE = -1 + class PanoShotView: def __contains__(self, arg0: str) -> bool: ... def __getitem__(self, arg0: str) -> Shot: ... @@ -228,6 +320,7 @@ class PanoShotView: def items(self) -> Iterator: ... def keys(self) -> Iterator: ... def values(self) -> Iterator: ... + class RigCamera: def __getstate__(self) -> tuple: ... @overload @@ -236,13 +329,14 @@ class RigCamera: def __init__(self, arg0: opensfm.pygeometry.Pose, arg1: str) -> None: ... def __setstate__(self, arg0: tuple) -> None: ... @property - def id(self) -> str:... + def id(self) -> str: ... @id.setter - def id(self, arg0: str) -> None:... + def id(self, arg0: str) -> None: ... @property - def pose(self) -> opensfm.pygeometry.Pose:... + def pose(self) -> opensfm.pygeometry.Pose: ... @pose.setter - def pose(self, arg0: opensfm.pygeometry.Pose) -> None:... + def pose(self, arg0: opensfm.pygeometry.Pose) -> None: ... + class RigCameraView: def __contains__(self, arg0: str) -> bool: ... def __getitem__(self, arg0: str) -> RigCamera: ... @@ -253,29 +347,35 @@ class RigCameraView: def items(self) -> Iterator: ... def keys(self) -> Iterator: ... def values(self) -> Iterator: ... + class RigInstance: def __init__(self, arg0: str) -> None: ... def add_shot(self, arg0: RigCamera, arg1: Shot) -> None: ... def keys(self) -> Set[str]: ... def remove_shot(self, arg0: str) -> None: ... - def update_instance_pose_with_shot(self, arg0: str, arg1: opensfm.pygeometry.Pose) -> None: ... - def update_rig_camera_pose(self, arg0: str, arg1: opensfm.pygeometry.Pose) -> None: ... + def update_instance_pose_with_shot( + self, arg0: str, arg1: opensfm.pygeometry.Pose + ) -> None: ... + def update_rig_camera_pose( + self, arg0: str, arg1: opensfm.pygeometry.Pose + ) -> None: ... @property - def camera_ids(self) -> Dict[str, str]:... + def camera_ids(self) -> Dict[str, str]: ... @property - def id(self) -> str:... + def id(self) -> str: ... @id.setter - def id(self, arg0: str) -> None:... + def id(self, arg0: str) -> None: ... @property - def pose(self) -> opensfm.pygeometry.Pose:... + def pose(self) -> opensfm.pygeometry.Pose: ... @pose.setter - def pose(self, arg1: opensfm.pygeometry.Pose) -> None:... + def pose(self, arg1: opensfm.pygeometry.Pose) -> None: ... @property - def rig_camera_ids(self) -> Dict[str, str]:... + def rig_camera_ids(self) -> Dict[str, str]: ... @property - def rig_cameras(self) -> Dict[str, RigCamera]:... + def rig_cameras(self) -> Dict[str, RigCamera]: ... @property - def shots(self) -> Dict[str, Shot]:... + def shots(self) -> Dict[str, Shot]: ... + class RigInstanceView: def __contains__(self, arg0: str) -> bool: ... def __getitem__(self, arg0: str) -> RigInstance: ... @@ -286,8 +386,11 @@ class RigInstanceView: def items(self) -> Iterator: ... def keys(self) -> Iterator: ... def values(self) -> Iterator: ... + class Shot: - def __init__(self, arg0: str, arg1: opensfm.pygeometry.Camera, arg2: opensfm.pygeometry.Pose) -> None: ... + def __init__( + self, arg0: str, arg1: opensfm.pygeometry.Camera, arg2: opensfm.pygeometry.Pose + ) -> None: ... def bearing(self, arg0: numpy.ndarray) -> numpy.ndarray: ... def bearing_many(self, arg0: numpy.ndarray) -> numpy.ndarray: ... def get_landmark_observation(self, arg0: Landmark) -> Observation: ... @@ -299,85 +402,90 @@ class Shot: def remove_observation(self, arg0: int) -> None: ... def set_rig(self, arg0: RigInstance, arg1: RigCamera) -> None: ... @property - def camera(self) -> opensfm.pygeometry.Camera:... + def camera(self) -> opensfm.pygeometry.Camera: ... @property - def covariance(self) -> numpy.ndarray:... + def covariance(self) -> numpy.ndarray: ... @covariance.setter - def covariance(self, arg1: numpy.ndarray) -> None:... + def covariance(self, arg1: numpy.ndarray) -> None: ... @property - def id(self) -> str:... + def id(self) -> str: ... @property - def merge_cc(self) -> int:... + def merge_cc(self) -> int: ... @merge_cc.setter - def merge_cc(self, arg0: int) -> None:... + def merge_cc(self, arg0: int) -> None: ... @property - def mesh(self) -> ShotMesh:... + def mesh(self) -> ShotMesh: ... @mesh.setter - def mesh(self, arg0: ShotMesh) -> None:... + def mesh(self, arg0: ShotMesh) -> None: ... @property - def metadata(self) -> ShotMeasurements:... + def metadata(self) -> ShotMeasurements: ... @metadata.setter - def metadata(self, arg1: ShotMeasurements) -> None:... + def metadata(self, arg1: ShotMeasurements) -> None: ... @property - def pose(self) -> opensfm.pygeometry.Pose:... + def pose(self) -> opensfm.pygeometry.Pose: ... @pose.setter - def pose(self, arg1: opensfm.pygeometry.Pose) -> None:... + def pose(self, arg1: opensfm.pygeometry.Pose) -> None: ... @property - def rig_camera(self) -> RigCamera:... + def rig_camera(self) -> RigCamera: ... @property - def rig_camera_id(self) -> str:... + def rig_camera_id(self) -> str: ... @property - def rig_instance(self) -> RigInstance:... + def rig_instance(self) -> RigInstance: ... @property - def rig_instance_id(self) -> str:... + def rig_instance_id(self) -> str: ... @property - def scale(self) -> float:... + def scale(self) -> float: ... @scale.setter - def scale(self, arg0: float) -> None:... + def scale(self, arg0: float) -> None: ... + class ShotMeasurementDouble: def __getstate__(self) -> tuple: ... def __init__(self) -> None: ... def __setstate__(self, arg0: tuple) -> None: ... def reset(self) -> None: ... @property - def has_value(self) -> bool:... + def has_value(self) -> bool: ... @property - def value(self) -> float:... + def value(self) -> float: ... @value.setter - def value(self, arg1: float) -> None:... + def value(self, arg1: float) -> None: ... + class ShotMeasurementInt: def __getstate__(self) -> tuple: ... def __init__(self) -> None: ... def __setstate__(self, arg0: tuple) -> None: ... def reset(self) -> None: ... @property - def has_value(self) -> bool:... + def has_value(self) -> bool: ... @property - def value(self) -> int:... + def value(self) -> int: ... @value.setter - def value(self, arg1: int) -> None:... + def value(self, arg1: int) -> None: ... + class ShotMeasurementString: def __getstate__(self) -> tuple: ... def __init__(self) -> None: ... def __setstate__(self, arg0: tuple) -> None: ... def reset(self) -> None: ... @property - def has_value(self) -> bool:... + def has_value(self) -> bool: ... @property - def value(self) -> str:... + def value(self) -> str: ... @value.setter - def value(self, arg1: str) -> None:... + def value(self, arg1: str) -> None: ... + class ShotMeasurementVec3d: def __getstate__(self) -> tuple: ... def __init__(self) -> None: ... def __setstate__(self, arg0: tuple) -> None: ... def reset(self) -> None: ... @property - def has_value(self) -> bool:... + def has_value(self) -> bool: ... @property - def value(self) -> numpy.ndarray:... + def value(self) -> numpy.ndarray: ... @value.setter - def value(self, arg1: numpy.ndarray) -> None:... + def value(self, arg1: numpy.ndarray) -> None: ... + class ShotMeasurements: def __copy__(self) -> ShotMeasurements: ... def __getstate__(self) -> tuple: ... @@ -385,58 +493,60 @@ class ShotMeasurements: def __setstate__(self, arg0: tuple) -> None: ... def set(self, arg0: ShotMeasurements) -> None: ... @property - def attributes(self) -> Dict[str, str]:... + def attributes(self) -> Dict[str, str]: ... @attributes.setter - def attributes(self, arg1: Dict[str, str]) -> None:... + def attributes(self, arg1: Dict[str, str]) -> None: ... @property - def capture_time(self) -> ShotMeasurementDouble:... + def capture_time(self) -> ShotMeasurementDouble: ... @capture_time.setter - def capture_time(self, arg0: ShotMeasurementDouble) -> None:... + def capture_time(self, arg0: ShotMeasurementDouble) -> None: ... @property - def compass_accuracy(self) -> ShotMeasurementDouble:... + def compass_accuracy(self) -> ShotMeasurementDouble: ... @compass_accuracy.setter - def compass_accuracy(self, arg0: ShotMeasurementDouble) -> None:... + def compass_accuracy(self, arg0: ShotMeasurementDouble) -> None: ... @property - def compass_angle(self) -> ShotMeasurementDouble:... + def compass_angle(self) -> ShotMeasurementDouble: ... @compass_angle.setter - def compass_angle(self, arg0: ShotMeasurementDouble) -> None:... + def compass_angle(self, arg0: ShotMeasurementDouble) -> None: ... @property - def gps_accuracy(self) -> ShotMeasurementDouble:... + def gps_accuracy(self) -> ShotMeasurementDouble: ... @gps_accuracy.setter - def gps_accuracy(self, arg0: ShotMeasurementDouble) -> None:... + def gps_accuracy(self, arg0: ShotMeasurementDouble) -> None: ... @property - def gps_position(self) -> ShotMeasurementVec3d:... + def gps_position(self) -> ShotMeasurementVec3d: ... @gps_position.setter - def gps_position(self, arg0: ShotMeasurementVec3d) -> None:... + def gps_position(self, arg0: ShotMeasurementVec3d) -> None: ... @property - def gravity_down(self) -> ShotMeasurementVec3d:... + def gravity_down(self) -> ShotMeasurementVec3d: ... @gravity_down.setter - def gravity_down(self, arg0: ShotMeasurementVec3d) -> None:... + def gravity_down(self, arg0: ShotMeasurementVec3d) -> None: ... @property - def opk_accuracy(self) -> ShotMeasurementDouble:... + def opk_accuracy(self) -> ShotMeasurementDouble: ... @opk_accuracy.setter - def opk_accuracy(self, arg0: ShotMeasurementDouble) -> None:... + def opk_accuracy(self, arg0: ShotMeasurementDouble) -> None: ... @property - def opk_angles(self) -> ShotMeasurementVec3d:... + def opk_angles(self) -> ShotMeasurementVec3d: ... @opk_angles.setter - def opk_angles(self, arg0: ShotMeasurementVec3d) -> None:... + def opk_angles(self, arg0: ShotMeasurementVec3d) -> None: ... @property - def orientation(self) -> ShotMeasurementInt:... + def orientation(self) -> ShotMeasurementInt: ... @orientation.setter - def orientation(self, arg0: ShotMeasurementInt) -> None:... + def orientation(self, arg0: ShotMeasurementInt) -> None: ... @property - def sequence_key(self) -> ShotMeasurementString:... + def sequence_key(self) -> ShotMeasurementString: ... @sequence_key.setter - def sequence_key(self, arg0: ShotMeasurementString) -> None:... + def sequence_key(self, arg0: ShotMeasurementString) -> None: ... + class ShotMesh: @property - def faces(self) -> numpy.ndarray:... + def faces(self) -> numpy.ndarray: ... @faces.setter - def faces(self, arg1: numpy.ndarray) -> None:... + def faces(self, arg1: numpy.ndarray) -> None: ... @property - def vertices(self) -> numpy.ndarray:... + def vertices(self) -> numpy.ndarray: ... @vertices.setter - def vertices(self, arg1: numpy.ndarray) -> None:... + def vertices(self, arg1: numpy.ndarray) -> None: ... + class ShotView: def __contains__(self, arg0: str) -> bool: ... def __getitem__(self, arg0: str) -> Shot: ... @@ -447,13 +557,20 @@ class ShotView: def items(self) -> Iterator: ... def keys(self) -> Iterator: ... def values(self) -> Iterator: ... + class TracksManager: def __init__(self) -> None: ... def add_observation(self, arg0: str, arg1: str, arg2: Observation) -> None: ... def as_string(self) -> str: ... - def construct_sub_tracks_manager(self, arg0: List[str], arg1: List[str]) -> TracksManager: ... - def get_all_common_observations(self, arg0: str, arg1: str) -> List[Tuple[str, Observation, Observation]]: ... - def get_all_pairs_connectivity(self, shots: List[str] = [], tracks: List[str] = []) -> Dict[Tuple[str, str], int]: ... + def construct_sub_tracks_manager( + self, arg0: List[str], arg1: List[str] + ) -> TracksManager: ... + def get_all_common_observations( + self, arg0: str, arg1: str + ) -> List[Tuple[str, Observation, Observation]]: ... + def get_all_pairs_connectivity( + self, shots: List[str] = [], tracks: List[str] = [] + ) -> Dict[Tuple[str, str], int]: ... def get_observation(self, arg0: str, arg1: str) -> Observation: ... def get_shot_ids(self) -> List[str]: ... def get_shot_observations(self, arg0: str) -> Dict[str, Observation]: ... @@ -469,6 +586,9 @@ class TracksManager: def num_tracks(self) -> int: ... def remove_observation(self, arg0: str, arg1: str) -> None: ... def write_to_file(self, arg0: str) -> None: ... -Angular = ... -Normalized = ... -Pixel = ... \ No newline at end of file + +Angular: "ErrorType" +METRICS_ONLY: "GroundControlPointRole" +Normalized: "ErrorType" +OPTIMIZATION: "GroundControlPointRole" +Pixel: "ErrorType" diff --git a/opensfm/src/map/python/pybind.cc b/opensfm/src/map/python/pybind.cc index b914cbc8e..57f470f8f 100644 --- a/opensfm/src/map/python/pybind.cc +++ b/opensfm/src/map/python/pybind.cc @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -19,7 +20,7 @@ namespace py = pybind11; template -void DeclareShotMeasurement(py::module &m, const std::string &type_name) { +void DeclareShotMeasurement(py::module& m, const std::string& type_name) { using SM = foundation::OptionalValue; std::string class_name = std::string("ShotMeasurement") + type_name; @@ -31,7 +32,7 @@ void DeclareShotMeasurement(py::module &m, const std::string &type_name) { &SM::SetValue) .def("reset", &SM::Reset) .def(py::pickle( - [](const SM &sm) { + [](const SM& sm) { return py::make_tuple(sm.HasValue(), sm.Value()); }, [](py::tuple p) { @@ -64,6 +65,13 @@ PYBIND11_MODULE(pymap, m) { .value("Angular", map::Map::Angular) .export_values(); + py::class_(m, "Depth") + .def(py::init(), py::arg("value"), + py::arg("is_radial"), py::arg("std_deviation")) + .def_readwrite("value", &map::Depth::value) + .def_readwrite("is_radial", &map::Depth::is_radial) + .def_readwrite("std_deviation", &map::Depth::std_deviation); + py::class_(m, "Observation") .def(py::init(), py::arg("x"), py::arg("y"), py::arg("s"), py::arg("r"), py::arg("g"), @@ -76,18 +84,19 @@ PYBIND11_MODULE(pymap, m) { .def_readwrite("color", &map::Observation::color) .def_readwrite("segmentation", &map::Observation::segmentation_id) .def_readwrite("instance", &map::Observation::instance_id) + .def_readwrite("depth_prior", &map::Observation::depth_prior) .def_readonly_static("NO_SEMANTIC_VALUE", &map::Observation::NO_SEMANTIC_VALUE) .def( "copy", - [](const map::Observation &to_copy) { + [](const map::Observation& to_copy) { map::Observation copy = to_copy; return copy; }, py::return_value_policy::copy); py::class_(m, "Landmark") - .def(py::init()) + .def(py::init()) .def_readonly("id", &map::Landmark::id_) .def_property("coordinates", &map::Landmark::GetGlobalPos, &map::Landmark::SetGlobalPos) @@ -116,7 +125,7 @@ PYBIND11_MODULE(pymap, m) { .def_property("attributes", &map::ShotMeasurements::GetAttributes, &map::ShotMeasurements::SetAttributes) .def(py::pickle( - [](const map::ShotMeasurements &s) { + [](const map::ShotMeasurements& s) { return py::make_tuple( s.gps_accuracy_, s.gps_position_, s.orientation_, s.capture_time_, s.gravity_down_, s.compass_angle_, @@ -140,7 +149,7 @@ PYBIND11_MODULE(pymap, m) { })) .def( "__copy__", - [](const map::ShotMeasurements &to_copy) { + [](const map::ShotMeasurements& to_copy) { map::ShotMeasurements copy; copy.Set(to_copy); return copy; @@ -156,12 +165,12 @@ PYBIND11_MODULE(pymap, m) { py::class_(m, "RigCamera") .def(py::init<>()) - .def(py::init()) + .def(py::init()) .def_readwrite("id", &map::RigCamera::id) .def_readwrite("pose", &map::RigCamera::pose) // pickle support .def(py::pickle( - [](const map::RigCamera &rc) { + [](const map::RigCamera& rc) { return py::make_tuple(rc.pose, rc.id); }, [](py::tuple s) { @@ -180,17 +189,17 @@ PYBIND11_MODULE(pymap, m) { py::return_value_policy::reference_internal) .def_property_readonly( "rig_camera_ids", - [](const map::RigInstance &ri) { + [](const map::RigInstance& ri) { std::map rig_camera_ids; - for (const auto &rig_camera : ri.GetRigCameras()) { + for (const auto& rig_camera : ri.GetRigCameras()) { rig_camera_ids[rig_camera.first] = rig_camera.second->id; } return rig_camera_ids; }) .def_property_readonly("camera_ids", - [](const map::RigInstance &ri) { + [](const map::RigInstance& ri) { std::map camera_ids; - for (const auto &shot : ri.GetShots()) { + for (const auto& shot : ri.GetShots()) { camera_ids[shot.first] = shot.second->GetCamera()->id; } @@ -207,8 +216,8 @@ PYBIND11_MODULE(pymap, m) { .def("update_rig_camera_pose", &map::RigInstance::UpdateRigCameraPose); shotCls - .def(py::init()) + .def(py::init()) .def_readonly("id", &map::Shot::id_) .def_readwrite("mesh", &map::Shot::mesh) .def_property("covariance", &map::Shot::GetCovariance, @@ -245,20 +254,28 @@ PYBIND11_MODULE(pymap, m) { py::class_( m, "GroundControlPointObservation") .def(py::init()) - .def(py::init()) + .def(py::init()) .def_readwrite("shot_id", &map::GroundControlPointObservation::shot_id_) .def_readwrite("uid", &map::GroundControlPointObservation::uid_) .def_readwrite("projection", &map::GroundControlPointObservation::projection_); + py::enum_(m, "GroundControlPointRole") + .value("OPTIMIZATION", map::GroundControlPointRole::OPTIMIZATION) + .value("METRICS_ONLY", map::GroundControlPointRole::METRICS_ONLY) + .export_values(); + py::class_(m, "GroundControlPoint") .def(py::init()) .def_readwrite("id", &map::GroundControlPoint::id_) - .def_readwrite("survey_point_id", &map::GroundControlPoint::survey_point_id_) + .def_readwrite("survey_point_id", + &map::GroundControlPoint::survey_point_id_) .def_readwrite("has_altitude", &map::GroundControlPoint::has_altitude_) .def_readwrite("lla", &map::GroundControlPoint::lla_) + .def_readwrite("coordinates", &map::GroundControlPoint::coordinates_) .def_property("lla_vec", &map::GroundControlPoint::GetLlaVec3d, &map::GroundControlPoint::SetLla) + .def_readwrite("role", &map::GroundControlPoint::role_) .def_property("observations", &map::GroundControlPoint::GetObservations, &map::GroundControlPoint::SetObservations) .def("add_observation", &map::GroundControlPoint::AddObservation); @@ -274,7 +291,6 @@ PYBIND11_MODULE(pymap, m) { .def_static("merge_tracks_manager", &map::TracksManager::MergeTracksManager) .def("add_observation", &map::TracksManager::AddObservation) - .def("remove_observation", &map::TracksManager::RemoveObservation) .def("num_shots", &map::TracksManager::NumShots) .def("num_tracks", &map::TracksManager::NumTracks) .def("get_shot_ids", &map::TracksManager::GetShotIds) @@ -289,6 +305,9 @@ PYBIND11_MODULE(pymap, m) { .def("get_all_common_observations", &map::TracksManager::GetAllCommonObservations, py::call_guard()) + .def("get_all_common_observations_arrays", + &map::TracksManager::GetAllCommonObservationsArrays, + py::call_guard()) .def("get_all_pairs_connectivity", &map::TracksManager::GetAllPairsConnectivity, py::arg("shots") = std::vector(), @@ -296,34 +315,34 @@ PYBIND11_MODULE(pymap, m) { py::call_guard()); py::class_(m, "PanoShotView") - .def(py::init(), + .def(py::init(), py::keep_alive<1, 2>()) // Keep map alive while view is used .def("__len__", &map::PanoShotView::NumberOfShots) .def( "items", - [](const map::PanoShotView &sv) { - auto &shots = sv.GetShots(); + [](const map::PanoShotView& sv) { + auto& shots = sv.GetShots(); return py::make_ref_iterator(shots.begin(), shots.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "values", - [](const map::PanoShotView &sv) { - auto &shots = sv.GetShots(); + [](const map::PanoShotView& sv) { + auto& shots = sv.GetShots(); return py::make_ref_value_iterator(shots.begin(), shots.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "__iter__", - [](const map::PanoShotView &sv) { - const auto &shots = sv.GetShots(); + [](const map::PanoShotView& sv) { + const auto& shots = sv.GetShots(); return py::make_key_iterator(shots.begin(), shots.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "keys", - [](const map::PanoShotView &sv) { - const auto &shots = sv.GetShots(); + [](const map::PanoShotView& sv) { + const auto& shots = sv.GetShots(); return py::make_key_iterator(shots.begin(), shots.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used @@ -334,34 +353,34 @@ PYBIND11_MODULE(pymap, m) { .def("__contains__", &map::PanoShotView::HasShot); py::class_(m, "ShotView") - .def(py::init(), + .def(py::init(), py::keep_alive<1, 2>()) // Keep map alive while view is used .def("__len__", &map::ShotView::NumberOfShots) .def( "items", - [](const map::ShotView &sv) { - const auto &shots = sv.GetShots(); + [](const map::ShotView& sv) { + const auto& shots = sv.GetShots(); return py::make_ref_iterator(shots.begin(), shots.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "values", - [](const map::ShotView &sv) { - const auto &shots = sv.GetShots(); + [](const map::ShotView& sv) { + const auto& shots = sv.GetShots(); return py::make_ref_value_iterator(shots.begin(), shots.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "__iter__", - [](const map::ShotView &sv) { - const auto &shots = sv.GetShots(); + [](const map::ShotView& sv) { + const auto& shots = sv.GetShots(); return py::make_key_iterator(shots.begin(), shots.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "keys", - [](const map::ShotView &sv) { - const auto &shots = sv.GetShots(); + [](const map::ShotView& sv) { + const auto& shots = sv.GetShots(); return py::make_key_iterator(shots.begin(), shots.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used @@ -372,34 +391,34 @@ PYBIND11_MODULE(pymap, m) { .def("__contains__", &map::ShotView::HasShot); py::class_(m, "LandmarkView") - .def(py::init(), + .def(py::init(), py::keep_alive<1, 2>()) // Keep map alive while view is used .def("__len__", &map::LandmarkView::NumberOfLandmarks) .def( "items", - [](const map::LandmarkView &sv) { - auto &lms = sv.GetLandmarks(); + [](const map::LandmarkView& sv) { + auto& lms = sv.GetLandmarks(); return py::make_ref_iterator(lms.begin(), lms.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "values", - [](const map::LandmarkView &sv) { - auto &lms = sv.GetLandmarks(); + [](const map::LandmarkView& sv) { + auto& lms = sv.GetLandmarks(); return py::make_ref_value_iterator(lms.begin(), lms.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "__iter__", - [](const map::LandmarkView &sv) { - const auto &lms = sv.GetLandmarks(); + [](const map::LandmarkView& sv) { + const auto& lms = sv.GetLandmarks(); return py::make_key_iterator(lms.begin(), lms.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "keys", - [](const map::LandmarkView &sv) { - const auto &lms = sv.GetLandmarks(); + [](const map::LandmarkView& sv) { + const auto& lms = sv.GetLandmarks(); return py::make_key_iterator(lms.begin(), lms.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used @@ -410,34 +429,34 @@ PYBIND11_MODULE(pymap, m) { .def("__contains__", &map::LandmarkView::HasLandmark); py::class_(m, "CameraView") - .def(py::init(), + .def(py::init(), py::keep_alive<1, 2>()) // Keep map alive while view is used .def("__len__", &map::CameraView::NumberOfCameras) .def( "items", - [](const map::CameraView &sv) { - const auto &cams = sv.GetCameras(); + [](const map::CameraView& sv) { + const auto& cams = sv.GetCameras(); return py::make_iterator(cams.begin(), cams.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "values", - [](map::CameraView &sv) { - auto &cams = sv.GetCameras(); + [](map::CameraView& sv) { + auto& cams = sv.GetCameras(); return py::make_ref_value_iterator(cams.begin(), cams.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "__iter__", - [](const map::CameraView &sv) { - const auto &cams = sv.GetCameras(); + [](const map::CameraView& sv) { + const auto& cams = sv.GetCameras(); return py::make_key_iterator(cams.begin(), cams.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "keys", - [](const map::CameraView &sv) { - const auto &cams = sv.GetCameras(); + [](const map::CameraView& sv) { + const auto& cams = sv.GetCameras(); return py::make_key_iterator(cams.begin(), cams.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used @@ -448,34 +467,34 @@ PYBIND11_MODULE(pymap, m) { .def("__contains__", &map::CameraView::HasCamera); py::class_(m, "BiasView") - .def(py::init(), + .def(py::init(), py::keep_alive<1, 2>()) // Keep map alive while view is used .def("__len__", &map::BiasView::NumberOfBiases) .def( "items", - [](const map::BiasView &sv) { - const auto &biases = sv.GetBiases(); + [](const map::BiasView& sv) { + const auto& biases = sv.GetBiases(); return py::make_iterator(biases.begin(), biases.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "values", - [](map::BiasView &sv) { - auto &biases = sv.GetBiases(); + [](map::BiasView& sv) { + auto& biases = sv.GetBiases(); return py::make_ref_value_iterator(biases.begin(), biases.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "__iter__", - [](const map::BiasView &sv) { - const auto &biases = sv.GetBiases(); + [](const map::BiasView& sv) { + const auto& biases = sv.GetBiases(); return py::make_key_iterator(biases.begin(), biases.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "keys", - [](const map::BiasView &sv) { - const auto &biases = sv.GetBiases(); + [](const map::BiasView& sv) { + const auto& biases = sv.GetBiases(); return py::make_key_iterator(biases.begin(), biases.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used @@ -486,34 +505,34 @@ PYBIND11_MODULE(pymap, m) { .def("__contains__", &map::BiasView::HasBias); py::class_(m, "RigCameraView") - .def(py::init(), + .def(py::init(), py::keep_alive<1, 2>()) // Keep map alive while view is used .def("__len__", &map::RigCameraView::NumberOfRigCameras) .def( "items", - [](const map::RigCameraView &sv) { - const auto &cams = sv.GetRigCameras(); + [](const map::RigCameraView& sv) { + const auto& cams = sv.GetRigCameras(); return py::make_iterator(cams.begin(), cams.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "values", - [](map::RigCameraView &sv) { - auto &cams = sv.GetRigCameras(); + [](map::RigCameraView& sv) { + auto& cams = sv.GetRigCameras(); return py::make_ref_value_iterator(cams.begin(), cams.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "__iter__", - [](const map::RigCameraView &sv) { - const auto &cams = sv.GetRigCameras(); + [](const map::RigCameraView& sv) { + const auto& cams = sv.GetRigCameras(); return py::make_key_iterator(cams.begin(), cams.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "keys", - [](const map::RigCameraView &sv) { - const auto &cams = sv.GetRigCameras(); + [](const map::RigCameraView& sv) { + const auto& cams = sv.GetRigCameras(); return py::make_key_iterator(cams.begin(), cams.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used @@ -524,35 +543,35 @@ PYBIND11_MODULE(pymap, m) { .def("__contains__", &map::RigCameraView::HasRigCamera); py::class_(m, "RigInstanceView") - .def(py::init(), + .def(py::init(), py::keep_alive<1, 2>()) // Keep map alive while view is used .def("__len__", &map::RigInstanceView::NumberOfRigInstances) .def( "items", - [](const map::RigInstanceView &sv) { - const auto &instances = sv.GetRigInstances(); + [](const map::RigInstanceView& sv) { + const auto& instances = sv.GetRigInstances(); return py::make_iterator(instances.begin(), instances.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "values", - [](map::RigInstanceView &sv) { - auto &instances = sv.GetRigInstances(); + [](map::RigInstanceView& sv) { + auto& instances = sv.GetRigInstances(); return py::make_ref_value_iterator(instances.begin(), instances.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "__iter__", - [](const map::RigInstanceView &sv) { - const auto &instances = sv.GetRigInstances(); + [](const map::RigInstanceView& sv) { + const auto& instances = sv.GetRigInstances(); return py::make_iterator(instances.begin(), instances.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used .def( "keys", - [](const map::RigInstanceView &sv) { - const auto &instances = sv.GetRigInstances(); + [](const map::RigInstanceView& sv) { + const auto& instances = sv.GetRigInstances(); return py::make_key_iterator(instances.begin(), instances.end()); }, py::keep_alive<0, 1>()) // Keep view alive while iterator is used @@ -570,7 +589,7 @@ PYBIND11_MODULE(pymap, m) { .def("create_camera", &map::Map::CreateCamera, py::arg("camera"), py::return_value_policy::reference_internal) .def("get_camera", - py::overload_cast(&map::Map::GetCamera), + py::overload_cast(&map::Map::GetCamera), py::return_value_policy::reference_internal) // Bias .def("set_bias", &map::Map::SetBias, @@ -589,34 +608,35 @@ PYBIND11_MODULE(pymap, m) { .def("create_landmark", &map::Map::CreateLandmark, py::arg("lm_id"), py::arg("global_position"), py::return_value_policy::reference_internal) - .def("remove_landmark", (void (map::Map::*)(const map::Landmark *const)) & - map::Map::RemoveLandmark) - .def("remove_landmark", (void (map::Map::*)(const map::LandmarkId &)) & - map::Map::RemoveLandmark) + .def("remove_landmark", + (void (map::Map::*)( + const map::Landmark* const))&map::Map::RemoveLandmark) + .def( + "remove_landmark", + (void (map::Map::*)(const map::LandmarkId&))&map::Map::RemoveLandmark) .def("has_landmark", &map::Map::HasLandmark) .def("get_landmark", - py::overload_cast(&map::Map::GetLandmark), + py::overload_cast(&map::Map::GetLandmark), py::return_value_policy::reference_internal) .def("clear_observations_and_landmarks", &map::Map::ClearObservationsAndLandmarks) .def("clean_landmarks_below_min_observations", &map::Map::CleanLandmarksBelowMinObservations) // Shot + .def("create_shot", + py::overload_cast(&map::Map::CreateShot), + py::return_value_policy::reference_internal) .def( "create_shot", - py::overload_cast( + py::overload_cast( &map::Map::CreateShot), py::return_value_policy::reference_internal) - .def("create_shot", - py::overload_cast(&map::Map::CreateShot), - py::return_value_policy::reference_internal) .def("remove_shot", &map::Map::RemoveShot) .def("get_shot", - py::overload_cast(&map::Map::GetShot), + py::overload_cast(&map::Map::GetShot), py::return_value_policy::reference_internal) .def("update_shot", &map::Map::UpdateShot, py::return_value_policy::reference_internal) @@ -625,24 +645,25 @@ PYBIND11_MODULE(pymap, m) { py::return_value_policy::reference_internal) .def("remove_pano_shot", &map::Map::RemovePanoShot) .def("get_pano_shot", - py::overload_cast(&map::Map::GetPanoShot), + py::overload_cast(&map::Map::GetPanoShot), py::return_value_policy::reference_internal) .def("update_pano_shot", &map::Map::UpdatePanoShot, py::return_value_policy::reference_internal) // Observation .def("add_observation", - (void (map::Map::*)(map::Shot *const, map::Landmark *const, - const map::Observation &)) & - map::Map::AddObservation, + (void (map::Map::*)( + map::Shot* const, map::Landmark* const, + const map::Observation&))&map::Map::AddObservation, py::arg("shot"), py::arg("landmark"), py::arg("observation")) .def("add_observation", - (void (map::Map::*)(const map::ShotId &, const map::LandmarkId &, - const map::Observation &)) & - map::Map::AddObservation, + (void (map::Map::*)( + const map::ShotId&, const map::LandmarkId&, + const map::Observation&))&map::Map::AddObservation, py::arg("shot_Id"), py::arg("landmark_id"), py::arg("observation")) .def("remove_observation", - (void (map::Map::*)(const map::ShotId &, const map::LandmarkId &)) & - map::Map::RemoveObservation, + (void (map::Map::*)( + const map::ShotId&, + const map::LandmarkId&))&map::Map::RemoveObservation, py::arg("shot"), py::arg("landmark")) // Getters .def("get_shots", &map::Map::GetShotView) @@ -655,7 +676,7 @@ PYBIND11_MODULE(pymap, m) { .def("set_reference", &map::Map::SetTopocentricConverter) // Reference .def("get_reference", - [](const map::Map &map) { + [](const map::Map& map) { py::module::import("opensfm.pygeo"); return map.GetTopocentricConverter(); }) diff --git a/opensfm/src/map/rig.h b/opensfm/src/map/rig.h index c355fd2bf..4dab9ac3a 100644 --- a/opensfm/src/map/rig.h +++ b/opensfm/src/map/rig.h @@ -51,7 +51,8 @@ class RigInstance { std::unordered_map& GetRigCameras() { return shots_rig_cameras_; } - const std::unordered_map& GetRigCameras() const { + const std::unordered_map& GetRigCameras() + const { return shots_rig_cameras_; } std::set GetShotIDs() const; diff --git a/opensfm/src/map/shot.h b/opensfm/src/map/shot.h index 098980e7c..670652b8c 100644 --- a/opensfm/src/map/shot.h +++ b/opensfm/src/map/shot.h @@ -71,8 +71,8 @@ class Shot { // Pose void SetPose(const geometry::Pose& pose); - const geometry::Pose* const GetPose() const; - geometry::Pose* const GetPose(); + const geometry::Pose* GetPose() const; + geometry::Pose* GetPose(); Mat4d GetWorldToCam() const { return GetPose()->WorldToCamera(); } Mat4d GetCamToWorld() const { return GetPose()->CameraToWorld(); } @@ -101,9 +101,11 @@ class Shot { const Observation& GetObservation(const FeatureId id) const { return landmark_observations_.at(landmark_id_.at(id)); } - void CreateObservation(Landmark* lm, const Observation& obs) { - landmark_observations_.insert(std::make_pair(lm, obs)); + const Observation* CreateObservation(Landmark* lm, const Observation& obs) { + const auto inserted = + landmark_observations_.insert(std::make_pair(lm, obs)); landmark_id_.insert(std::make_pair(obs.feature_id, lm)); + return &inserted.first->second; } Observation* GetLandmarkObservation(Landmark* lm) { return &landmark_observations_.at(lm); @@ -117,6 +119,14 @@ class Shot { } } void RemoveLandmarkObservation(const FeatureId id); + void ClearLandmarkObservationsUnsafe() { + // Use swap idiom to actually release memory, not just clear elements. + // clear() may retain allocated memory/bucket capacity. + decltype(landmark_observations_) empty_obs; + landmark_observations_.swap(empty_obs); + decltype(landmark_id_) empty_ids; + landmark_id_.swap(empty_ids); + } // Metadata such as GPS, IMU, time const ShotMeasurements& GetShotMeasurements() const { @@ -134,13 +144,14 @@ class Shot { bool operator<=(const Shot& shot) const { return id_ <= shot.id_; } bool operator>(const Shot& shot) const { return id_ > shot.id_; } bool operator>=(const Shot& shot) const { return id_ >= shot.id_; } - const geometry::Camera* const GetCamera() const { return shot_camera_; } + const geometry::Camera* GetCamera() const { return shot_camera_; } // Camera-related Vec2d Project(const Vec3d& global_pos) const; MatX2d ProjectMany(const MatX3d& points) const; Vec3d Bearing(const Vec2d& point) const; MatX3d BearingMany(const MatX2d& points) const; + Vec3d LandmarkBearing(const Landmark* landmark) const; // Covariance MatXd GetCovariance() const { return covariance_.Value(); } diff --git a/opensfm/src/map/src/landmark.cc b/opensfm/src/map/src/landmark.cc index 14b1f6ed5..9cbfba89d 100644 --- a/opensfm/src/map/src/landmark.cc +++ b/opensfm/src/map/src/landmark.cc @@ -24,14 +24,22 @@ FeatureId Landmark::GetObservationIdInShot(Shot* shot) const { if (obs_it == observations_.end()) { throw std::runtime_error("Accessing with invalid shot ptr!"); } - return observations_.at(shot); + return observations_.at(shot)->feature_id; } -void Landmark::AddObservation(Shot* shot, const FeatureId& feat_id) { - observations_.emplace(shot, feat_id); +const Observation& Landmark::GetObservationInShot(Shot* shot) const { + auto obs_it = observations_.find(shot); + if (obs_it == observations_.end()) { + throw std::runtime_error("Accessing with invalid shot ptr!"); + } + return *observations_.at(shot); +} + +void Landmark::AddObservation(Shot* shot, const Observation* observation) { + observations_[shot] = observation; } -const std::map& Landmark::GetObservations() - const { +const std::map& +Landmark::GetObservations() const { return observations_; } diff --git a/opensfm/src/map/src/map.cc b/opensfm/src/map/src/map.cc index cce614ffc..096758d98 100644 --- a/opensfm/src/map/src/map.cc +++ b/opensfm/src/map/src/map.cc @@ -66,8 +66,8 @@ std::unique_ptr Map::DeepCopy(const Map& map, bool copy_observations) { void Map::AddObservation(Shot* const shot, Landmark* const lm, const Observation& obs) { - lm->AddObservation(shot, obs.feature_id); - shot->CreateObservation(lm, obs); + auto* observation = shot->CreateObservation(lm, obs); + lm->AddObservation(shot, observation); } void Map::AddObservation(const ShotId& shot_id, const LandmarkId& lm_id, @@ -131,16 +131,11 @@ Landmark& Map::GetLandmark(const LandmarkId& lm_id) { } void Map::ClearObservationsAndLandmarks() { - // first JUST delete the observations of the landmark - for (auto& id_lm : landmarks_) { - auto& observations = id_lm.second.GetObservations(); - for (const auto& obs : observations) { - obs.first->RemoveLandmarkObservation(obs.second); - } - id_lm.second.ClearObservations(); + for (auto& shot_pair : shots_) { + shot_pair.second.ClearLandmarkObservationsUnsafe(); } - // then clear the landmarks_ - landmarks_.clear(); + decltype(landmarks_) empty_landmarks; + landmarks_.swap(empty_landmarks); } void Map::CleanLandmarksBelowMinObservations(const size_t min_observations) { @@ -151,8 +146,8 @@ void Map::CleanLandmarksBelowMinObservations(const size_t min_observations) { const auto& observations = landmark.GetObservations(); for (const auto& obs : observations) { Shot* shot = obs.first; - const auto feat_id = obs.second; - shot->RemoveLandmarkObservation(feat_id); + const auto* feat = obs.second; + shot->RemoveLandmarkObservation(feat->feature_id); } // 3) Remove from landmarks it = landmarks_.erase(it); @@ -308,8 +303,8 @@ void Map::RemoveLandmark(const LandmarkId& lm_id) { const auto& observations = landmark.GetObservations(); for (const auto& obs : observations) { Shot* shot = obs.first; - const auto feat_id = obs.second; - shot->RemoveLandmarkObservation(feat_id); + const auto feat = obs.second; + shot->RemoveLandmarkObservation(feat->feature_id); } // 3) Remove from landmarks @@ -320,8 +315,8 @@ void Map::RemoveLandmark(const LandmarkId& lm_id) { } geometry::Camera& Map::CreateCamera(const geometry::Camera& cam) { - auto it = cameras_.emplace(std::make_pair(cam.id, cam)); - bias_.emplace(std::make_pair(cam.id, geometry::Similarity())); + auto it = cameras_.emplace(cam.id, cam); + bias_.emplace(cam.id, geometry::Similarity()); return it.first->second; } @@ -413,7 +408,7 @@ RigCamera& Map::CreateRigCamera(const map::RigCamera& rig_camera) { throw std::runtime_error("RigCamera " + rig_camera.id + " already exists."); } - auto it = rig_cameras_.emplace(std::make_pair(rig_camera.id, rig_camera)); + auto it = rig_cameras_.emplace(rig_camera.id, rig_camera); return it.first->second; } diff --git a/opensfm/src/map/src/observation.cc b/opensfm/src/map/src/observation.cc index 61773f4c9..badd93529 100644 --- a/opensfm/src/map/src/observation.cc +++ b/opensfm/src/map/src/observation.cc @@ -1,5 +1,3 @@ #include -namespace map { -constexpr int Observation::NO_SEMANTIC_VALUE; -} +namespace map {} diff --git a/opensfm/src/map/src/shot.cc b/opensfm/src/map/src/shot.cc index fe81b68ff..0b13520da 100644 --- a/opensfm/src/map/src/shot.cc +++ b/opensfm/src/map/src/shot.cc @@ -152,7 +152,7 @@ geometry::Pose Shot::GetPoseInRig() const { return rig_camera_pose.Compose(pose_instance); } -const geometry::Pose* const Shot::GetPose() const { +const geometry::Pose* Shot::GetPose() const { *pose_ = GetPoseInRig(); if (IsSingleShotRig(rig_instance_, rig_camera_)) { return &rig_instance_->GetPose(); @@ -160,7 +160,7 @@ const geometry::Pose* const Shot::GetPose() const { return pose_.get(); } -geometry::Pose* const Shot::GetPose() { +geometry::Pose* Shot::GetPose() { *pose_ = GetPoseInRig(); if (IsSingleShotRig(rig_instance_, rig_camera_)) { return &rig_instance_->GetPose(); @@ -185,6 +185,15 @@ Vec3d Shot::Bearing(const Vec2d& point) const { return GetPose()->RotationCameraToWorld() * shot_camera_->Bearing(point); } +Vec3d Shot::LandmarkBearing(const Landmark* landmark) const { + auto it = landmark_observations_.find(const_cast(landmark)); + if (it == landmark_observations_.end()) { + throw std::runtime_error("Landmark not observed in this shot"); + } + const Observation& obs = it->second; + return Bearing(obs.point); +} + MatX3d Shot::BearingMany(const MatX2d& points) const { MatX3d bearings(points.rows(), 3); for (int i = 0; i < points.rows(); ++i) { diff --git a/opensfm/src/map/src/tracks_manager.cc b/opensfm/src/map/src/tracks_manager.cc index 5d45343c7..b8ff60328 100644 --- a/opensfm/src/map/src/tracks_manager.cc +++ b/opensfm/src/map/src/tracks_manager.cc @@ -1,6 +1,10 @@ +#include #include #include +#include +#include +#include #include #include #include @@ -110,7 +114,7 @@ map::TracksManager InstanciateFromStreamV0(S& fstream) { SeparateLineByTabs(line, elems); if (elems.size() != N_ENTRIES) // process only valid lines { - std::runtime_error( + throw std::runtime_error( "Encountered invalid line. A line must contain exactly " + std::to_string(N_ENTRIES) + " values!"); } @@ -140,7 +144,7 @@ map::TracksManager InstanciateFromStreamV1(S& fstream) { SeparateLineByTabs(line, elems); if (elems.size() != N_ENTRIES) // process only valid lines { - std::runtime_error( + throw std::runtime_error( "Encountered invalid line. A line must contain exactly " + std::to_string(N_ENTRIES) + " values!"); } @@ -170,7 +174,7 @@ map::TracksManager InstanciateFromStreamV2(S& fstream) { SeparateLineByTabs(line, elems); if (elems.size() != N_ENTRIES) // process only valid lines { - std::runtime_error( + throw std::runtime_error( "Encountered invalid line. A line must contain exactly " + std::to_string(N_ENTRIES) + " values!"); } @@ -241,104 +245,156 @@ map::TracksManager InstanciateFromStreamT(S& fstream, const std::string &filenam } // namespace namespace map { -void TracksManager::AddObservation(const ShotId& shot_id, - const TrackId& track_id, - const Observation& observation) { - tracks_per_shot_[shot_id][track_id] = observation; - shots_per_track_[track_id][shot_id] = observation; -} -void TracksManager::RemoveObservation(const ShotId& shot_id, - const TrackId& track_id) { - const auto find_shot = tracks_per_shot_.find(shot_id); - if (find_shot == tracks_per_shot_.end()) { - throw std::runtime_error("Accessing invalid shot ID"); - } - const auto find_track = shots_per_track_.find(track_id); - if (find_track == shots_per_track_.end()) { - throw std::runtime_error("Accessing invalid track ID"); +TracksManager::StringId TracksManager::GetShotIndex(const ShotId& id) { + const auto it = shot_id_to_index_.find(id); + if (it == shot_id_to_index_.end()) { + throw std::runtime_error("Accessing invalid shot ID: " + id); } - find_shot->second.erase(track_id); - find_track->second.erase(shot_id); + return it->second; } -int TracksManager::NumShots() const { return tracks_per_shot_.size(); } - -int TracksManager::NumTracks() const { return shots_per_track_.size(); } +TracksManager::StringId TracksManager::GetTrackIndex(const TrackId& id) { + const auto it = track_id_to_index_.find(id); + if (it == track_id_to_index_.end()) { + throw std::runtime_error("Accessing invalid track ID: " + id); + } + return it->second; +} -bool TracksManager::HasShotObservations(const ShotId& shot) const { - return tracks_per_shot_.count(shot) > 0; +TracksManager::StringId TracksManager::GetOrInsertShotIndex(const ShotId& id) { + const auto it = shot_id_to_index_.find(id); + if (it != shot_id_to_index_.end()) { + return it->second; + } + const StringId idx = shot_ids_.size(); + shot_ids_.push_back(id); + shot_id_to_index_[id] = idx; + tracks_per_shot_.emplace_back(); + return idx; } -std::vector TracksManager::GetShotIds() const { - std::vector shots; - shots.reserve(tracks_per_shot_.size()); - for (const auto& it : tracks_per_shot_) { - shots.push_back(it.first); +TracksManager::StringId TracksManager::GetOrInsertTrackIndex( + const TrackId& id) { + const auto it = track_id_to_index_.find(id); + if (it != track_id_to_index_.end()) { + return it->second; } - return shots; + const StringId idx = track_ids_.size(); + track_ids_.push_back(id); + track_id_to_index_[id] = idx; + shots_per_track_.emplace_back(); + return idx; } -std::vector TracksManager::GetTrackIds() const { - std::vector tracks; - tracks.reserve(shots_per_track_.size()); - for (const auto& it : shots_per_track_) { - tracks.push_back(it.first); +void TracksManager::AddObservation(const ShotId& shot_id, + const TrackId& track_id, + const Observation& observation) { + const StringId shot_idx = GetOrInsertShotIndex(shot_id); + const StringId track_idx = GetOrInsertTrackIndex(track_id); + + auto& shot_tracks = tracks_per_shot_[shot_idx]; + auto it = shot_tracks.find(track_idx); + if (it != shot_tracks.end()) { + observations_[it->second] = observation; + return; } - return tracks; + + // Allocate new index and store observation + observations_.push_back(observation); + ObservationIndex obs_idx = observations_.size() - 1; + + tracks_per_shot_[shot_idx].emplace(track_idx, obs_idx); + shots_per_track_[track_idx].emplace(shot_idx, obs_idx); +} + +int TracksManager::NumShots() const { return shot_ids_.size(); } + +int TracksManager::NumTracks() const { return track_ids_.size(); } + +bool TracksManager::HasShotObservations(const ShotId& shot) const { + return shot_id_to_index_.count(shot) > 0; } +std::vector TracksManager::GetShotIds() const { return shot_ids_; } + +std::vector TracksManager::GetTrackIds() const { return track_ids_; } + Observation TracksManager::GetObservation(const ShotId& shot, const TrackId& track) const { - const auto find_shot = tracks_per_shot_.find(shot); - if (find_shot == tracks_per_shot_.end()) { - throw std::runtime_error("Accessing invalid shot ID"); - } - const auto find_track = find_shot->second.find(track); - if (find_track == find_shot->second.end()) { + // Use map::at to throw if not found, consistent with original implementation + const StringId shot_idx = shot_id_to_index_.at(shot); + const StringId track_idx = track_id_to_index_.at(track); + + const auto& shot_tracks = tracks_per_shot_[shot_idx]; + const auto it = shot_tracks.find(track_idx); + if (it == shot_tracks.end()) { throw std::runtime_error("Accessing invalid track ID"); } - return find_track->second; + return observations_[it->second]; } -const std::unordered_map& -TracksManager::GetShotObservations(const ShotId& shot) const { - const auto find_shot = tracks_per_shot_.find(shot); - if (find_shot == tracks_per_shot_.end()) { +std::unordered_map TracksManager::GetShotObservations( + const ShotId& shot) const { + const auto it = shot_id_to_index_.find(shot); + if (it == shot_id_to_index_.end()) { throw std::runtime_error("Accessing invalid shot ID"); } - return find_shot->second; + const StringId shot_idx = it->second; + + std::unordered_map result; + const auto& shot_tracks = tracks_per_shot_[shot_idx]; + result.reserve(shot_tracks.size()); + + for (const auto& [track_idx, obs_idx] : shot_tracks) { + result.emplace(track_ids_[track_idx], observations_[obs_idx]); + } + return result; } -const std::unordered_map& -TracksManager::GetTrackObservations(const TrackId& track) const { - const auto find_track = shots_per_track_.find(track); - if (find_track == shots_per_track_.end()) { +std::unordered_map TracksManager::GetTrackObservations( + const TrackId& track) const { + const auto it = track_id_to_index_.find(track); + if (it == track_id_to_index_.end()) { throw std::runtime_error("Accessing invalid track ID"); } - return find_track->second; + const StringId track_idx = it->second; + + std::unordered_map result; + const auto& track_shots = shots_per_track_[track_idx]; + result.reserve(track_shots.size()); + + for (const auto& [shot_idx, obs_idx] : track_shots) { + result.emplace(shot_ids_[shot_idx], observations_[obs_idx]); + } + return result; } TracksManager TracksManager::ConstructSubTracksManager( const std::vector& tracks, const std::vector& shots) const { - std::unordered_set shotsTmp; + std::unordered_set allowed_shot_indices; for (const auto& id : shots) { - shotsTmp.insert(id); + const auto it = shot_id_to_index_.find(id); + if (it != shot_id_to_index_.end()) { + allowed_shot_indices.insert(it->second); + } } TracksManager subset; for (const auto& track_id : tracks) { - const auto find_track = shots_per_track_.find(track_id); - if (find_track == shots_per_track_.end()) { + const auto it_track = track_id_to_index_.find(track_id); + if (it_track == track_id_to_index_.end()) { continue; } - for (const auto& obs : find_track->second) { - const auto& shot_id = obs.first; - if (shotsTmp.find(shot_id) == shotsTmp.end()) { - continue; + const StringId track_idx = it_track->second; + + const auto& track_shots = shots_per_track_[track_idx]; + for (const auto& [shot_idx, obs_idx] : track_shots) { + if (allowed_shot_indices.count(shot_idx)) { + subset.AddObservation(shot_ids_[shot_idx], track_id, + observations_[obs_idx]); } - subset.AddObservation(shot_id, track_id, obs.second); } } return subset; @@ -347,64 +403,96 @@ TracksManager TracksManager::ConstructSubTracksManager( std::vector TracksManager::GetAllCommonObservations(const ShotId& shot1, const ShotId& shot2) const { - auto find_shot1 = tracks_per_shot_.find(shot1); - auto find_shot2 = tracks_per_shot_.find(shot2); - if (find_shot1 == tracks_per_shot_.end() || - find_shot2 == tracks_per_shot_.end()) { + const auto find_shot1 = shot_id_to_index_.find(shot1); + const auto find_shot2 = shot_id_to_index_.find(shot2); + if (find_shot1 == shot_id_to_index_.end() || + find_shot2 == shot_id_to_index_.end()) { throw std::runtime_error("Accessing invalid shot ID"); } + const StringId idx1 = find_shot1->second; + const StringId idx2 = find_shot2->second; + + const auto& tracks1 = tracks_per_shot_[idx1]; + const auto& tracks2 = tracks_per_shot_[idx2]; + std::vector tuples; - for (const auto& p : find_shot1->second) { - const auto find = find_shot2->second.find(p.first); - if (find != find_shot2->second.end()) { - tuples.emplace_back(p.first, p.second, find->second); + tuples.reserve(std::min(tracks1.size(), tracks2.size())); + + for (const auto& p : tracks1) { + const auto find = tracks2.find(p.first); + if (find != tracks2.end()) { + tuples.emplace_back(track_ids_.at(p.first), observations_.at(p.second), + observations_.at(find->second)); } } return tuples; } +std::tuple, MatX2f, MatX2f> +TracksManager::GetAllCommonObservationsArrays(const ShotId& shot1, + const ShotId& shot2) const { + const auto tuples = GetAllCommonObservations(shot1, shot2); + + std::vector track_ids(tuples.size()); + MatX2f points1(tuples.size(), 2); + MatX2f points2(tuples.size(), 2); + for (int i = 0; i < tuples.size(); ++i) { + const auto& [track_id, obs1, obs2] = tuples[i]; + track_ids[i] = track_id; + points1.row(i) = obs1.point.cast(); + points2.row(i) = obs2.point.cast(); + } + return {track_ids, points1, points2}; +} + std::unordered_map TracksManager::GetAllPairsConnectivity( const std::vector& shots, const std::vector& tracks) const { std::unordered_map common_per_pair; - std::vector tracks_to_use; + std::vector tracks_to_use; if (tracks.empty()) { - for (const auto& track : shots_per_track_) { - tracks_to_use.push_back(track.first); - } + tracks_to_use.resize(track_ids_.size()); + std::iota(tracks_to_use.begin(), tracks_to_use.end(), 0); } else { - tracks_to_use = tracks; + tracks_to_use.reserve(tracks.size()); + for (const auto& t_id : tracks) { + auto it = track_id_to_index_.find(t_id); + if (it != track_id_to_index_.end()) { + tracks_to_use.push_back(it->second); + } + } } - std::unordered_set shots_to_use; + std::vector shots_to_use(shot_ids_.size(), false); if (shots.empty()) { - for (const auto& shot : tracks_per_shot_) { - shots_to_use.insert(shot.first); - } + std::fill(shots_to_use.begin(), shots_to_use.end(), true); } else { - for (const auto& shot : shots) { - shots_to_use.insert(shot); + for (const auto& s_id : shots) { + auto it = shot_id_to_index_.find(s_id); + if (it != shot_id_to_index_.end()) { + shots_to_use[it->second] = true; + } } } - for (const auto& track_id : tracks_to_use) { - const auto find_track = shots_per_track_.find(track_id); - if (find_track == shots_per_track_.end()) { - continue; - } - const auto& track = find_track->second; - for (const auto& it1 : track) { - const auto& shot_id1 = it1.first; - if (shots_to_use.find(shot_id1) != shots_to_use.end()) { - for (const auto& it2 : track) { - const auto& shot_id2 = it2.first; - if (shot_id1 < shot_id2 && - shots_to_use.find(shot_id2) != shots_to_use.end()) { - ++common_per_pair[std::make_pair(shot_id1, shot_id2)]; - } + for (StringId track_idx : tracks_to_use) { + const auto& track_entries = shots_per_track_[track_idx]; + + for (const auto& [shot_idx1, _] : track_entries) { + if (!shots_to_use[shot_idx1]) { + continue; + } + const auto& shot_id1 = shot_ids_[shot_idx1]; + for (const auto& [shot_idx2, _] : track_entries) { + if (!shots_to_use[shot_idx2]) { + continue; + } + const auto& shot_id2 = shot_ids_[shot_idx2]; + if (shot_id1 < shot_id2) { + ++common_per_pair[std::make_pair(shot_id1, shot_id2)]; } } } @@ -414,30 +502,27 @@ TracksManager::GetAllPairsConnectivity( TracksManager TracksManager::MergeTracksManager( const std::vector& tracks_managers) { - // Some typedefs claryfying the aggregations - using FeatureId = std::pair; - using SingleTrackId = std::pair; - - // Union-find main data - std::vector>> - union_find_elements; - - // Aggregate tracks be merged using (shot_id, feature_id) - std::unordered_map, HashPair> + using FeatureId_2 = std::pair; + using TrackRef = std::pair; + std::unordered_map, HashPair> observations_per_feature_id; - for (int i = 0; i < tracks_managers.size(); ++i) { - const auto& manager = tracks_managers[i]; - for (const auto& track_obses : manager->shots_per_track_) { + std::vector>> union_find_elements; + + for (int mgr_idx = 0; mgr_idx < tracks_managers.size(); ++mgr_idx) { + const auto* mgr = tracks_managers[mgr_idx]; + for (StringId track_idx = 0; track_idx < mgr->track_ids_.size(); + ++track_idx) { const auto element_id = union_find_elements.size(); - for (const auto& shot_obs : track_obses.second) { - observations_per_feature_id[std::make_pair(shot_obs.first, - shot_obs.second.feature_id)] - .push_back(element_id); + for (const auto& [shot_idx, obs_idx] : mgr->shots_per_track_[track_idx]) { + const auto& obs = mgr->observations_[obs_idx]; + const ShotId& shot_id = mgr->shot_ids_[shot_idx]; + + observations_per_feature_id[{shot_id, obs.feature_id}].emplace_back( + element_id); } + union_find_elements.emplace_back( - std::unique_ptr>( - new UnionFindElement( - std::make_pair(track_obses.first, i)))); + new UnionFindElement({mgr_idx, track_idx})); } } @@ -446,7 +531,7 @@ TracksManager TracksManager::MergeTracksManager( return merged; } - // Union-find any two tracks sharing a common FeatureId + // Union-find any two tracks sharing a common FeatureId_2 // For N tracks, make 0 the parent of [1, ... N-1[ for (const auto& tracks_agg : observations_per_feature_id) { if (tracks_agg.second.empty()) { @@ -466,12 +551,14 @@ TracksManager TracksManager::MergeTracksManager( const auto merged_track_id = std::to_string(i); // Run over tracks to merged into a new single track for (const auto& manager_n_track_id : tracks_agg) { - const auto manager_id = manager_n_track_id->data.second; - const auto track_id = manager_n_track_id->data.first; - const auto track = - tracks_managers[manager_id]->shots_per_track_.at(track_id); - for (const auto& shot_obs : track) { - merged.AddObservation(shot_obs.first, merged_track_id, shot_obs.second); + const auto manager_id = manager_n_track_id->data.first; + const auto track_idx = manager_n_track_id->data.second; + const auto* mgr = tracks_managers[manager_id]; + + const auto& observations = mgr->shots_per_track_[track_idx]; + for (const auto& [shot_idx, obs_idx] : observations) { + merged.AddObservation(mgr->shot_ids_[shot_idx], merged_track_id, + mgr->observations_[obs_idx]); } } } diff --git a/opensfm/src/map/test/map_test.cc b/opensfm/src/map/test/map_test.cc index 280078dec..29be042de 100644 --- a/opensfm/src/map/test/map_test.cc +++ b/opensfm/src/map/test/map_test.cc @@ -127,9 +127,6 @@ class ToyMapFixture : public EmptyMapFixture { std::array num_shots = {5, 3}; }; -const int ToyMapFixture::num_points; -const int ToyMapFixture::num_cameras; - TEST_F(ToyMapFixture, ReturnsNumberOfShots) { ASSERT_EQ(map.NumberOfShots(), 8); } @@ -271,8 +268,8 @@ TEST_F(OneCameraMapFixture, ComputeReprojectionErrorNormalized) { auto errors = map.ComputeReprojectionErrors(manager, map::Map::ErrorType::Normalized); const auto computed = errors["0"]["1"]; - ASSERT_NEAR(expected[0] / scale, computed[0], 1e-8); - ASSERT_NEAR(expected[1] / scale, computed[1], 1e-8); + ASSERT_NEAR(expected[0] / scale, computed[0], 1e-7); + ASSERT_NEAR(expected[1] / scale, computed[1], 1e-7); } class OneRigMapFixture : public EmptyMapFixture { @@ -347,7 +344,7 @@ TEST_F(ToyMapFixture, ToTracksManager) { ASSERT_EQ(manager.NumTracks(), map.NumberOfLandmarks()); ASSERT_EQ(manager.NumShots(), map.NumberOfShots() + map.NumberOfPanoShots()); for (const auto& shot_pair : map.GetShots()) { - const auto& shot_obs1 = manager.GetShotObservations(shot_pair.first); + const auto shot_obs1 = manager.GetShotObservations(shot_pair.first); const auto& shot_obs2 = shot_pair.second.GetLandmarkObservations(); ASSERT_EQ(shot_obs1.size(), shot_obs2.size()); diff --git a/opensfm/src/map/test/tracks_manager_test.cc b/opensfm/src/map/test/tracks_manager_test.cc index 507203912..711d84170 100644 --- a/opensfm/src/map/test/tracks_manager_test.cc +++ b/opensfm/src/map/test/tracks_manager_test.cc @@ -7,9 +7,12 @@ namespace { class TempFile { public: TempFile() { - char tmpname[L_tmpnam]; - tmpnam(tmpname); - filename = std::string(tmpname); + char filenameTmp[] = "/tmp/opensfm_tracks_manager_test_XXXXXX"; + int fd = mkstemp(filenameTmp); + if (fd == -1) { + std::runtime_error("Could not create temporary file"); + } + filename = std::string(filenameTmp); } ~TempFile() { remove(filename.c_str()); } @@ -60,13 +63,6 @@ TEST_F(TracksManagerTest, AddsObservation) { EXPECT_EQ(manager.GetObservation("4", "1"), obs); } -TEST_F(TracksManagerTest, RemoveObservation) { - manager.RemoveObservation("3", "1"); - auto copy = track; - copy.erase("3"); - EXPECT_EQ(manager.GetTrackObservations("1"), copy); -} - TEST_F(TracksManagerTest, ReturnsAllCommonObservations) { const auto tuple = std::make_tuple("1", map::Observation(1.0, 1.0, 1.0, 1, 1, 1, 1, 1, 1), @@ -129,30 +125,41 @@ TEST_F(TracksManagerTest, MergeThreeTracksManager) { auto merged = map::TracksManager::MergeTracksManager({&manager1, &manager2, &manager3}); - std::unordered_map track0; - track0["1"] = o1; - track0["2"] = o2; - track0["3"] = o3; - track0["4"] = o4; - track0["5"] = o5; - - std::unordered_map track1; - track1["6"] = o6; - track1["7"] = o7; - - std::unordered_map track2; - track2["8"] = o8; - - std::unordered_map track3; - track3["1"] = o0; + const auto track0 = merged.GetTrackObservations("0"); + const auto track1 = merged.GetTrackObservations("1"); + const auto track2 = merged.GetTrackObservations("2"); + const auto track3 = merged.GetTrackObservations("3"); EXPECT_THAT( merged.GetTrackIds(), ::testing::WhenSorted(::testing::ElementsAre("0", "1", "2", "3"))); - EXPECT_EQ(merged.GetTrackObservations("0"), track2); - EXPECT_EQ(merged.GetTrackObservations("1"), track3); - EXPECT_EQ(merged.GetTrackObservations("2"), track1); - EXPECT_EQ(merged.GetTrackObservations("3"), track0); + + std::unordered_map gt_trackA; + gt_trackA["1"] = o1; + gt_trackA["2"] = o2; + gt_trackA["3"] = o3; + gt_trackA["4"] = o4; + gt_trackA["5"] = o5; + + std::unordered_map gt_trackB; + gt_trackB["6"] = o6; + gt_trackB["7"] = o7; + + std::unordered_map gt_trackC; + gt_trackC["8"] = o8; + + std::unordered_map gt_trackD; + gt_trackD["1"] = o0; + + // Expect the two sets of tracks to be the same for some reordering + EXPECT_TRUE(track0 == gt_trackA || track1 == gt_trackA || + track2 == gt_trackA || track3 == gt_trackA); + EXPECT_TRUE(track0 == gt_trackB || track1 == gt_trackB || + track2 == gt_trackB || track3 == gt_trackB); + EXPECT_TRUE(track0 == gt_trackC || track1 == gt_trackC || + track2 == gt_trackC || track3 == gt_trackC); + EXPECT_TRUE(track0 == gt_trackD || track1 == gt_trackD || + track2 == gt_trackD || track3 == gt_trackD); } TEST_F(TracksManagerTest, HasIOFileConsistency) { diff --git a/opensfm/src/map/tracks_manager.h b/opensfm/src/map/tracks_manager.h index ab17d3189..9a2cbb2aa 100644 --- a/opensfm/src/map/tracks_manager.h +++ b/opensfm/src/map/tracks_manager.h @@ -9,11 +9,16 @@ #include namespace map { + +// Index type for observation storage +using ObservationIndex = size_t; +constexpr ObservationIndex INVALID_OBSERVATION_INDEX = + static_cast(-1); + class TracksManager { public: void AddObservation(const ShotId& shot_id, const TrackId& track_id, const Observation& observation); - void RemoveObservation(const ShotId& shot_id, const TrackId& track_id); Observation GetObservation(const ShotId& shot, const TrackId& track) const; int NumShots() const; @@ -21,9 +26,13 @@ class TracksManager { std::vector GetShotIds() const; std::vector GetTrackIds() const; - const std::unordered_map& GetShotObservations( + // Returns a map of track_id -> observation for a given shot + // Note: This constructs the map on each call (no longer returns a reference) + std::unordered_map GetShotObservations( const ShotId& shot) const; - const std::unordered_map& GetTrackObservations( + // Returns a map of shot_id -> observation for a given track + // Note: This constructs the map on each call (no longer returns a reference) + std::unordered_map GetTrackObservations( const TrackId& track) const; TracksManager ConstructSubTracksManager( @@ -33,6 +42,9 @@ class TracksManager { using KeyPointTuple = std::tuple; std::vector GetAllCommonObservations( const ShotId& shot1, const ShotId& shot2) const; + std::tuple, MatX2f, MatX2f> + GetAllCommonObservationsArrays(const ShotId& shot1, + const ShotId& shot2) const; using ShotPair = std::pair; std::unordered_map GetAllPairsConnectivity( @@ -54,9 +66,26 @@ class TracksManager { static int TRACKS_VERSION; private: - std::unordered_map> - tracks_per_shot_; - std::unordered_map> - shots_per_track_; + // Interning types and helpers + using StringId = size_t; + StringId GetShotIndex(const ShotId& id); + StringId GetTrackIndex(const TrackId& id); + StringId GetOrInsertShotIndex(const ShotId& id); + StringId GetOrInsertTrackIndex(const TrackId& id); + + // Single storage for all observations - each observation stored exactly once + std::vector observations_; + + // Interning storage + std::vector shot_ids_; + std::vector track_ids_; + std::unordered_map shot_id_to_index_; + std::unordered_map track_id_to_index_; + + // Adjacency lists using integer indices + // tracks_per_shot_[shot_index] -> map {track_index -> obs_index} + std::vector> tracks_per_shot_; + // shots_per_track_[track_index] -> map {shot_index -> obs_index} + std::vector> shots_per_track_; }; } // namespace map diff --git a/opensfm/src/robust/CMakeLists.txt b/opensfm/src/robust/CMakeLists.txt index 8509a61a0..44f94a420 100644 --- a/opensfm/src/robust/CMakeLists.txt +++ b/opensfm/src/robust/CMakeLists.txt @@ -33,3 +33,4 @@ target_link_libraries(pyrobust set_target_properties(pyrobust PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${opensfm_SOURCE_DIR}/.." ) +install(TARGETS pyrobust LIBRARY DESTINATION .) diff --git a/opensfm/src/robust/absolute_pose_known_rotation_model.h b/opensfm/src/robust/absolute_pose_known_rotation_model.h index ddd088973..1b74438ac 100644 --- a/opensfm/src/robust/absolute_pose_known_rotation_model.h +++ b/opensfm/src/robust/absolute_pose_known_rotation_model.h @@ -2,6 +2,7 @@ #include #include + #include "model.h" class AbsolutePoseKnownRotation diff --git a/opensfm/src/robust/absolute_pose_model.h b/opensfm/src/robust/absolute_pose_model.h index 7dfac5875..146c12e47 100644 --- a/opensfm/src/robust/absolute_pose_model.h +++ b/opensfm/src/robust/absolute_pose_model.h @@ -2,6 +2,7 @@ #include #include + #include "model.h" class AbsolutePose : public Model { diff --git a/opensfm/src/robust/essential_model.h b/opensfm/src/robust/essential_model.h index 08d75bc29..1b5d26b01 100644 --- a/opensfm/src/robust/essential_model.h +++ b/opensfm/src/robust/essential_model.h @@ -2,6 +2,7 @@ #include #include + #include "model.h" class EpipolarSymmetric { diff --git a/opensfm/src/robust/line_model.h b/opensfm/src/robust/line_model.h index 6af58753f..1a6026a44 100644 --- a/opensfm/src/robust/line_model.h +++ b/opensfm/src/robust/line_model.h @@ -9,7 +9,7 @@ class Line : public Model { static const int MINIMAL_SAMPLES = 2; template - static int Estimate(IT begin, IT end, Type* models) { + static int Estimate(IT begin, IT /* end */, Type* models) { const auto x1 = *begin; const auto x2 = *(++begin); const auto b = (x1[0] * x2[1] - x1[1] * x2[0]) / (x1[0] - x2[0]); diff --git a/opensfm/src/robust/pyrobust.pyi b/opensfm/src/robust/pyrobust.pyi index 0846303e9..ec78203b7 100644 --- a/opensfm/src/robust/pyrobust.pyi +++ b/opensfm/src/robust/pyrobust.pyi @@ -2,8 +2,13 @@ # Do not manually edit # To regenerate: # $ buck run //mapillary/opensfm/opensfm/src/robust:pyrobust_stubgen -# Use proper mode, e.g. @arvr/mode/linux/dev for arvr # @generated +# +# Tip: Be sure to run this with the build mode you use for your project, e.g., +# @//arvr/mode/linux/opt (or dev) in arvr. +# +# Ignore errors for [5] global variable types and [24] untyped generics. +# pyre-ignore-all-errors[5,24] import numpy from typing import * diff --git a/opensfm/src/robust/relative_pose_model.h b/opensfm/src/robust/relative_pose_model.h index 160b8e314..1d0fa8c29 100644 --- a/opensfm/src/robust/relative_pose_model.h +++ b/opensfm/src/robust/relative_pose_model.h @@ -3,6 +3,7 @@ #include #include #include + #include "essential_model.h" #include "model.h" diff --git a/opensfm/src/robust/relative_rotation_model.h b/opensfm/src/robust/relative_rotation_model.h index 49270ede4..1a23236a3 100644 --- a/opensfm/src/robust/relative_rotation_model.h +++ b/opensfm/src/robust/relative_rotation_model.h @@ -2,6 +2,7 @@ #include #include + #include "model.h" class RelativeRotation : public Model { diff --git a/opensfm/src/robust/scorer.h b/opensfm/src/robust/scorer.h index 2eaba0034..6fccaeb95 100644 --- a/opensfm/src/robust/scorer.h +++ b/opensfm/src/robust/scorer.h @@ -23,7 +23,8 @@ class RansacScoring { RansacScoring(double threshold) : threshold_(threshold) {} template - ScoreInfo Score(IT begin, IT end, const ScoreInfo& best_score) const { + ScoreInfo Score(IT begin, IT end, + const ScoreInfo& /* best_score */) const { ScoreInfo score; for (IT it = begin; it != end; ++it) { if (it->norm() < threshold_) { @@ -62,7 +63,8 @@ class MSacScoring { MSacScoring(double threshold) : threshold_(threshold) {} template - ScoreInfo Score(IT begin, IT end, const ScoreInfo& best_score) const { + ScoreInfo Score(IT begin, IT end, + const ScoreInfo& /* best_score */) const { ScoreInfo score; for (IT it = begin; it != end; ++it) { const auto v = (*it).norm(); @@ -87,7 +89,8 @@ class LMedSScoring : public MedianBasedScoring { : MedianBasedScoring(0.5), multiplier_(multiplier) {} template - ScoreInfo Score(IT begin, IT end, const ScoreInfo& best_score) const { + ScoreInfo Score(IT begin, IT end, + const ScoreInfo& /* best_score */) const { const auto median = this->ComputeMedian(begin, end); const auto mad = 1.4826 * median; const auto threshold = this->multiplier_ * mad; diff --git a/opensfm/src/sfm/CMakeLists.txt b/opensfm/src/sfm/CMakeLists.txt index a3eb2ae85..ee86f42f1 100644 --- a/opensfm/src/sfm/CMakeLists.txt +++ b/opensfm/src/sfm/CMakeLists.txt @@ -2,9 +2,11 @@ set(SFM_FILES retriangulation.h ba_helpers.h tracks_helpers.h + map_helpers.h src/retriangulation.cc src/ba_helpers.cc src/tracks_helpers.cc + src/map_helpers.cc ) add_library(sfm ${SFM_FILES}) target_link_libraries(sfm @@ -13,7 +15,9 @@ target_link_libraries(sfm PRIVATE foundation map + geometry bundle + vl ) target_include_directories(sfm PUBLIC ${CMAKE_SOURCE_DIR} ${CERES_INCLUDE_DIR}/ceres/internal/miniglog) @@ -40,3 +44,4 @@ target_link_libraries(pysfm set_target_properties(pysfm PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${opensfm_SOURCE_DIR}/.." ) +install(TARGETS pysfm LIBRARY DESTINATION .) diff --git a/opensfm/src/sfm/ba_helpers.h b/opensfm/src/sfm/ba_helpers.h index e0c8552d1..db6652b5b 100644 --- a/opensfm/src/sfm/ba_helpers.h +++ b/opensfm/src/sfm/ba_helpers.h @@ -12,12 +12,18 @@ namespace sfm { class GroundControlPoint; class BAHelpers { public: + struct TracksSelection { + std::unordered_set selected_tracks; + std::unordered_set other_tracks; + }; + static py::dict Bundle( map::Map& map, const std::unordered_map& camera_priors, const std::unordered_map& rig_camera_priors, const AlignedVector& gcp, + int grid_size, const py::dict& config); static py::tuple BundleLocal( @@ -26,7 +32,9 @@ class BAHelpers { const std::unordered_map& rig_camera_priors, const AlignedVector& gcp, - const map::ShotId& central_shot_id, const py::dict& config); + const map::ShotId& central_shot_id, + int grid_size, + const py::dict& config); static py::dict BundleShotPoses( map::Map& map, const std::unordered_set& shot_ids, @@ -57,6 +65,11 @@ class BAHelpers { const AlignedVector& gcp, const py::dict& config); + static TracksSelection SelectTracksGrid( + map::Map& map, + const std::unordered_set& shot_ids, + size_t grid_size); + private: static std::unordered_set DirectShotNeighbors( map::Map& map, const std::unordered_set& shot_ids, @@ -64,6 +77,7 @@ class BAHelpers { static bool TriangulateGCP( const map::GroundControlPoint& point, const std::unordered_map& shots, + float reproj_threshold, Vec3d& coordinates); static void AlignmentConstraints( diff --git a/opensfm/src/sfm/map_helpers.h b/opensfm/src/sfm/map_helpers.h new file mode 100644 index 000000000..34e5fe9bb --- /dev/null +++ b/opensfm/src/sfm/map_helpers.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace sfm::map_helpers { + +int FilterBadlyConditionedPoints(map::Map& map, double min_angle_deg = 1.0, + double min_abs_det = 1e-15); +int RemoveIsolatedPoints(map::Map& map, int k = 7); + +} // namespace sfm::map_helpers diff --git a/opensfm/src/sfm/pysfm.pyi b/opensfm/src/sfm/pysfm.pyi index 78b943472..71f66c3a2 100644 --- a/opensfm/src/sfm/pysfm.pyi +++ b/opensfm/src/sfm/pysfm.pyi @@ -2,36 +2,94 @@ # Do not manually edit # To regenerate: # $ buck run //mapillary/opensfm/opensfm/src/sfm:pysfm_stubgen -# Use proper mode, e.g. @arvr/mode/linux/dev for arvr # @generated +# +# Tip: Be sure to run this with the build mode you use for your project, e.g., +# @//arvr/mode/linux/opt (or dev) in arvr. +# +# Ignore errors for [24] untyped generics. +# pyre-ignore-all-errors[24] import opensfm.pybundle import opensfm.pygeometry import opensfm.pymap from typing import * -__all__ = [ -"BAHelpers", -"add_connections", -"count_tracks_per_shot", -"realign_maps", -"remove_connections" + +__all__ = [ + "BAHelpers", + "StaticExtensionLoader", + "add_connections", + "count_tracks_per_shot", + "filter_badly_conditioned_points", + "realign_maps", + "remove_connections", + "remove_isolated_points", ] + class BAHelpers: @staticmethod - def add_gcp_to_bundle(arg0: opensfm.pybundle.BundleAdjuster, arg1: opensfm.pymap.Map, arg2: List[opensfm.pymap.GroundControlPoint], arg3: dict) -> int: ... + def add_gcp_to_bundle( + arg0: opensfm.pybundle.BundleAdjuster, + arg1: opensfm.pymap.Map, + arg2: list[opensfm.pymap.GroundControlPoint], + arg3: dict, + ) -> int: ... @staticmethod - def bundle(arg0: opensfm.pymap.Map, arg1: Dict[str, opensfm.pygeometry.Camera], arg2: Dict[str, opensfm.pymap.RigCamera], arg3: List[opensfm.pymap.GroundControlPoint], arg4: dict) -> dict: ... + def bundle( + arg0: opensfm.pymap.Map, + arg1: dict[str, opensfm.pygeometry.Camera], + arg2: dict[str, opensfm.pymap.RigCamera], + arg3: list[opensfm.pymap.GroundControlPoint], + arg4: dict, + ) -> dict: ... @staticmethod - def bundle_local(arg0: opensfm.pymap.Map, arg1: Dict[str, opensfm.pygeometry.Camera], arg2: Dict[str, opensfm.pymap.RigCamera], arg3: List[opensfm.pymap.GroundControlPoint], arg4: str, arg5: dict) -> tuple: ... + def bundle_local( + arg0: opensfm.pymap.Map, + arg1: dict[str, opensfm.pygeometry.Camera], + arg2: dict[str, opensfm.pymap.RigCamera], + arg3: list[opensfm.pymap.GroundControlPoint], + arg4: str, + arg5: dict, + ) -> tuple: ... @staticmethod - def bundle_shot_poses(arg0: opensfm.pymap.Map, arg1: Set[str], arg2: Dict[str, opensfm.pygeometry.Camera], arg3: Dict[str, opensfm.pymap.RigCamera], arg4: dict) -> dict: ... + def bundle_shot_poses( + arg0: opensfm.pymap.Map, + arg1: set[str], + arg2: dict[str, opensfm.pygeometry.Camera], + arg3: dict[str, opensfm.pymap.RigCamera], + arg4: dict, + ) -> dict: ... @staticmethod - def bundle_to_map(arg0: opensfm.pybundle.BundleAdjuster, arg1: opensfm.pymap.Map, arg2: bool) -> None: ... + def bundle_to_map( + arg0: opensfm.pybundle.BundleAdjuster, arg1: opensfm.pymap.Map, arg2: bool + ) -> None: ... @staticmethod - def detect_alignment_constraints(arg0: opensfm.pymap.Map, arg1: dict, arg2: List[opensfm.pymap.GroundControlPoint]) -> str: ... + def detect_alignment_constraints( + arg0: opensfm.pymap.Map, + arg1: dict, + arg2: list[opensfm.pymap.GroundControlPoint], + ) -> str: ... @staticmethod - def shot_neighborhood_ids(arg0: opensfm.pymap.Map, arg1: str, arg2: int, arg3: int, arg4: int) -> Tuple[Set[str], Set[str]]: ... -def add_connections(arg0: opensfm.pymap.TracksManager, arg1: str, arg2: List[str]) -> None:... -def count_tracks_per_shot(arg0: opensfm.pymap.TracksManager, arg1: List[str], arg2: List[str]) -> Dict[str, int]:... -def realign_maps(arg0: opensfm.pymap.Map, arg1: opensfm.pymap.Map, arg2: bool) -> None:... -def remove_connections(arg0: opensfm.pymap.TracksManager, arg1: str, arg2: List[str]) -> None:... + def shot_neighborhood_ids( + arg0: opensfm.pymap.Map, arg1: str, arg2: int, arg3: int, arg4: int + ) -> tuple[set[str], set[str]]: ... + +class StaticExtensionLoader: + pass + +def add_connections( + arg0: opensfm.pymap.TracksManager, arg1: str, arg2: list[str] +) -> None: ... +def count_tracks_per_shot( + arg0: opensfm.pymap.TracksManager, arg1: list[str], arg2: list[str] +) -> dict[str, int]: ... +def filter_badly_conditioned_points( + map: opensfm.pymap.Map, min_angle_deg: float = 1.0, min_abs_det: float = 1e-15 +) -> int: ... +def realign_maps( + arg0: opensfm.pymap.Map, arg1: opensfm.pymap.Map, arg2: bool +) -> None: ... +def remove_connections( + arg0: opensfm.pymap.TracksManager, arg1: str, arg2: list[str] +) -> None: ... +def remove_isolated_points(map: opensfm.pymap.Map, k: int = 7) -> int: ... diff --git a/opensfm/src/sfm/python/pybind.cc b/opensfm/src/sfm/python/pybind.cc index 77466e1dd..4fd2bcd12 100644 --- a/opensfm/src/sfm/python/pybind.cc +++ b/opensfm/src/sfm/python/pybind.cc @@ -5,11 +5,10 @@ #include #include #include +#include #include #include -#include - PYBIND11_MODULE(pysfm, m) { py::module::import("opensfm.pymap"); py::module::import("opensfm.pygeometry"); @@ -18,7 +17,13 @@ PYBIND11_MODULE(pysfm, m) { m.def("count_tracks_per_shot", &sfm::tracks_helpers::CountTracksPerShot); m.def("add_connections", &sfm::tracks_helpers::AddConnections, py::call_guard()); - m.def("remove_connections", &sfm::tracks_helpers::RemoveConnections, + m.def("filter_badly_conditioned_points", + &sfm::map_helpers::FilterBadlyConditionedPoints, py::arg("map"), + py::arg("min_angle_deg") = 1.0, py::arg("min_abs_det") = 1e-15, + py::call_guard()); + + m.def("remove_isolated_points", &sfm::map_helpers::RemoveIsolatedPoints, + py::arg("map"), py::arg("k") = 7, py::call_guard()); py::class_(m, "BAHelpers") @@ -33,4 +38,9 @@ PYBIND11_MODULE(pysfm, m) { m.def("realign_maps", &sfm::retriangulation::RealignMaps, py::call_guard()); + + m.def("reconstruct_from_tracks_manager", + &sfm::retriangulation::ReconstructFromTracksManager, py::arg("map"), + py::arg("tracks_manager"), py::arg("config"), + py::call_guard()); } diff --git a/opensfm/src/sfm/retriangulation.h b/opensfm/src/sfm/retriangulation.h index 100a64ca0..531aed88d 100644 --- a/opensfm/src/sfm/retriangulation.h +++ b/opensfm/src/sfm/retriangulation.h @@ -1,10 +1,20 @@ #pragma once #include #include +#include -namespace sfm { -namespace retriangulation { +#include + +namespace py = pybind11; + +namespace sfm::retriangulation { void RealignMaps(const map::Map& reference, map::Map& to_align, bool update_points); -} // namespace retriangulation -} // namespace sfm +int Triangulate(map::Map& map, + const std::unordered_set& track_ids, + float reproj_threshold, float min_angle, float min_depth, + int processing_threads); +void ReconstructFromTracksManager(map::Map& map, + const map::TracksManager& tracks_manager, + const py::dict& config); +} // namespace sfm::retriangulation diff --git a/opensfm/src/sfm/src/ba_helpers.cc b/opensfm/src/sfm/src/ba_helpers.cc index 4e15575a2..57f75b7df 100644 --- a/opensfm/src/sfm/src/ba_helpers.cc +++ b/opensfm/src/sfm/src/ba_helpers.cc @@ -4,10 +4,12 @@ #include #include #include +#include #include #include #include +#include #include "geo/geo.h" #include "map/defines.h" @@ -118,17 +120,32 @@ py::tuple BAHelpers::BundleLocal( const std::unordered_map& rig_camera_priors, const AlignedVector& gcp, - const map::ShotId& central_shot_id, const py::dict& config) { + const map::ShotId& central_shot_id, int grid_size, const py::dict& config) { py::dict report; - const auto start = std::chrono::high_resolution_clock::now(); + + const auto timer_neighborhood_start = + std::chrono::high_resolution_clock::now(); auto neighborhood = ShotNeighborhood( map, central_shot_id, config["local_bundle_radius"].cast(), config["local_bundle_min_common_points"].cast(), config["local_bundle_max_shots"].cast()); + const auto timer_neighborhood_end = std::chrono::high_resolution_clock::now(); auto& interior = neighborhood.first; auto& boundary = neighborhood.second; + // Convert subset to set for fast lookup + std::unordered_set all_shots_interior; + for (auto* shot : interior) { + all_shots_interior.insert(shot->GetId()); + } + const auto timer_grid_start = std::chrono::high_resolution_clock::now(); + auto selection = SelectTracksGrid(map, all_shots_interior, grid_size); + const auto& subset = selection.selected_tracks; + auto& to_retriangulate = selection.other_tracks; + const auto timer_grid_end = std::chrono::high_resolution_clock::now(); + // set up BA + const auto start = std::chrono::high_resolution_clock::now(); auto ba = bundle::BundleAdjuster(); ba.SetUseAnalyticDerivatives( config["bundle_analytic_derivatives"].cast()); @@ -143,8 +160,6 @@ py::tuple BAHelpers::BundleLocal( std::unordered_set int_and_bound(interior.cbegin(), interior.cend()); int_and_bound.insert(boundary.cbegin(), boundary.cend()); - std::unordered_set points; - py::list pt_ids; constexpr bool point_constant{false}; constexpr bool rig_camera_constant{true}; @@ -215,33 +230,61 @@ py::tuple BAHelpers::BundleLocal( } } + double t_projections = 0; + const auto t_pts_start = std::chrono::high_resolution_clock::now(); + + // Retrieve a mapping between map shots and bundle shots we're just created + std::unordered_map shot_lookup; + shot_lookup.reserve(interior.size() + boundary.size()); for (auto* shot : interior) { - // Add all points of the shots that are in the interior - for (const auto& lm_obs : shot->GetLandmarkObservations()) { - auto* lm = lm_obs.first; - if (points.count(lm) == 0) { - points.insert(lm); - pt_ids.append(lm->id_); - ba.AddPoint(lm->id_, lm->GetGlobalPos(), point_constant); - } - const auto& obs = lm_obs.second; - ba.AddPointProjectionObservation(shot->id_, lm_obs.first->id_, obs.point, - obs.scale); - } + shot_lookup[shot] = ba.GetShotRaw(shot->id_); } for (auto* shot : boundary) { - for (const auto& lm_obs : shot->GetLandmarkObservations()) { - auto* lm = lm_obs.first; - if (points.count(lm) > 0) { - const auto& obs = lm_obs.second; - ba.AddPointProjectionObservation(shot->id_, lm_obs.first->id_, - obs.point, obs.scale); + shot_lookup[shot] = ba.GetShotRaw(shot->id_); + } + + // Run over selected tracks only and add all their observations + std::unordered_set points; + py::list pt_ids; + size_t added_landmarks = 0; + size_t added_reprojections = 0; + for (const auto& selected_track_id : subset) { + auto& lm = map.GetLandmark(selected_track_id); + + auto* ba_point = ba.AddPoint(lm.id_, lm.GetGlobalPos(), point_constant); + + points.insert(&lm); + pt_ids.append(lm.id_); + ++added_landmarks; + + for (const auto& obs_pair : lm.GetObservations()) { + auto* shot = obs_pair.first; + auto* obs = obs_pair.second; + + auto s_it = shot_lookup.find(shot); + if (s_it == shot_lookup.end()) { + throw std::runtime_error("Shot " + shot->id_ + + " not found in bundle adjuster"); } + ba.AddPointProjectionObservationRaw(s_it->second, ba_point, obs->point, + obs->scale, obs->depth_prior); + ++added_reprojections; } } + t_projections += std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - t_pts_start) + .count() / + 1000000.0; + if (config["bundle_use_gcp"].cast() && !gcp.empty()) { + const auto t_gcp_start = std::chrono::high_resolution_clock::now(); AddGCPToBundle(ba, map, gcp, config); + t_projections += + std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - t_gcp_start) + .count() / + 1000000.0; } ba.SetPointProjectionLossFunction( @@ -249,6 +292,7 @@ py::tuple BAHelpers::BundleLocal( config["loss_function_threshold"].cast()); ba.SetInternalParametersPriorSD( config["exif_focal_sd"].cast(), + config["aspect_ratio_sd"].cast(), config["principal_point_sd"].cast(), config["radial_distortion_k1_sd"].cast(), config["radial_distortion_k2_sd"].cast(), @@ -261,7 +305,7 @@ py::tuple BAHelpers::BundleLocal( ba.SetNumThreads(config["processes"].cast()); ba.SetMaxNumIterations(10); - ba.SetLinearSolverType("DENSE_SCHUR"); + ba.SetLinearSolverType("SPARSE_SCHUR"); const auto timer_setup = std::chrono::high_resolution_clock::now(); { @@ -281,13 +325,34 @@ py::tuple BAHelpers::BundleLocal( point->SetGlobalPos(pt.GetValue()); point->SetReprojectionErrors(pt.reprojection_errors); } + const auto timer_teardown = std::chrono::high_resolution_clock::now(); + sfm::retriangulation::Triangulate( + map, to_retriangulate, config["triangulation_threshold"].cast(), + config["triangulation_min_ray_angle"].cast(), + config["triangulation_min_depth"].cast(), + config["processes"].cast()); + const auto timer_triangulate = std::chrono::high_resolution_clock::now(); + report["brief_report"] = ba.BriefReport(); report["wall_times"] = py::dict(); + report["wall_times"]["neighborhood"] = + std::chrono::duration_cast( + timer_neighborhood_end - timer_neighborhood_start) + .count() / + 1000000.0; report["wall_times"]["setup"] = std::chrono::duration_cast(timer_setup - start) .count() / 1000000.0; + report["wall_times"]["setup_projections"] = t_projections; + report["wall_times"]["setup_other"] = + report["wall_times"]["setup"].cast() - t_projections; + report["wall_times"]["grid"] = + std::chrono::duration_cast(timer_grid_end - + timer_grid_start) + .count() / + 1000000.0; report["wall_times"]["run"] = std::chrono::duration_cast(timer_run - timer_setup) @@ -298,20 +363,27 @@ py::tuple BAHelpers::BundleLocal( timer_run) .count() / 1000000.0; + report["wall_times"]["triangulate"] = + std::chrono::duration_cast(timer_triangulate - + timer_teardown) + .count() / + 1000000.0; + report["num_images"] = interior.size(); report["num_interior_images"] = interior.size(); report["num_boundary_images"] = boundary.size(); report["num_other_images"] = map.NumberOfShots() - interior.size() - boundary.size(); + report["num_points"] = added_landmarks; + report["num_reprojections"] = added_reprojections; return py::make_tuple(pt_ids, report); } bool BAHelpers::TriangulateGCP( const map::GroundControlPoint& point, const std::unordered_map& shots, - Vec3d& coordinates) { - constexpr auto reproj_threshold{1.0}; + float reproj_threshold, Vec3d& coordinates) { constexpr auto min_ray_angle = 0.1 * M_PI / 180.0; - constexpr auto max_ray_angle = M_PI - min_ray_angle; + constexpr auto min_depth = 1e-3; // Assume GCPs 1mm+ away from the camera MatX3d os, bs; size_t added = 0; coordinates = Vec3d::Zero(); @@ -332,8 +404,8 @@ bool BAHelpers::TriangulateGCP( os.conservativeResize(added, Eigen::NoChange); if (added >= 2) { const std::vector thresholds(added, reproj_threshold); - const auto& res = geometry::TriangulateBearingsMidpoint(os, bs, thresholds, - min_ray_angle, max_ray_angle); + const auto& res = geometry::TriangulateBearingsMidpoint( + os, bs, thresholds, min_ray_angle, min_depth); coordinates = res.second; return res.first; } @@ -347,35 +419,41 @@ size_t BAHelpers::AddGCPToBundle( const auto& reference = map.GetTopocentricConverter(); const auto& shots = map.GetShots(); - const auto dominant_terms = ba.GetRigInstances().size() + - ba.GetProjectionsCount() + - ba.GetRelativeMotionsCount(); - - size_t total_terms = 0; - for (const auto& point : gcp) { - Vec3d coordinates; - if (TriangulateGCP(point, shots, coordinates) || !point.lla_.empty()) { - ++total_terms; - } - for (const auto& obs : point.observations_) { - total_terms += (shots.count(obs.shot_id_) > 0); - } - } - - double global_weight = config["gcp_global_weight"].cast() * - dominant_terms / std::max(1, total_terms); + const float reproj_threshold = + config["gcp_reprojection_error_threshold"].cast(); size_t added_gcp_observations = 0; for (const auto& point : gcp) { const auto point_id = "gcp-" + point.id_; Vec3d coordinates; - if (!TriangulateGCP(point, shots, coordinates)) { + if (!TriangulateGCP(point, shots, reproj_threshold, coordinates)) { if (!point.lla_.empty()) { coordinates = reference.ToTopocentric(point.GetLlaVec3d()); } else { continue; } } + + double avg_observations = 0.; + int valid_shots = 0; + for (const auto& obs : point.observations_) { + const auto shot_it = shots.find(obs.shot_id_); + if (shot_it != shots.end()) { + const auto& shot = (shot_it->second); + avg_observations += shot.GetLandmarkObservations().size(); + ++valid_shots; + } + } + + if (!valid_shots) { + continue; + } + avg_observations /= valid_shots; + const double weight_factor = std::sqrt(std::max(1.0, avg_observations)); + + const double prior_balance = std::max(1.0, (double)valid_shots); + const double prior_weight = config["gcp_global_weight"].cast() * + weight_factor * prior_balance; constexpr auto point_constant{false}; ba.AddPoint(point_id, coordinates, point_constant); if (!point.lla_.empty()) { @@ -383,16 +461,18 @@ size_t BAHelpers::AddGCPToBundle( config["gcp_horizontal_sd"].cast(), config["gcp_vertical_sd"].cast()); ba.AddPointPrior(point_id, reference.ToTopocentric(point.GetLlaVec3d()), - point_std / global_weight, point.has_altitude_); + point_std / prior_weight, point.has_altitude_); } // Now iterate through the observations + const double obs_weight = config["gcp_global_weight"].cast() * + weight_factor * prior_balance; for (const auto& obs : point.observations_) { const auto& shot_id = obs.shot_id_; if (shots.count(shot_id) > 0) { constexpr double scale{0.001}; ba.AddPointProjectionObservation(shot_id, point_id, obs.projection_, - scale / global_weight); + scale / obs_weight); ++added_gcp_observations; } } @@ -400,6 +480,79 @@ size_t BAHelpers::AddGCPToBundle( return added_gcp_observations; } +BAHelpers::TracksSelection BAHelpers::SelectTracksGrid( + map::Map& map, const std::unordered_set& shot_ids, + size_t grid_size) { + TracksSelection selection; + if (shot_ids.empty() || grid_size <= 1) { + return selection; + } + + const auto default_num_tracks = grid_size * grid_size * shot_ids.size() / 2; + auto& set_selected_tracks = selection.selected_tracks; + set_selected_tracks.reserve(default_num_tracks); + auto& set_other_tracks = selection.other_tracks; + set_other_tracks.reserve(default_num_tracks * 4); + + // Prepare grid cells: each cell holds the longest track (by observation + // count) + std::vector> grid(grid_size * grid_size, + {"", 0}); + + // For each shot (image) + for (const auto& shot_id : shot_ids) { + const auto& shot = map.GetShot(shot_id); + const int width = shot.GetCamera()->width; + const int height = shot.GetCamera()->height; + if (width <= 0 || height <= 0) { + continue; + } + + std::fill(grid.begin(), grid.end(), + std::make_pair("", 0)); + + // For each observation in the shot + for (const auto& lm_obs : shot.GetLandmarkObservations()) { + auto* lm = lm_obs.first; + const auto& obs = lm_obs.second; + set_other_tracks.insert(lm->id_); + + // Get normalized coordinates [0,1] + const auto normalize = std::max(width, height); + double x = (obs.point(0) * normalize + width * 0.5) / width; + double y = (obs.point(1) * normalize + height * 0.5) / height; + // Clamp to [0,1] + x = std::max(0.0, std::min(1.0, x)); + y = std::max(0.0, std::min(1.0, y)); + // Compute grid cell + const int gx = std::min(static_cast(x * grid_size), + static_cast(grid_size - 1)); + const int gy = std::min(static_cast(y * grid_size), + static_cast(grid_size - 1)); + // Track length = number of observations + const size_t track_len = lm->GetObservations().size(); + // Keep the longest track in this cell + if (track_len > grid[gx + gy * grid_size].second && + set_selected_tracks.count(lm->id_) == 0) { + grid[gx + gy * grid_size] = {lm->id_, track_len}; + } + } + + // Add selected tracks for this shot + for (int i = 0; i < static_cast(grid_size * grid_size); ++i) { + const auto& track_id = grid[i].first; + if (!track_id.empty()) { + set_selected_tracks.insert(track_id); + } + } + } + for (const auto& selected_track : set_selected_tracks) { + set_other_tracks.erase(selected_track); + } + + return selection; +} + py::dict BAHelpers::BundleShotPoses( map::Map& map, const std::unordered_set& shot_ids, const std::unordered_map& camera_priors, @@ -424,10 +577,16 @@ py::dict BAHelpers::BundleShotPoses( rig_instances_ids.insert(shot.GetRigInstanceId()); } std::unordered_set rig_cameras_ids; + std::unordered_set cameras_ids; for (const auto& rig_instance_id : rig_instances_ids) { auto& instance = map.GetRigInstance(rig_instance_id); for (const auto& shot_n_rig_camera : instance.GetRigCameras()) { - rig_cameras_ids.insert(shot_n_rig_camera.second->id); + const auto rig_camera_id = shot_n_rig_camera.second->id; + rig_cameras_ids.insert(rig_camera_id); + + const auto shot_id = shot_n_rig_camera.first; + const auto camera_id = map.GetShot(shot_id).GetCamera()->id; + cameras_ids.insert(camera_id); } } @@ -438,16 +597,10 @@ py::dict BAHelpers::BundleShotPoses( rig_camera_priors.at(rig_camera_id).pose, fix_rig_camera); } - std::unordered_set added_cameras; - for (const auto& shot_id : shot_ids) { - const auto& shot = map.GetShot(shot_id); - const auto& cam = *shot.GetCamera(); - if (added_cameras.find(cam.id) != added_cameras.end()) { - continue; - } - const auto& cam_prior = camera_priors.at(cam.id); - ba.AddCamera(cam.id, cam, cam_prior, fix_cameras); - added_cameras.insert(cam.id); + for (const auto& camera_id : cameras_ids) { + const auto& cam = map.GetCamera(camera_id); + const auto& cam_prior = camera_priors.at(camera_id); + ba.AddCamera(camera_id, cam, cam_prior, fix_cameras); } std::unordered_set landmarks; @@ -483,7 +636,7 @@ py::dict BAHelpers::BundleShotPoses( shot_cameras[shot_id] = shot.GetCamera()->id; shot_rig_cameras[shot_id] = shot_n_rig_camera.second->id; - const auto is_fixed = shot_ids.find(shot_id) != shot_ids.end(); + const auto is_fixed = shot_ids.find(shot_id) == shot_ids.end(); if (!is_fixed) { if (config["bundle_use_gps"].cast()) { const auto pos = shot.GetShotMeasurements().gps_position_; @@ -513,20 +666,27 @@ py::dict BAHelpers::BundleShotPoses( } // add observations + const auto t_projections_start = std::chrono::high_resolution_clock::now(); for (const auto& shot_id : shot_ids) { const auto& shot = map.GetShot(shot_id); for (const auto& lm_obs : shot.GetLandmarkObservations()) { const auto& obs = lm_obs.second; ba.AddPointProjectionObservation(shot.id_, lm_obs.first->id_, obs.point, - obs.scale); + obs.scale, obs.depth_prior); } } + const double t_projections = + std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - t_projections_start) + .count() / + 1000000.0; ba.SetPointProjectionLossFunction( config["loss_function"].cast(), config["loss_function_threshold"].cast()); ba.SetInternalParametersPriorSD( config["exif_focal_sd"].cast(), + config["aspect_ratio_sd"].cast(), config["principal_point_sd"].cast(), config["radial_distortion_k1_sd"].cast(), config["radial_distortion_k2_sd"].cast(), @@ -562,6 +722,9 @@ py::dict BAHelpers::BundleShotPoses( std::chrono::duration_cast(timer_setup - start) .count() / 1000000.0; + report["wall_times"]["setup_projections"] = t_projections; + report["wall_times"]["setup_other"] = + report["wall_times"]["setup"].cast() - t_projections; report["wall_times"]["run"] = std::chrono::duration_cast(timer_run - timer_setup) @@ -580,9 +743,22 @@ py::dict BAHelpers::Bundle( const std::unordered_map& camera_priors, const std::unordered_map& rig_camera_priors, - const AlignedVector& gcp, const py::dict& config) { + const AlignedVector& gcp, int grid_size, + const py::dict& config) { py::dict report; + // Get shot ids from the map + const auto& all_shots = map.GetShots(); + std::unordered_set shot_ids; + for (const auto& shot_pair : all_shots) { + shot_ids.insert(shot_pair.first); + } + const auto timer_grid_start = std::chrono::high_resolution_clock::now(); + auto selection = SelectTracksGrid(map, shot_ids, grid_size); + const auto& subset = selection.selected_tracks; + auto& to_retriangulate = selection.other_tracks; + const auto timer_grid_end = std::chrono::high_resolution_clock::now(); + auto ba = bundle::BundleAdjuster(); const bool fix_cameras = !config["optimize_camera_parameters"].cast(); ba.SetUseAnalyticDerivatives( @@ -596,9 +772,25 @@ py::dict BAHelpers::Bundle( ba.AddCamera(cam.id, cam, cam_prior, fix_cameras); } - for (const auto& pt_pair : map.GetLandmarks()) { - const auto& pt = pt_pair.second; - ba.AddPoint(pt.id_, pt.GetGlobalPos(), false); + // Only add points in the subset + std::unordered_map landmark_lookup; + landmark_lookup.reserve(subset.empty() ? map.GetLandmarks().size() + : subset.size()); + + // Two different - yet similar - loops to avoid + // one dummy structure allocation + if (!subset.empty()) { + for (const auto& track_id : subset) { + const auto& pt = map.GetLandmark(track_id); + ba.AddPoint(pt.id_, pt.GetGlobalPos(), false); + landmark_lookup[&pt] = ba.GetPointRaw(pt.id_); + } + } else { + for (const auto& lm_pair : map.GetLandmarks()) { + const auto& pt = lm_pair.second; + ba.AddPoint(pt.id_, pt.GetGlobalPos(), false); + landmark_lookup[&pt] = ba.GetPointRaw(pt.id_); + } } auto align_method = config["align_method"].cast(); @@ -621,8 +813,13 @@ py::dict BAHelpers::Bundle( // setup rig cameras constexpr size_t kMinRigInstanceForAdjust{10}; + const size_t shots_per_rig_cameras = + map.GetRigCameras().size() > 0 + ? static_cast(map.GetShots().size() / + map.GetRigCameras().size()) + : 1; const auto lock_rig_camera = - map.GetRigInstances().size() <= kMinRigInstanceForAdjust; + shots_per_rig_cameras <= kMinRigInstanceForAdjust; for (const auto& camera_pair : map.GetRigCameras()) { // could be set to false (not locked) the day we expose leverarm adjustment const bool is_leverarm = @@ -653,6 +850,13 @@ py::dict BAHelpers::Bundle( const auto pos = shot.GetShotMeasurements().gps_position_; const auto acc = shot.GetShotMeasurements().gps_accuracy_; if (pos.HasValue() && acc.HasValue()) { + if (acc.Value() <= 0) { + throw std::runtime_error( + "Shot " + shot.GetId() + + " has an accuracy <= 0: " + std::to_string(acc.Value()) + + ". Try modifying " + "your input parser to filter such values."); + } average_position += pos.Value(); average_std += acc.Value(); ++gps_count; @@ -672,28 +876,55 @@ py::dict BAHelpers::Bundle( } } + double t_projections = 0; + const auto t_obs_start = std::chrono::high_resolution_clock::now(); + size_t added_reprojections = 0; + + std::unordered_map shot_lookup; + shot_lookup.reserve(map.GetShots().size()); + for (const auto& shot_pair : map.GetShots()) { const auto& shot = shot_pair.second; - // that one doesn't have it's rig counterpart if (do_add_align_vector) { constexpr double std_dev = 1e-3; ba.AddAbsoluteUpVector(shot.id_, up_vector, std_dev); } + shot_lookup[&shot] = ba.GetShotRaw(shot.id_); + } - // setup observations for any shot type - for (const auto& lm_obs : shot.GetLandmarkObservations()) { - const auto& obs = lm_obs.second; - ba.AddPointProjectionObservation(shot.id_, lm_obs.first->id_, obs.point, - obs.scale); + for (const auto& lm_pair : landmark_lookup) { + const map::Landmark* lm = lm_pair.first; + bundle::Point* bp = lm_pair.second; + + for (const auto& obs_entry : lm->GetObservations()) { + map::Shot* shot = obs_entry.first; + const map::Observation* obs = obs_entry.second; + + auto s_it = shot_lookup.find(shot); + if (s_it != shot_lookup.end()) { + ba.AddPointProjectionObservationRaw(s_it->second, bp, obs->point, + obs->scale, obs->depth_prior); + ++added_reprojections; + } } } + t_projections += std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - t_obs_start) + .count() / + 1000000.0; if (config["bundle_use_gcp"].cast() && !gcp.empty()) { + const auto t_gcp_start = std::chrono::high_resolution_clock::now(); AddGCPToBundle(ba, map, gcp, config); + t_projections += + std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - t_gcp_start) + .count() / + 1000000.0; } - if (config["bundle_compensate_gps_bias"].cast()) { + if (config["bundle_compensate_gps_bias"].cast() && !gcp.empty()) { const auto& biases = map.GetBiases(); for (const auto& camera : map.GetCameras()) { ba.SetCameraBias(camera.first, biases.at(camera.first)); @@ -705,6 +936,7 @@ py::dict BAHelpers::Bundle( config["loss_function_threshold"].cast()); ba.SetInternalParametersPriorSD( config["exif_focal_sd"].cast(), + config["aspect_ratio_sd"].cast(), config["principal_point_sd"].cast(), config["radial_distortion_k1_sd"].cast(), config["radial_distortion_k2_sd"].cast(), @@ -728,14 +960,31 @@ py::dict BAHelpers::Bundle( const auto timer_run = std::chrono::high_resolution_clock::now(); BundleToMap(ba, map, !fix_cameras); - const auto timer_teardown = std::chrono::high_resolution_clock::now(); + + if (!subset.empty()) { + sfm::retriangulation::Triangulate( + map, to_retriangulate, config["triangulation_threshold"].cast(), + config["triangulation_min_ray_angle"].cast(), + config["triangulation_min_depth"].cast(), + config["processes"].cast()); + } + const auto timer_triangulate = std::chrono::high_resolution_clock::now(); + report["brief_report"] = ba.BriefReport(); report["wall_times"] = py::dict(); report["wall_times"]["setup"] = std::chrono::duration_cast(timer_setup - start) .count() / 1000000.0; + report["wall_times"]["setup_projections"] = t_projections; + report["wall_times"]["setup_other"] = + report["wall_times"]["setup"].cast() - t_projections; + report["wall_times"]["grid"] = + std::chrono::duration_cast(timer_grid_end - + timer_grid_start) + .count() / + 1000000.0; report["wall_times"]["run"] = std::chrono::duration_cast(timer_run - timer_setup) @@ -746,6 +995,15 @@ py::dict BAHelpers::Bundle( timer_run) .count() / 1000000.0; + report["wall_times"]["triangulate"] = + std::chrono::duration_cast(timer_triangulate - + timer_teardown) + .count() / + 1000000.0; + report["num_images"] = map.GetShots().size(); + report["num_points"] = + subset.empty() ? map.GetLandmarks().size() : subset.size(); + report["num_reprojections"] = added_reprojections; return report; } @@ -795,10 +1053,15 @@ void BAHelpers::BundleToMap(const bundle::BundleAdjuster& bundle_adjuster, // Update points for (auto& point : output_map.GetLandmarks()) { - const auto& pt = bundle_adjuster.GetPoint(point.first); + if (!bundle_adjuster.HasPoint(point.first)) { + continue; + } + auto pt = bundle_adjuster.GetPoint(point.first); if (!pt.GetValue().allFinite()) { - throw std::runtime_error("Point " + point.first + - " has either NaN or INF values."); + // set large reprojection errors + for (auto& proj_error : pt.reprojection_errors) { + proj_error.second.setConstant(1.0); + } } point.second.SetGlobalPos(pt.GetValue()); point.second.SetReprojectionErrors(pt.reprojection_errors); @@ -828,9 +1091,14 @@ void BAHelpers::AlignmentConstraints( // Triangulated vs measured points if (!gcp.empty() && config["bundle_use_gcp"].cast()) { for (const auto& point : gcp) { - if (point.lla_.empty()) continue; + if (point.lla_.empty()) { + continue; + } Vec3d coordinates; - if (TriangulateGCP(point, shots, coordinates)) { + if (TriangulateGCP( + point, shots, + config["gcp_reprojection_error_threshold"].cast(), + coordinates)) { Xp.row(idx) = topocentricConverter.ToTopocentric(point.GetLlaVec3d()); X.row(idx) = coordinates; ++idx; diff --git a/opensfm/src/sfm/src/map_helpers.cc b/opensfm/src/sfm/src/map_helpers.cc new file mode 100644 index 000000000..752c9d3c4 --- /dev/null +++ b/opensfm/src/sfm/src/map_helpers.cc @@ -0,0 +1,238 @@ +#include "sfm/map_helpers.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern "C" { +#include +} + +namespace sfm::map_helpers { + +int FilterBadlyConditionedPoints(map::Map& map, double min_angle_deg, + double min_abs_det) { + std::vector to_remove; + + // Cull based on stats on covariance's condition number + constexpr double kMaxCond = 1000; + constexpr double kSigmaMultiplier = 1.0; + std::vector> landmark_conds; + landmark_conds.reserve(map.GetLandmarks().size()); + + // Iterate over a snapshot of landmarks to decide removals. + for (const auto& kv : map.GetLandmarks()) { + const map::LandmarkId& lm_id = kv.first; + const map::Landmark& lm = kv.second; + + const auto& observations = lm.GetObservations(); + + std::vector cameras; + cameras.reserve(observations.size()); + std::vector poses; + poses.reserve(observations.size()); + std::vector obs_vec; + obs_vec.reserve(observations.size()); + + // Build camera/pose/observation vectors from observations. + for (const auto& obs_kv : observations) { + map::Shot* shot_ptr = obs_kv.first; + if (!shot_ptr) { + continue; + } + + const geometry::Pose* pose_ptr = shot_ptr->GetPose(); + const geometry::Camera* cam_ptr = shot_ptr->GetCamera(); + if (!pose_ptr || !cam_ptr) { + continue; + } + + // Get the shot's Observation for this landmark. + map::Observation* obs_ptr = + shot_ptr->GetLandmarkObservation(const_cast(&lm)); + if (!obs_ptr) { + continue; + } + + cameras.push_back(*cam_ptr); + poses.push_back(*pose_ptr); + obs_vec.push_back(obs_ptr->point); + } + + // First simple check based on angle between all pairs of raysmap + double rad_angle = min_angle_deg * M_PI / 180.0; + bool to_keep = false; + for (size_t i = 0; i < poses.size() && !to_keep; ++i) { + for (size_t j = i + 1; j < poses.size() && !to_keep; ++j) { + const auto ray1 = + (lm.GetGlobalPos() - poses[i].GetOrigin()).normalized(); + const auto ray2 = + (lm.GetGlobalPos() - poses[j].GetOrigin()).normalized(); + const double angle = geometry::AngleBetweenVectors(ray1, ray2); + if (angle > rad_angle) { + to_keep = true; + break; + } + } + } + if (!to_keep) { + to_remove.push_back(lm_id); + continue; + } + + // Compute inverse covariance for the point. + auto invcov_and_score = geometry::covariance::ComputePointInverseCovariance( + cameras, poses, obs_vec, lm.GetGlobalPos()); + const auto& invcov = invcov_and_score.first; + + // Basic numerical checks. + if (!invcov.allFinite()) { + to_remove.push_back(lm_id); + continue; + } + + // Determinant-based test. + const double det = invcov.determinant(); + if (!std::isfinite(det) || std::abs(det) < min_abs_det) { + to_remove.push_back(lm_id); + continue; + } + + // Eigenvalue-based test (condition number). + Eigen::SelfAdjointEigenSolver es(invcov.inverse()); + if (es.info() != Eigen::Success) { + to_remove.push_back(lm_id); + continue; + } + const auto& eigs = es.eigenvalues(); + if (!(eigs.array().isFinite().all())) { + to_remove.push_back(lm_id); + continue; + } + + const double max_eig = eigs.maxCoeff(); + const double min_eig = eigs.minCoeff(); + + // If any eigenvalue is non-positive or extremely small, mark removal. + if (min_eig <= 0.0 || !std::isfinite(min_eig)) { + to_remove.push_back(lm_id); + continue; + } + + const double cond = std::min(std::sqrt(max_eig / min_eig), kMaxCond); + if (!std::isfinite(cond)) { + to_remove.push_back(lm_id); + continue; + } + landmark_conds.emplace_back(lm_id, cond); + } + + // Compute median and MAD of condition numbers. + if (!landmark_conds.empty()) { + const double avg_cond = + std::accumulate( + landmark_conds.begin(), landmark_conds.end(), 0.0, + [](double sum, const auto& pair) { return sum + pair.second; }) / + landmark_conds.size(); + const double sigma = std::sqrt( + std::accumulate(landmark_conds.begin(), landmark_conds.end(), 0.0, + [avg_cond](double sum, const auto& pair) { + return sum + (pair.second - avg_cond) * + (pair.second - avg_cond); + }) / + landmark_conds.size()); + const double threshold = avg_cond + kSigmaMultiplier * sigma; + for (const auto& [lm_id, cond] : landmark_conds) { + if (cond > threshold) { + to_remove.push_back(lm_id); + } + } + } + + // Remove collected landmarks from the map. + int removed = 0; + for (const auto& lm_id : to_remove) { + if (map.HasLandmark(lm_id)) { + map.RemoveLandmark(lm_id); + ++removed; + } + } + + return removed; +} + +int RemoveIsolatedPoints(map::Map& map, int k) { + const auto& landmarks = map.GetLandmarks(); + if (landmarks.size() <= k) { + return 0; + } + + // Gather all positions + std::vector lm_ids; + std::vector positions; // flattened for VLFeat (x0,y0,z0,x1,y1,z1,...) + lm_ids.reserve(landmarks.size()); + positions.reserve(landmarks.size() * 3); + for (const auto& kv : landmarks) { + lm_ids.push_back(kv.first); + const Vec3d& pos = kv.second.GetGlobalPos(); + positions.push_back(static_cast(pos.x())); + positions.push_back(static_cast(pos.y())); + positions.push_back(static_cast(pos.z())); + } + + // Build VLFeat KD-tree + VlKDForest* forest = vl_kdforest_new(VL_TYPE_FLOAT, 3, 1, VlDistanceL2); + vl_kdforest_build(forest, lm_ids.size(), positions.data()); + + // Query kNN for each point in parallel + std::vector avg_dists(lm_ids.size(), 0.0); + std::vector neighbors(k + 1); + for (vl_size i = 0; i < lm_ids.size(); ++i) { + const float* query = positions.data() + 3 * i; + vl_kdforest_query(forest, neighbors.data(), k + 1, query); + + // neighbors[0] is always the query point itself (distance 0) + double sum = 0.0; + int found = 0; + for (int j = 1; j < k + 1; ++j) { + if (std::isfinite(neighbors[j].distance)) { + sum += neighbors[j].distance; + ++found; + } + } + avg_dists[i] = found > 0 ? sum / found : 0.0; + } + vl_kdforest_delete(forest); + + // Compute mean and stddev of average neighbor distances + const double mean = std::accumulate(avg_dists.begin(), avg_dists.end(), 0.0) / + avg_dists.size(); + const double std_dev = + std::sqrt(std::accumulate(avg_dists.begin(), avg_dists.end(), 0.0, + [mean](double sum, double val) { + return sum + (val - mean) * (val - mean); + }) / + avg_dists.size()); + + // Remove points whose avg kNN distance > mean + stddev + constexpr double kSigmaMultiplier = 1.25; + int removed = 0; + for (size_t i = 0; i < lm_ids.size(); ++i) { + if (avg_dists[i] > mean + kSigmaMultiplier * std_dev) { + if (map.HasLandmark(lm_ids[i])) { + map.RemoveLandmark(lm_ids[i]); + ++removed; + } + } + } + return removed; +} + +} // namespace sfm::map_helpers diff --git a/opensfm/src/sfm/src/retriangulation.cc b/opensfm/src/sfm/src/retriangulation.cc index 78c30c447..2c51a8ffd 100644 --- a/opensfm/src/sfm/src/retriangulation.cc +++ b/opensfm/src/sfm/src/retriangulation.cc @@ -1,12 +1,29 @@ +#include +#include +#include #include +#include #include +#include #include +#include +#include +#include #include -#include +#include +#include +#include -namespace sfm { -namespace retriangulation { +namespace { +struct TriangulationResult { + map::TrackId track_id; + Vec3d position; + std::vector> observations; +}; +} // namespace + +namespace sfm::retriangulation { void RealignMaps(const map::Map& map_from, map::Map& map_to, bool update_points) { const auto& map_from_shots = map_from.GetShots(); @@ -114,5 +131,226 @@ void RealignMaps(const map::Map& map_from, map::Map& map_to, map_to.RemoveShot(shot_id); } } -} // namespace retriangulation -} // namespace sfm + +void ReconstructFromTracksManager(map::Map& map, + const map::TracksManager& tracks_manager, + const py::dict& config) { + foundation::ScopedMallocArena scoped_malloc_arena; + + const float triangulation_threshold = + config["triangulation_threshold"].cast(); + const float min_angle = config["triangulation_min_ray_angle"].cast(); + const float min_depth = config["triangulation_min_depth"].cast(); + const int refinement_iterations = + config["triangulation_refinement_iterations"].cast(); + int processes = config["processes"].cast(); + const float min_angle_rad = min_angle * M_PI / 180.0; + + // Clear existing observations and landmarks + map.ClearObservationsAndLandmarks(); + + auto& shots = map.GetShots(); + auto track_ids = tracks_manager.GetTrackIds(); + + // Randomize track ids to avoid threads to only get invalid tracks + std::shuffle(track_ids.begin(), track_ids.end(), + std::mt19937{std::random_device{}()}); + + // Helper lambda to process a single track + auto process_track = + [&](const map::TrackId& track_id, + std::vector>& track_obs, + std::vector& thresholds, MatX3d& origins, + MatX3d& bearings) -> std::pair { + const auto obs_dict = tracks_manager.GetTrackObservations(track_id); + + track_obs.clear(); + track_obs.reserve(obs_dict.size()); + + for (const auto& kv : obs_dict) { + const auto it = shots.find(kv.first); + if (it != shots.end()) { + track_obs.emplace_back(&it->second, kv.second); + } + } + + if (track_obs.size() < 2) { + return {false, Vec3d::Zero()}; + } + + const size_t track_size = track_obs.size(); + if (thresholds.size() < track_size) { + thresholds.resize(track_size, triangulation_threshold); + } + + if (static_cast(origins.rows()) < track_size) { + origins.resize(track_size, 3); + bearings.resize(track_size, 3); + } + + for (size_t j = 0; j < track_size; ++j) { + origins.row(j) = track_obs[j].first->GetPose()->GetOrigin(); + bearings.row(j) = track_obs[j].first->Bearing(track_obs[j].second.point); + } + + auto res = geometry::TriangulateBearingsMidpoint( + origins.topRows(track_size), bearings.topRows(track_size), thresholds, + min_angle_rad, min_depth); + + if (res.first) { + Vec3d refined = geometry::PointRefinement( + origins.topRows(track_size), bearings.topRows(track_size), res.second, + refinement_iterations); + return {true, refined}; + } + return {false, Vec3d::Zero()}; + }; + + foundation::ConcurrentQueue queue; + std::atomic producers_active{0}; + + // 0 - Parallel track triangulation and valid observation collection per shot + // (per thread, then re-aggregated) +#pragma omp parallel num_threads(processes) + { + const int thread_id = omp_get_thread_num(); + const int num_threads = omp_get_num_threads(); + + // Reusable buffers per thread + std::vector thresholds; + MatX3d origins, bearings; + std::vector> track_obs; + + if (num_threads == 1) { + // Sequential fallback + for (const auto& track_id : track_ids) { + auto res = + process_track(track_id, track_obs, thresholds, origins, bearings); + if (res.first) { + auto& landmark = map.CreateLandmark(track_id, res.second); + for (const auto& shot_n_obs : track_obs) { + auto* new_obs = shot_n_obs.first->CreateObservation( + &landmark, shot_n_obs.second); + landmark.AddObservation(shot_n_obs.first, new_obs); + } + } + } + } else { + // Producer-Consumer Pattern +#pragma omp single + producers_active = num_threads - 1; + + if (thread_id == 0) { + // Consumer: Thread 0 + TriangulationResult res; + while (queue.Pop(res)) { + auto& landmark = map.CreateLandmark(res.track_id, res.position); + for (const auto& obs_pair : res.observations) { + auto* new_obs = + obs_pair.first->CreateObservation(&landmark, obs_pair.second); + landmark.AddObservation(obs_pair.first, new_obs); + } + } + } else { + // Producers: Threads 1..N-1 + // Manual loop distribution with stride + for (size_t i = thread_id - 1; i < track_ids.size(); + i += (num_threads - 1)) { + const auto& track_id = track_ids[i]; + auto res = + process_track(track_id, track_obs, thresholds, origins, bearings); + if (res.first) { + queue.Push({track_id, res.second, track_obs}); + } + } + + if (--producers_active == 0) { + queue.Finish(); + } + } + } + } +} + +int Triangulate(map::Map& map, + const std::unordered_set& track_ids, + float reproj_threshold, float min_angle, float min_depth, + int processing_threads) { + foundation::ScopedMallocArena scoped_malloc_arena; + + constexpr size_t kDefaultObservations = 10; + const float min_angle_rad = min_angle * M_PI / 180.0; + + int count = 0; + std::vector track_ids_vec(track_ids.begin(), track_ids.end()); + std::vector to_delete; +#pragma omp parallel num_threads(processing_threads) + { + std::vector thread_to_delete; + + std::vector threshold_list(kDefaultObservations, reproj_threshold); + MatX3d origins(kDefaultObservations, 3); + MatX3d bearings(kDefaultObservations, 3); +#pragma omp for schedule(static) + for (int i = 0; i < static_cast(track_ids_vec.size()); ++i) { + const auto& track_id = track_ids_vec.at(i); + if (!map.HasLandmark(track_id)) { + continue; + } + auto& landmark = map.GetLandmark(track_id); + const auto& observations = landmark.GetObservations(); + const size_t num_observations = observations.size(); + if (num_observations < 2) { + thread_to_delete.push_back(track_id); + continue; + } + + if (threshold_list.size() != num_observations) { + threshold_list.resize(num_observations, reproj_threshold); + } + if (origins.rows() != num_observations) { + origins.resize(num_observations, 3); + } + if (bearings.rows() != num_observations) { + bearings.resize(num_observations, 3); + } + + int idx = 0; + for (const auto& obs_pair : observations) { + const map::Shot* shot = obs_pair.first; + origins.row(idx) = shot->GetPose()->GetOrigin(); + bearings.row(idx) = shot->LandmarkBearing(&landmark); + ++idx; + } + + // Triangulate using midpoint method + const auto ok_pos = geometry::TriangulateBearingsMidpoint( + origins, bearings, threshold_list, min_angle_rad, min_depth); + if (!ok_pos.first) { + thread_to_delete.push_back(track_id); + } else { + landmark.SetGlobalPos(ok_pos.second); + + // Refine the triangulated point + const auto pos_refined = geometry::PointRefinement( + origins, bearings, landmark.GetGlobalPos(), 20); + landmark.SetGlobalPos(pos_refined); + } + } +#pragma omp critical + { + to_delete.insert(to_delete.end(), thread_to_delete.begin(), + thread_to_delete.end()); + } + } + + // Remove landmarks that were not triangulated + for (const auto& track_id : to_delete) { + if (map.HasLandmark(track_id)) { + map.RemoveLandmark(track_id); + } + } + return count; +} + +} // namespace sfm::retriangulation diff --git a/opensfm/src/sfm/src/tracks_helpers.cc b/opensfm/src/sfm/src/tracks_helpers.cc index 27e4ff967..c1ceda591 100644 --- a/opensfm/src/sfm/src/tracks_helpers.cc +++ b/opensfm/src/sfm/src/tracks_helpers.cc @@ -1,10 +1,10 @@ +#include #include -#include #include +#include -namespace sfm { -namespace tracks_helpers { +namespace sfm::tracks_helpers { std::unordered_map CountTracksPerShot( const map::TracksManager& manager, const std::vector& shots, const std::vector& tracks) { @@ -12,20 +12,41 @@ std::unordered_map CountTracksPerShot( for (const auto& track : tracks) { tracks_set.insert(track); } + std::unordered_map counts; - for (const auto& shot : shots) { - const auto& observations = manager.GetShotObservations(shot); - - int sum = 0; - for (const auto& obs : observations) { - const auto& trackID = obs.first; - if (tracks_set.find(trackID) == tracks_set.end()) { - continue; + for (int i = 0; i < shots.size(); ++i) { + counts[shots[i]] = 0; + } + +#pragma omp parallel + { + std::vector thread_counts(shots.size(), 0); +#pragma omp for + for (int i = 0; i < shots.size(); ++i) { + const auto& shot = shots[i]; + const auto observations = manager.GetShotObservations(shot); + + int sum = 0; + for (const auto& obs : observations) { + const auto& trackID = obs.first; + if (tracks_set.find(trackID) == tracks_set.end()) { + continue; + } + ++sum; + } + thread_counts[i] = sum; + } + +#pragma omp critical + { + for (int i = 0; i < shots.size(); ++i) { + if (thread_counts[i] > 0) { + counts[shots[i]] = thread_counts[i]; + } } - ++sum; } - counts[shot] = sum; } + return counts; } @@ -37,11 +58,4 @@ void AddConnections(map::TracksManager& manager, const map::ShotId& shot_id, } } -void RemoveConnections(map::TracksManager& manager, const map::ShotId& shot_id, - const std::vector& connections) { - for (const auto& connection : connections) { - manager.RemoveObservation(shot_id, connection); - } -} -} // namespace tracks_helpers -} // namespace sfm +} // namespace sfm::tracks_helpers diff --git a/opensfm/src/sfm/test/tracks_helpers_test.cc b/opensfm/src/sfm/test/tracks_helpers_test.cc index 7420d08bb..6acf66c79 100644 --- a/opensfm/src/sfm/test/tracks_helpers_test.cc +++ b/opensfm/src/sfm/test/tracks_helpers_test.cc @@ -18,11 +18,11 @@ class TracksHelpersTest : public ::testing::Test { track["2"] = o2; track["3"] = o3; - connections_add.push_back("1"); - connections_add.push_back("2"); - connections_add.push_back("3"); + connections_add.emplace_back("1"); + connections_add.emplace_back("2"); + connections_add.emplace_back("3"); - connections_remove.push_back("1"); + connections_remove.emplace_back("1"); } map::TracksManager manager; @@ -53,14 +53,4 @@ TEST_F(TracksHelpersTest, AddConnections) { EXPECT_EQ(3, counts.at("3")); } -TEST_F(TracksHelpersTest, RemoveConnections) { - sfm::tracks_helpers::RemoveConnections(manager, "1", connections_remove); - - const auto counts = - sfm::tracks_helpers::CountTracksPerShot(manager, {"1", "2", "3"}, {"1"}); - - EXPECT_EQ(0, counts.at("1")); - EXPECT_EQ(1, counts.at("2")); - EXPECT_EQ(1, counts.at("3")); -} } // namespace diff --git a/opensfm/src/sfm/tracks_helpers.h b/opensfm/src/sfm/tracks_helpers.h index 42e7f5f00..c969e2e13 100644 --- a/opensfm/src/sfm/tracks_helpers.h +++ b/opensfm/src/sfm/tracks_helpers.h @@ -7,16 +7,14 @@ #include #include -#include +#include +#include -namespace sfm { -namespace tracks_helpers { +namespace sfm::tracks_helpers { std::unordered_map CountTracksPerShot( const map::TracksManager& manager, const std::vector& shots, const std::vector& tracks); void AddConnections(map::TracksManager& manager, const map::ShotId& shot_id, const std::vector& connections); -void RemoveConnections(map::TracksManager& manager, const map::ShotId& shot_id, - const std::vector& connections); -} // namespace tracks_helpers -} // namespace sfm + +} // namespace sfm::tracks_helpers diff --git a/opensfm/src/testing_main.cc b/opensfm/src/testing_main.cc index c5bfdd004..33a5e122c 100644 --- a/opensfm/src/testing_main.cc +++ b/opensfm/src/testing_main.cc @@ -2,7 +2,7 @@ #include #include -int main(int argc, char **argv) { +int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); google::ParseCommandLineFlags(&argc, &argv, true); google::InitGoogleLogging(argv[0]); diff --git a/opensfm/src/third_party/akaze/lib/AKAZE.cpp b/opensfm/src/third_party/akaze/lib/AKAZE.cpp index 26472fea2..e5eb91bfd 100644 --- a/opensfm/src/third_party/akaze/lib/AKAZE.cpp +++ b/opensfm/src/third_party/akaze/lib/AKAZE.cpp @@ -165,8 +165,9 @@ int AKAZE::Create_Nonlinear_Scale_Space(const cv::Mat& img) { } // Perform FED n inner steps - for (int j = 0; j < nsteps_[i-1]; j++) + for (int j = 0; j < nsteps_[i-1]; j++) { nld_step_scalar(evolution_[i].Lt, evolution_[i].Lflow, evolution_[i].Lstep, tsteps_[i-1][j]); + } } t2 = cv::getTickCount(); @@ -241,8 +242,9 @@ void AKAZE::Compute_Determinant_Hessian_Response() { const float* lxy = evolution_[i].Lxy.ptr(ix); const float* lyy = evolution_[i].Lyy.ptr(ix); float* ldet = evolution_[i].Ldet.ptr(ix); - for (int jx = 0; jx < evolution_[i].Ldet.cols; jx++) + for (int jx = 0; jx < evolution_[i].Ldet.cols; jx++) { ldet[jx] = lxx[jx]*lyy[jx]-lxy[jx]*lxy[jx]; + } } } } @@ -267,7 +269,7 @@ void AKAZE::Find_Scale_Space_Extrema(std::vector& kpts) { double t1 = 0.0, t2 = 0.0; float value = 0.0; float dist = 0.0, ratio = 0.0, smax = 0.0; - int npoints = 0, id_repeated = 0; + int id_repeated = 0; int sigma_size_ = 0, left_x = 0, right_x = 0, up_y = 0, down_y = 0; bool is_extremum = false, is_repeated = false, is_out = false; cv::KeyPoint point; @@ -355,7 +357,6 @@ void AKAZE::Find_Scale_Space_Extrema(std::vector& kpts) { point.pt.x *= ratio; point.pt.y *= ratio; kpts_aux.push_back(point); - npoints++; } else { point.pt.x *= ratio; @@ -373,17 +374,17 @@ void AKAZE::Find_Scale_Space_Extrema(std::vector& kpts) { for (size_t i = 0; i < kpts_aux.size(); i++) { is_repeated = false; - const cv::KeyPoint& point = kpts_aux[i]; + const cv::KeyPoint& point_2 = kpts_aux[i]; for (size_t j = i+1; j < kpts_aux.size(); j++) { // Compare response with the upper scale - if ((point.class_id+1) == kpts_aux[j].class_id) { + if ((point_2.class_id+1) == kpts_aux[j].class_id) { - dist = (point.pt.x-kpts_aux[j].pt.x)*(point.pt.x-kpts_aux[j].pt.x) + - (point.pt.y-kpts_aux[j].pt.y)*(point.pt.y-kpts_aux[j].pt.y); + dist = (point_2.pt.x-kpts_aux[j].pt.x)*(point_2.pt.x-kpts_aux[j].pt.x) + + (point_2.pt.y-kpts_aux[j].pt.y)*(point_2.pt.y-kpts_aux[j].pt.y); - if (dist <= point.size*point.size) { - if (point.response < kpts_aux[j].response) { + if (dist <= point_2.size*point_2.size) { + if (point_2.response < kpts_aux[j].response) { is_repeated = true; break; } @@ -391,8 +392,9 @@ void AKAZE::Find_Scale_Space_Extrema(std::vector& kpts) { } } - if (is_repeated == false) - kpts.push_back(point); + if (is_repeated == false) { + kpts.push_back(point_2); + } } // Keep only the k-best keypoints @@ -403,12 +405,11 @@ void AKAZE::Find_Scale_Space_Extrema(std::vector& kpts) { std::vector > radiuses(kpts.size()); for (size_t i = 0; i < kpts.size(); ++i) { - float radius = 99999999999; + float radius = 99999999999.f; for (size_t j = 0; j < kpts.size(); ++j) { if (kpts[i].response < kpts[j].response) { float dx_ = kpts[j].pt.x - kpts[i].pt.x; float dy_ = kpts[j].pt.y - kpts[i].pt.y; - float sigma_ = kpts[j].size; float radius_ = dx_ * dx_ + dy_ * dy_; // TODO(pau) use sigma to compute a 3d radius if (radius_ < radius) { radius = radius_; @@ -427,7 +428,7 @@ void AKAZE::Find_Scale_Space_Extrema(std::vector& kpts) { } else { // Non-adapting suppression: keep k best response. std::sort(kpts.begin(), kpts.end(), compareKeyPointResponse); - kpts.resize(options_.target_num_features); + kpts.resize(options_.target_num_features); } } std::cout << "Keeping " << kpts.size() << " out of " << num_features_before << "\n"; @@ -583,10 +584,11 @@ void AKAZE::Compute_Descriptors(std::vector& kpts, cv::Mat& desc) #pragma omp parallel for #endif for (int i = 0; i < (int)(kpts.size()); i++) { - if (options_.descriptor_size == 0) + if (options_.descriptor_size == 0) { Get_Upright_MLDB_Full_Descriptor(kpts[i], desc.ptr(i)); - else + } else { Get_Upright_MLDB_Descriptor_Subset(kpts[i], desc.ptr(i)); + } } } break; @@ -597,10 +599,11 @@ void AKAZE::Compute_Descriptors(std::vector& kpts, cv::Mat& desc) #endif for (int i = 0; i < (int)(kpts.size()); i++) { Compute_Main_Orientation(kpts[i]); - if (options_.descriptor_size == 0) + if (options_.descriptor_size == 0) { Get_MLDB_Full_Descriptor(kpts[i], desc.ptr(i)); - else + } else { Get_MLDB_Descriptor_Subset(kpts[i], desc.ptr(i)); + } } } break; @@ -843,8 +846,9 @@ void AKAZE::Get_SURF_Descriptor_64(const cv::KeyPoint& kpt, float *desc) const { // convert to unit vector len = sqrt(len); - for (int i = 0; i < dsize; i++) + for (int i = 0; i < dsize; i++) { desc[i] /= len; + } } /* ************************************************************************* */ @@ -954,8 +958,9 @@ void AKAZE::Get_MSURF_Upright_Descriptor_64(const cv::KeyPoint& kpt, float *desc // convert to unit vector len = sqrt(len); - for (int i = 0; i < dsize; i++) - desc[i] /= len; + for (int i_2 = 0; i_2 < dsize; i_2++) { + desc[i_2] /= len; + } } /* ************************************************************************* */ @@ -1069,8 +1074,9 @@ void AKAZE::Get_MSURF_Descriptor_64(const cv::KeyPoint& kpt, float *desc) const // convert to unit vector len = sqrt(len); - for (int i = 0; i < dsize; i++) - desc[i] /= len; + for (int i_2 = 0; i_2 < dsize; i_2++) { + desc[i_2] /= len; + } } /* ************************************************************************* */ @@ -1170,11 +1176,13 @@ void AKAZE::MLDB_Fill_Values(float* values, int sample_step, int level, values[valpos] = di; - if (nr_channels > 1) + if (nr_channels > 1) { values[valpos + 1] = dx; + } - if (nr_channels > 2) + if (nr_channels > 2) { values[valpos + 2] = dy; + } valpos += nr_channels; } @@ -1229,11 +1237,13 @@ void AKAZE::MLDB_Fill_Upright_Values(float* values, int sample_step, int level, values[valpos] = di; - if (nr_channels > 1) + if (nr_channels > 1) { values[valpos + 1] = dx; + } - if (nr_channels > 2) + if (nr_channels > 2) { values[valpos + 2] = dy; + } valpos += nr_channels; } @@ -1569,16 +1579,19 @@ void libAKAZE::generateDescriptorSubsample(cv::Mat& sampleList, cv::Mat& compari /* ************************************************************************* */ void libAKAZE::check_descriptor_limits(int &x, int &y, int width, int height) { - if (x < 0) + if (x < 0) { x = 0; + } - if (y < 0) + if (y < 0) { y = 0; + } - if (x > width-1) + if (x > width-1) { x = width-1; + } - if (y > height-1) + if (y > height-1) { y = height-1; + } } - diff --git a/opensfm/src/third_party/akaze/lib/fed.cpp b/opensfm/src/third_party/akaze/lib/fed.cpp index 7973af6b8..8989c2463 100644 --- a/opensfm/src/third_party/akaze/lib/fed.cpp +++ b/opensfm/src/third_party/akaze/lib/fed.cpp @@ -66,14 +66,16 @@ int fed_tau_internal(const int n, const float scale, const float tau_max, float c = 0.0, d = 0.0; // Time savers vector tauh; // Helper vector for unsorted taus - if (n <= 0) + if (n <= 0) { return 0; + } // Allocate memory for the time step size tau = vector(n); - if (reordering) + if (reordering) { tauh = vector(n); + } // Compute time saver c = 1.0f / (4.0f * (float)n + 2.0f); @@ -83,10 +85,11 @@ int fed_tau_internal(const int n, const float scale, const float tau_max, for (int k = 0; k < n; ++k) { float h = cos(M_PI * (2.0f * (float)k + 1.0f) * c); - if (reordering) + if (reordering) { tauh[k] = d / (h * h); - else + } else { tau[k] = d / (h * h); + } } // Permute list of time steps according to chosen reordering function diff --git a/opensfm/src/third_party/akaze/lib/nldiffusion_functions.cpp b/opensfm/src/third_party/akaze/lib/nldiffusion_functions.cpp index 1fdab7b95..5fa764094 100644 --- a/opensfm/src/third_party/akaze/lib/nldiffusion_functions.cpp +++ b/opensfm/src/third_party/akaze/lib/nldiffusion_functions.cpp @@ -33,11 +33,13 @@ void gaussian_2D_convolution(const cv::Mat& src, cv::Mat& dst, size_t ksize_x, } // The kernel size must be and odd number - if ((ksize_x % 2) == 0) + if ((ksize_x % 2) == 0) { ksize_x += 1; + } - if ((ksize_y % 2) == 0) + if ((ksize_y % 2) == 0) { ksize_y += 1; + } // Perform the Gaussian Smoothing with border replication cv::GaussianBlur(src, dst, cv::Size(ksize_x, ksize_y), sigma, sigma, cv::BORDER_REPLICATE); @@ -58,8 +60,9 @@ void pm_g1(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, const float k) { const float* Lx_row = Lx.ptr(y); const float* Ly_row = Ly.ptr(y); float* dst_row = dst.ptr(y); - for (int x = 0; x < sz.width; x++) + for (int x = 0; x < sz.width; x++) { dst_row[x] = (-inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x])); + } } cv::exp(dst, dst); @@ -74,8 +77,9 @@ void pm_g2(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, const float k) { const float* Lx_row = Lx.ptr(y); const float* Ly_row = Ly.ptr(y); float* dst_row = dst.ptr(y); - for (int x = 0; x < sz.width; x++) + for (int x = 0; x < sz.width; x++) { dst_row[x] = 1.0 / (1.0+inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x])); + } } } @@ -130,8 +134,9 @@ float compute_k_percentile(const cv::Mat& img, float perc, float gscale, cv::Mat Ly = cv::Mat::zeros(img.rows, img.cols, CV_32F); // Set the histogram to zero - for (size_t i = 0; i < nbins; i++) + for (size_t i = 0; i < nbins; i++) { hist[i] = 0.0; +} // Perform the Gaussian convolution gaussian_2D_convolution(img, gaussian, ksize_x, ksize_y, gscale); @@ -151,8 +156,9 @@ float compute_k_percentile(const cv::Mat& img, float perc, float gscale, modg = sqrt(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]); // Get the maximum - if (modg > hmax) + if (modg > hmax) { hmax = modg; + } } } @@ -183,13 +189,15 @@ float compute_k_percentile(const cv::Mat& img, float perc, float gscale, // Now find the perc of the histogram percentile nthreshold = (size_t)(npoints*perc); - for (k = 0; nelements < nthreshold && k < nbins; k++) + for (k = 0; nelements < nthreshold && k < nbins; k++) { nelements = nelements + hist[k]; + } - if (nelements < nthreshold) + if (nelements < nthreshold) { kperc = 0.03; - else + } else { kperc = hmax*((float)(k)/(float)nbins); + } delete [] hist; return kperc; @@ -263,11 +271,11 @@ void nld_step_scalar(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, const float Ld_row_p = Ld.ptr(Lstep.rows-2); Lstep_row = Lstep.ptr(Lstep.rows-1); - for (int x = 1; x < Lstep.cols-1; x++) { - float xpos = (c_row[x]+c_row[x+1])*(Ld_row[x+1]-Ld_row[x]); - float xneg = (c_row[x-1]+c_row[x])*(Ld_row[x]-Ld_row[x-1]); - float ypos = (c_row[x]+c_row_p[x])*(Ld_row_p[x]-Ld_row[x]); - Lstep_row[x] = 0.5*stepsize*(xpos-xneg + ypos); + for (int x_2 = 1; x_2 < Lstep.cols-1; x_2++) { + float xpos_2 = (c_row[x_2]+c_row[x_2+1])*(Ld_row[x_2+1]-Ld_row[x_2]); + float xneg_2 = (c_row[x_2-1]+c_row[x_2])*(Ld_row[x_2]-Ld_row[x_2-1]); + float ypos_2 = (c_row[x_2]+c_row_p[x_2])*(Ld_row_p[x_2]-Ld_row[x_2]); + Lstep_row[x_2] = 0.5*stepsize*(xpos_2-xneg_2 + ypos_2); } xpos = (c_row[0]+c_row[1])*(Ld_row[1]-Ld_row[0]); @@ -282,31 +290,31 @@ void nld_step_scalar(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, const float // First and last columns for (int i = 1; i < Lstep.rows-1; i++) { - const float* c_row = c.ptr(i); + const float* c_row_2 = c.ptr(i); const float* c_row_m = c.ptr(i-1); - const float* c_row_p = c.ptr(i+1); - float* Ld_row = Ld.ptr(i); - float* Ld_row_p = Ld.ptr(i+1); + const float* c_row_p_2 = c.ptr(i+1); + float* Ld_row_2 = Ld.ptr(i); + float* Ld_row_p_2 = Ld.ptr(i+1); float* Ld_row_m = Ld.ptr(i-1); Lstep_row = Lstep.ptr(i); - float xpos = (c_row[0]+c_row[1])*(Ld_row[1]-Ld_row[0]); - float ypos = (c_row[0]+c_row_p[0])*(Ld_row_p[0]-Ld_row[0]); - float yneg = (c_row_m[0]+c_row[0])*(Ld_row[0]-Ld_row_m[0]); - Lstep_row[0] = 0.5*stepsize*(xpos+ypos-yneg); + float xpos_2 = (c_row_2[0]+c_row_2[1])*(Ld_row_2[1]-Ld_row_2[0]); + float ypos_2 = (c_row_2[0]+c_row_p_2[0])*(Ld_row_p_2[0]-Ld_row_2[0]); + float yneg = (c_row_m[0]+c_row_2[0])*(Ld_row_2[0]-Ld_row_m[0]); + Lstep_row[0] = 0.5*stepsize*(xpos_2+ypos_2-yneg); - float xneg = (c_row[Lstep.cols-2]+c_row[Lstep.cols-1])*(Ld_row[Lstep.cols-1]-Ld_row[Lstep.cols-2]); - ypos = (c_row[Lstep.cols-1]+c_row_p[Lstep.cols-1])*(Ld_row_p[Lstep.cols-1]-Ld_row[Lstep.cols-1]); - yneg = (c_row_m[Lstep.cols-1]+c_row[Lstep.cols-1])*(Ld_row[Lstep.cols-1]-Ld_row_m[Lstep.cols-1]); - Lstep_row[Lstep.cols-1] = 0.5*stepsize*(-xneg+ypos-yneg); + float xneg_2 = (c_row_2[Lstep.cols-2]+c_row_2[Lstep.cols-1])*(Ld_row_2[Lstep.cols-1]-Ld_row_2[Lstep.cols-2]); + ypos_2 = (c_row_2[Lstep.cols-1]+c_row_p_2[Lstep.cols-1])*(Ld_row_p_2[Lstep.cols-1]-Ld_row_2[Lstep.cols-1]); + yneg = (c_row_m[Lstep.cols-1]+c_row_2[Lstep.cols-1])*(Ld_row_2[Lstep.cols-1]-Ld_row_m[Lstep.cols-1]); + Lstep_row[Lstep.cols-1] = 0.5*stepsize*(-xneg_2+ypos_2-yneg); } // Ld = Ld + Lstep for (int y = 0; y < Lstep.rows; y++) { - float* Ld_row = Ld.ptr(y); - float* Lstep_row = Lstep.ptr(y); - for (int x = 0; x < Lstep.cols; x++) { - Ld_row[x] = Ld_row[x] + Lstep_row[x]; + float* Ld_row_2 = Ld.ptr(y); + float* Lstep_row_2 = Lstep.ptr(y); + for (int x_2 = 0; x_2 < Lstep.cols; x_2++) { + Ld_row_2[x_2] = Ld_row_2[x_2] + Lstep_row_2[x_2]; } } } @@ -343,8 +351,9 @@ void compute_derivative_kernels(cv::OutputArray kx_, cv::OutputArray ky_, int order = k == 0 ? dx : dy; float kerI[1000]; - for (int t = 0; t(i,j) < aux) + if (src.at(i,j) < aux) { aux = src.at(i,j); + } } } value = aux; @@ -49,8 +50,9 @@ void compute_max_32F(const cv::Mat &src, float& value) { float aux = 0.0; for (int i = 0; i < src.rows; i++) { for (int j = 0; j < src.cols; j++) { - if (src.at(i,j) > aux) + if (src.at(i,j) > aux) { aux = src.at(i,j); + } } } value = aux; @@ -184,10 +186,11 @@ void compute_inliers_ransac(const std::vector& matches, } if (npoints > 8) { - if (use_fund == true) + if (use_fund == true) { H = cv::findFundamentalMat(points1,points2,cv::FM_RANSAC,error,0.99,status); - else + } else { H = cv::findHomography(points1,points2,cv::RANSAC,error,status); + } for (int i = 0; i < npoints; i++) { if (status.at(i) == 1) { @@ -326,12 +329,13 @@ void draw_inliers(const cv::Mat& img1, const cv::Mat& img2, cv::Mat& img_com, x2 = (int)(ptpairs[i+1].x*ufactor+img1.cols+.5); y2 = (int)(ptpairs[i+1].y*vfactor+.5); - if (color == 0) + if (color == 0) { cv::line(img_com, cv::Point(x1,y1), cv::Point(x2,y2), cv::Scalar(255,255,0), 2); - else if (color == 1) + } else if (color == 1) { cv::line(img_com, cv::Point(x1,y1), cv::Point(x2,y2), cv::Scalar(255,0,0), 2); - else if (color == 2) + } else if (color == 2) { cv::line(img_com, cv::Point(x1,y1), cv::Point(x2,y2), cv::Scalar(0,0,255), 2); + } } } @@ -351,8 +355,9 @@ bool read_homography(const string& hFile, cv::Mat& H1toN) { ifstream pf; pf.open(filename.c_str(), std::ifstream::in); - if (!pf.is_open()) + if (!pf.is_open()) { return false; + } pf.getline(tmp_buf,tmp_buf_size); sscanf(tmp_buf,"%f %f %f",&h11,&h12,&h13); @@ -387,12 +392,6 @@ static inline std::ostream& cout_help() { return cout; } -/* ************************************************************************* */ -static inline std::string toUpper(std::string s) { - std::transform(s.begin(), s.end(), s.begin(), ::toupper); - return s; -} - /* ************************************************************************* */ void show_input_options_help(int example) { @@ -409,12 +408,12 @@ void show_input_options_help(int example) { else if (example == 2) { cout << "./akaze_compare img1.jpg img2.pgm [homography.txt] [options]" << endl; } - + cout << endl; cout_help() << "homography.txt is optional. If the txt file is not provided a planar homography will be estimated using RANSAC" << endl; cout << endl; - cout_help() << "Options below are not mandatory. Unless specified, default arguments are used." << endl << endl; + cout_help() << "Options below are not mandatory. Unless specified, default arguments are used." << endl << endl; // Justify on the left cout << left; diff --git a/opensfm/src/third_party/vlfeat/CMakeLists.txt b/opensfm/src/third_party/vlfeat/CMakeLists.txt index cf506d75a..0be13e5ef 100644 --- a/opensfm/src/third_party/vlfeat/CMakeLists.txt +++ b/opensfm/src/third_party/vlfeat/CMakeLists.txt @@ -1,5 +1,14 @@ file(GLOB VLFEAT_SRCS vl/*.c vl/*.h) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-absolute-value") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-but-set-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-logical-not-parentheses") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-tautological-constant-out-of-range-compare") + +if( ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64" ) + add_definitions( -DVL_DISABLE_SSE2 ) +endif() + if(WIN32) add_definitions(-D__SSE2__) endif() diff --git a/opensfm/src/third_party/vlfeat/vl/covdet.c b/opensfm/src/third_party/vlfeat/vl/covdet.c index 0b0592f0d..e77a85605 100644 --- a/opensfm/src/third_party/vlfeat/vl/covdet.c +++ b/opensfm/src/third_party/vlfeat/vl/covdet.c @@ -975,19 +975,6 @@ _vl_resize_buffer (void ** buffer, vl_size * bufferSize, vl_size targetSize) { } } -/** @brief Enlarge buffer - ** @param buffer - ** @param bufferSize - ** @param targetSize - ** @return error code - **/ - -static int -_vl_enlarge_buffer (void ** buffer, vl_size * bufferSize, vl_size targetSize) { - if (*bufferSize >= targetSize) return VL_ERR_OK ; - return _vl_resize_buffer(buffer,bufferSize,targetSize) ; -} - /* ---------------------------------------------------------------- */ /* Finding local extrema */ /* ---------------------------------------------------------------- */ diff --git a/opensfm/stats.py b/opensfm/stats.py index 9ab5d1ecc..af61c56e7 100644 --- a/opensfm/stats.py +++ b/opensfm/stats.py @@ -1,7 +1,9 @@ +# pyre-strict import datetime import math import os import random +import cv2 import statistics import json from collections import defaultdict @@ -9,20 +11,22 @@ from itertools import product import random from typing import Dict, List, Tuple, Optional, Any +from typing import Any, Callable, Dict, List, Optional, Tuple import matplotlib as mpl import matplotlib.cm as cm import matplotlib.colors as colors import matplotlib.pyplot as plt import numpy as np -from opensfm import io, multiview, feature_loader, pymap, types, pygeometry +from numpy.typing import NDArray +from opensfm import feature_loader, geometry, io, multiview, pygeometry, pymap, types from opensfm.dataset import DataSet, DataSetBase from opensfm import features RESIDUAL_PIXEL_CUTOFF = 4 -def _norm2d(point: np.ndarray) -> float: +def _norm2d(point: NDArray) -> float: return math.sqrt(point[0] * point[0] + point[1] * point[1]) @@ -38,7 +42,7 @@ def _length_histogram( return list(hist.keys()), list(hist.values()) -def _gps_errors(reconstruction: types.Reconstruction) -> List[np.ndarray]: +def _gps_errors(reconstruction: types.Reconstruction) -> List[NDArray]: errors = [] for shot in reconstruction.shots.values(): if shot.metadata.gps_position.has_value: @@ -77,7 +81,7 @@ def _gps_accuracy(reconstructions): else: return 15.0 -def _gps_gcp_errors_stats(errors: Optional[np.ndarray]) -> Dict[str, Any]: +def _gps_gcp_opk_errors_stats(errors: Optional[NDArray], names: List[str]) -> Dict[str, Any]: if errors is None or len(errors) == 0: return {} @@ -88,12 +92,13 @@ def _gps_gcp_errors_stats(errors: Optional[np.ndarray]) -> Dict[str, Any]: std_dev = np.std(errors, 0) average = np.average(np.linalg.norm(errors, axis=1)) - stats["mean"] = {"x": mean[0], "y": mean[1], "z": mean[2]} - stats["std"] = {"x": std_dev[0], "y": std_dev[1], "z": std_dev[2]} + stats["mean"] = {names[0]: mean[0], names[1]: mean[1], names[2]: mean[2]} + stats["std"] = {names[0]: std_dev[0], names[1] + : std_dev[1], names[2]: std_dev[2]} stats["error"] = { - "x": math.sqrt(m_squared[0]), - "y": math.sqrt(m_squared[1]), - "z": math.sqrt(m_squared[2]), + names[0]: math.sqrt(m_squared[0]), + names[1]: math.sqrt(m_squared[1]), + names[2]: math.sqrt(m_squared[2]), } stats["average_error"] = average @@ -206,7 +211,7 @@ def td_errors(data: DataSetBase, tracks_manager, reconstructions): if len(ray_errors) > 0: errors.append((np.max(np.array(ray_errors), axis=0))) - return _gps_gcp_errors_stats(errors) + return _gps_gcp_opk_errors_stats(errors, ["x", "y", "z"]) def gps_errors(reconstructions: List[types.Reconstruction]) -> Dict[str, Any]: @@ -214,7 +219,33 @@ def gps_errors(reconstructions: List[types.Reconstruction]) -> Dict[str, Any]: for rec in reconstructions: all_errors += _gps_errors(rec) - return _gps_gcp_errors_stats(np.array(all_errors)) + return _gps_gcp_opk_errors_stats(np.array(all_errors), ["x", "y", "z"]) + + +def _opk_errors(reconstruction: types.Reconstruction) -> List[NDArray]: + errors = [] + for shot in reconstruction.shots.values(): + if shot.metadata.opk_angles.has_value: + opk_exif = np.array(shot.metadata.opk_angles.value) + rotation_computed = shot.pose.get_rotation_matrix() + + # Extract OPK from computed rotation + opk_computed = np.degrees( + np.array(geometry.opk_from_rotation(rotation_computed)) + ) + + # Compute difference per-angle and normalize to [-180, 180] + opk_diff = opk_computed - opk_exif + opk_diff = (opk_diff + 180) % 360 - 180 + errors.append(opk_diff) + return errors + + +def opk_errors(reconstructions: List[types.Reconstruction]) -> Dict[str, Any]: + all_errors = [] + for rec in reconstructions: + all_errors += _opk_errors(rec) + return _gps_gcp_opk_errors_stats(np.array(all_errors), ["omega", "phi", "kappa"]) def gcp_errors( @@ -235,7 +266,9 @@ def gcp_errors( continue triangulated = None for rec in reconstructions: - triangulated = multiview.triangulate_gcp(gcp, rec.shots, 1.0, 0.1) + triangulated = multiview.triangulate_gcp( + gcp, rec.shots, data.config["gcp_reprojection_error_threshold"] + ) if triangulated is None: continue else: @@ -284,14 +317,16 @@ def gcp_errors( with open(os.path.join(data.data_path, "stats", "ground_control_points.json"), 'w') as f: f.write(json.dumps(gcp_stats, indent=4)) - return _gps_gcp_errors_stats(np.array(all_errors)) + return _gps_gcp_opk_errors_stats(np.array(all_errors), ["x", "y", "z"]) def _compute_errors( reconstructions: List[types.Reconstruction], tracks_manager: pymap.TracksManager -) -> Any: +) -> Callable[[int, pymap.ErrorType], Dict[str, Dict[str, NDArray]]]: @lru_cache(10) - def _compute_errors_cached(index, error_type) -> Dict[str, Dict[str, np.ndarray]]: + def _compute_errors_cached( + index: int, error_type: pymap.ErrorType + ) -> Dict[str, Dict[str, NDArray]]: return reconstructions[index].map.compute_reprojection_errors( tracks_manager, error_type, @@ -302,17 +337,17 @@ def _compute_errors_cached(index, error_type) -> Dict[str, Dict[str, np.ndarray] def _get_valid_observations( reconstructions: List[types.Reconstruction], tracks_manager: pymap.TracksManager -) -> Any: +) -> Callable[[int], Dict[str, Dict[str, pymap.Observation]]]: @lru_cache(10) def _get_valid_observations_cached( - index, + index: int, ) -> Dict[str, Dict[str, pymap.Observation]]: return reconstructions[index].map.get_valid_observations(tracks_manager) return _get_valid_observations_cached -THist = Tuple[np.ndarray, np.ndarray] +THist = Tuple[NDArray, NDArray] def _projection_error( @@ -405,15 +440,17 @@ def reconstruction_statistics( # observations total and average tracks lengths hist_agg = sorted(hist_agg.items(), key=lambda x: x[0]) - lengths, counts = np.array([int(x[0]) for x in hist_agg]), np.array( - [x[1] for x in hist_agg] + lengths, counts = ( + np.array([int(x[0]) for x in hist_agg]), + np.array([x[1] for x in hist_agg]), ) points_count = stats["reconstructed_points_count"] points_count_over_two = sum(counts[1:]) stats["observations_count"] = int(sum(lengths * counts)) stats["average_track_length"] = ( - (stats["observations_count"] / points_count) if points_count > 0 else -1 + (stats["observations_count"] / + points_count) if points_count > 0 else -1 ) stats["average_track_length_over_two"] = ( (int(sum(lengths[1:] * counts[1:])) / points_count_over_two) @@ -461,11 +498,10 @@ def processing_statistics( steps_times = {} for step_name, report_file in steps.items(): - file_path = os.path.join(data.data_path, "reports", report_file) - if os.path.exists(file_path): - with io.open_rt(file_path) as fin: - obj = io.json_load(fin) - else: + try: + report_str = data.load_report(report_file) + obj = io.json_loads(report_str) + except (IOError, OSError): obj = {} if "wall_time" in obj: steps_times[step_name] = obj["wall_time"] @@ -504,7 +540,8 @@ def processing_statistics( min_y = min(min_y, o[1]) max_x = max(max_x, o[0]) max_y = max(max_y, o[1]) - stats["area"] = (max_x - min_x) * (max_y - min_y) if min_x != default_max else -1 + stats["area"] = (max_x - min_x) * \ + (max_y - min_y) if min_x != default_max else -1 return stats @@ -517,7 +554,8 @@ def features_statistics( detected = [] images = {s for r in reconstructions for s in r.shots} for im in images: - features_data = feature_loader.instance.load_all_data(data, im, False, False) + features_data = feature_loader.instance.load_all_data( + data, im, False, False) if not features_data: continue detected.append(len(features_data.points)) @@ -529,7 +567,8 @@ def features_statistics( "median": int(np.median(detected)), } else: - stats["detected_features"] = {"min": -1, "max": -1, "mean": -1, "median": -1} + stats["detected_features"] = {"min": -1, + "max": -1, "mean": -1, "median": -1} per_shots = defaultdict(int) for rec in reconstructions: @@ -565,11 +604,14 @@ def cameras_statistics( stats = {} permutation = np.argsort([-len(r.shots) for r in reconstructions]) for camera_id, camera_model in data.load_camera_models().items(): - stats[camera_id] = {"initial_values": _cameras_statistics(camera_model)} + stats[camera_id] = { + "initial_values": _cameras_statistics(camera_model)} for idx in permutation: rec = reconstructions[idx] for camera in rec.cameras.values(): + if camera.id not in stats: + continue if "optimized_values" in stats[camera.id]: continue stats[camera.id]["optimized_values"] = _cameras_statistics(camera) @@ -613,7 +655,7 @@ def rig_statistics( } for rig_camera_id in rig_cameras: - if rig_camera.id not in stats: + if rig_camera_id not in stats: continue if "optimized_values" not in stats[rig_camera_id]: del stats[rig_camera_id] @@ -628,7 +670,8 @@ def compute_all_statistics( ) -> Dict[str, Any]: stats = {} - stats["processing_statistics"] = processing_statistics(data, reconstructions) + stats["processing_statistics"] = processing_statistics( + data, reconstructions) stats["features_statistics"] = features_statistics( data, tracks_manager, reconstructions ) @@ -640,6 +683,7 @@ def compute_all_statistics( stats["gps_errors"] = gps_errors(reconstructions) stats["gcp_errors"] = gcp_errors(data, reconstructions) stats["3d_errors"] = td_errors(data, tracks_manager, reconstructions) + stats["opk_errors"] = opk_errors(reconstructions) return stats @@ -660,7 +704,7 @@ def _heatmap_buckets(camera: pygeometry.Camera) -> Tuple[int, int]: return buckets, int(buckets / camera.width * camera.height) -def _get_gaussian_kernel(radius: int, ratio: float) -> np.ndarray: +def _get_gaussian_kernel(radius: int, ratio: float) -> NDArray: std_dev = radius / ratio half_kernel = list(range(1, radius + 1)) kernel = np.array(half_kernel + [radius + 1] + list(reversed(half_kernel))) @@ -684,13 +728,14 @@ def save_matchgraph( for shot in rec.shots: shot_component[shot] = i - connectivity = tracks_manager.get_all_pairs_connectivity(all_shots, all_points) + connectivity = tracks_manager.get_all_pairs_connectivity( + all_shots, all_points) all_values = connectivity.values() lowest = np.percentile(list(all_values), 5) highest = np.percentile(list(all_values), 95) plt.clf() - cmap = cm.get_cmap("viridis") + cmap = cm.viridis for (node1, node2), edge in sorted(connectivity.items(), key=lambda x: x[1]): if edge < 2 * data.config["resection_min_inliers"]: continue @@ -700,7 +745,7 @@ def save_matchgraph( continue o1 = reconstructions[comp1].shots[node1].pose.get_origin() o2 = reconstructions[comp2].shots[node2].pose.get_origin() - c = max(0, min(1.0, 1 - (edge - lowest) / (highest - lowest))) + c = max(0, min(1.0, 1 - (float(edge) - lowest) / (highest - lowest))) plt.plot([o1[0], o2[0]], [o1[1], o2[1]], linestyle="-", color=cmap(c)) for i, rec in enumerate(reconstructions): @@ -726,7 +771,7 @@ def save_matchgraph( ax=plt.gca(), ) - with io_handler.open(os.path.join(output_path, "matchgraph.png"), "wb") as fwb: + with io_handler.open_wb(os.path.join(output_path, "matchgraph.png")) as fwb: plt.savefig( fwb, dpi=300, @@ -761,7 +806,11 @@ def save_residual_histogram( h_angular, b_angular = stats["reconstruction_statistics"][ "reprojection_histogram_angular" ] - n, _, p_angular, = axs[ + ( + n, + _, + p_angular, + ) = axs[ 2 ].hist(b_angular[:-1], b_angular, weights=h_angular) n = n.astype("int") @@ -772,9 +821,7 @@ def save_residual_histogram( axs[1].set_title("Pixel Residual") axs[2].set_title("Angular Residual") - with io_handler.open( - os.path.join(output_path, "residual_histogram.png"), "wb" - ) as fwb: + with io_handler.open_wb(os.path.join(output_path, "residual_histogram.png")) as fwb: plt.savefig( fwb, dpi=300, @@ -851,8 +898,9 @@ def save_topview( kernel = _get_gaussian_kernel(splatting, 2) kernel /= kernel[splatting, splatting] for point, color in zip(points, colors): - x, y = int((point[0] - low_x) / size_x * im_size_x), int( - (point[1] - low_y) / size_y * im_size_y + x, y = ( + int((point[0] - low_x) / size_x * im_size_x), + int((point[1] - low_y) / size_y * im_size_y), ) if not ((0 < x < (im_size_x - 1)) and (0 < y < (im_size_y - 1))): continue @@ -863,8 +911,9 @@ def save_topview( size - max(y + splatting - (im_size_y - 2), 0), ) h_low_x, h_low_y = max(x - splatting, 0), max(y - splatting, 0) - h_high_x, h_high_y = min(x + splatting + 1, im_size_x - 1), min( - y + splatting + 1, im_size_y - 1 + h_high_x, h_high_y = ( + min(x + splatting + 1, im_size_x - 1), + min(y + splatting + 1, im_size_y - 1), ) for i in range(3): @@ -884,12 +933,13 @@ def save_topview( sorted_shots = sorted( rec.shots.values(), key=lambda x: x.metadata.capture_time.value ) - c_camera = cm.get_cmap("cool")(0 / len(reconstructions)) - c_gps = cm.get_cmap("autumn")(0 / len(reconstructions)) + c_camera = cm.cool(0 / len(reconstructions)) + c_gps = cm.autumn(0 / len(reconstructions)) for j, shot in enumerate(sorted_shots): o = shot.pose.get_origin() - x, y = int((o[0] - low_x) / size_x * im_size_x), int( - (o[1] - low_y) / size_y * im_size_y + x, y = ( + int((o[0] - low_x) / size_x * im_size_x), + int((o[1] - low_y) / size_y * im_size_y), ) plt.plot( x, @@ -904,8 +954,9 @@ def save_topview( # also display camera path using capture time if j < len(sorted_shots) - 1: n = sorted_shots[j + 1].pose.get_origin() - nx, ny = int((n[0] - low_x) / size_x * im_size_x), int( - (n[1] - low_y) / size_y * im_size_y + nx, ny = ( + int((n[0] - low_x) / size_x * im_size_x), + int((n[1] - low_y) / size_y * im_size_y), ) plt.plot( [x, nx], [y, ny], linestyle="-", color=c_camera, linewidth=linewidth @@ -915,8 +966,9 @@ def save_topview( if not shot.metadata.gps_position.has_value: continue gps = shot.metadata.gps_position.value - gps_x, gps_y = int((gps[0] - low_x) / size_x * im_size_x), int( - (gps[1] - low_y) / size_y * im_size_y + gps_x, gps_y = ( + int((gps[0] - low_x) / size_x * im_size_x), + int((gps[1] - low_y) / size_y * im_size_y), ) plt.plot( gps_x, @@ -971,7 +1023,8 @@ def save_heatmap( all_cameras[camera.id] = camera for i in range(len(reconstructions)): - valid_observations = _get_valid_observations(reconstructions, tracks_manager)(i) + valid_observations = _get_valid_observations( + reconstructions, tracks_manager)(i) for shot_id, observations in valid_observations.items(): shot = reconstructions[i].get_shot(shot_id) w = shot.camera.width @@ -1001,8 +1054,9 @@ def save_heatmap( size - max(y + splatting - (buckets_y - 2), 0), ) h_low_x, h_low_y = max(x - splatting, 0), max(y - splatting, 0) - h_high_x, h_high_y = min(x + splatting + 1, buckets_x - 1), min( - y + splatting + 1, buckets_y - 1 + h_high_x, h_high_y = ( + min(x + splatting + 1, buckets_x - 1), + min(y + splatting + 1, buckets_y - 1), ) camera_heatmap[h_low_y:h_high_y, h_low_x:h_high_x] += kernel[ k_low_y:k_high_y, k_low_x:k_high_x @@ -1034,13 +1088,11 @@ def save_heatmap( fontsize="x-small", ) - with io_handler.open( + with io_handler.open_wb( os.path.join( output_path, "heatmap_" + str(camera_id.replace("/", "_")) + ".png" - ), - "wb", + ) ) as fwb: - plt.savefig( fwb, dpi=300, @@ -1063,7 +1115,8 @@ def save_residual_grids( all_errors[camera_id] = [] for i in range(len(reconstructions)): - valid_observations = _get_valid_observations(reconstructions, tracks_manager)(i) + valid_observations = _get_valid_observations( + reconstructions, tracks_manager)(i) errors_scaled = _compute_errors(reconstructions, tracks_manager)( i, pymap.ErrorType.Normalized ) @@ -1148,7 +1201,7 @@ def save_residual_grids( ) norm = colors.Normalize(vmin=lowest, vmax=highest) - cmap = cm.get_cmap("viridis_r") + cmap = cm.viridis_r sm = cm.ScalarMappable(norm=norm, cmap=cmap) sm.set_array([]) plt.colorbar( @@ -1167,11 +1220,11 @@ def save_residual_grids( [0, buckets_y / 2, buckets_y], [0, int(h / 2), h], fontsize="x-small" ) - with io_handler.open( + with io_handler.open_wb( os.path.join( - output_path, "residuals_" + str(camera_id.replace("/", "_")) + ".png" - ), - "wb", + output_path, "residuals_" + + str(camera_id.replace("/", "_")) + ".png" + ) ) as fwb: plt.savefig( fwb, diff --git a/opensfm/synthetic_data/synthetic_dataset.py b/opensfm/synthetic_data/synthetic_dataset.py index a6023010d..b78e71178 100644 --- a/opensfm/synthetic_data/synthetic_dataset.py +++ b/opensfm/synthetic_data/synthetic_dataset.py @@ -1,18 +1,20 @@ +# pyre-strict import collections import logging import os import shelve -from typing import Any, Dict, Iterator, List, Optional, Tuple, Union +from typing import Any, Dict, Iterator, List, MutableMapping, Optional, Tuple import numpy as np -from opensfm import tracking, features as oft, types, pymap, pygeometry, io, geo +from numpy.typing import NDArray +from opensfm import features as oft, geo, io, pygeometry, pymap, tracking, types from opensfm.dataset import DataSet logger: logging.Logger = logging.getLogger(__name__) class SyntheticFeatures(collections.abc.MutableMapping): - database: Union[Dict[str, oft.FeaturesData], shelve.Shelf] + database: MutableMapping[str, oft.FeaturesData] def __init__(self, on_disk_filename: Optional[str]) -> None: if on_disk_filename: @@ -27,16 +29,16 @@ def sync(self) -> None: database = self.database if type(database) is dict: return - else: + elif type(database) is shelve.Shelf: database.sync() - def __getitem__(self, key) -> oft.FeaturesData: + def __getitem__(self, key: str) -> oft.FeaturesData: return self.database.__getitem__(key) - def __setitem__(self, key, item) -> None: + def __setitem__(self, key: str, item: oft.FeaturesData) -> None: return self.database.__setitem__(key, item) - def __delitem__(self, key) -> None: + def __delitem__(self, key: str) -> None: return self.database.__delitem__(key) def __iter__(self) -> Iterator[str]: @@ -72,16 +74,16 @@ def __init__( self.gcps = gcps self.features = features self.tracks_manager = tracks_manager - self.image_list = list(reconstruction.shots.keys()) + self.image_list: List[str] = list(reconstruction.shots.keys()) self.reference = reconstruction.reference - self.matches = None + self.matches: Optional[Dict[str, Dict[str, NDArray]]] = None self.config["use_altitude_tag"] = True self.config["align_method"] = "naive" def images(self) -> List[str]: return self.image_list - def _raise_if_absent_image(self, image: str): + def _raise_if_absent_image(self, image: str) -> None: if image not in self.image_list: raise RuntimeError("Image isn't present in the synthetic dataset") @@ -120,10 +122,8 @@ def features_exist(self, image: str) -> bool: return False return image in feat - def load_words(self, image: str): - self._raise_if_absent_image(image) - n_closest = 50 - return [image] * n_closest + def load_words(self, image: str) -> NDArray: + raise NotImplementedError def load_features(self, image: str) -> Optional[oft.FeaturesData]: self._raise_if_absent_image(image) @@ -145,7 +145,7 @@ def matches_exists(self, image: str) -> bool: return False return True - def load_matches(self, image: str) -> Dict[str, np.ndarray]: + def load_matches(self, image: str) -> Dict[str, NDArray]: self._raise_if_absent_image(image) self._check_and_create_matches() if self.matches is not None: @@ -160,7 +160,7 @@ def _check_and_create_matches(self) -> None: if self.matches is None: self.matches = self._construct_matches() - def _construct_matches(self) -> Dict[str, Any]: + def _construct_matches(self) -> Dict[str, Dict[str, NDArray]]: matches = {} tracks_manager = self.load_tracks_manager() for im1 in self.images(): diff --git a/opensfm/synthetic_data/synthetic_examples.py b/opensfm/synthetic_data/synthetic_examples.py index c17589395..129399cf5 100644 --- a/opensfm/synthetic_data/synthetic_examples.py +++ b/opensfm/synthetic_data/synthetic_examples.py @@ -1,3 +1,4 @@ +# pyre-strict from typing import Optional import opensfm.synthetic_data.synthetic_scene as ss diff --git a/opensfm/synthetic_data/synthetic_generator.py b/opensfm/synthetic_data/synthetic_generator.py index f4c7343a9..136fcabdd 100644 --- a/opensfm/synthetic_data/synthetic_generator.py +++ b/opensfm/synthetic_data/synthetic_generator.py @@ -1,3 +1,4 @@ +# pyre-strict import logging import math import time @@ -9,6 +10,7 @@ import opensfm.synthetic_data.synthetic_dataset as sd import scipy.signal as signal import scipy.spatial as spatial +from numpy.typing import NDArray from opensfm import ( features as oft, geo, @@ -24,20 +26,20 @@ logger: logging.Logger = logging.getLogger(__name__) -def derivative(func: Callable, x: np.ndarray) -> np.ndarray: +def derivative(func: Callable[[float], NDArray], x: float) -> NDArray: eps = 1e-10 d = (func(x + eps) - func(x)) / eps d /= np.linalg.norm(d) return d -def samples_generator_random_count(count: int) -> np.ndarray: +def samples_generator_random_count(count: int) -> NDArray: return np.random.rand(count) def samples_generator_interval( length: float, end: float, interval: float, interval_noise: float -) -> np.ndarray: +) -> NDArray: samples = np.linspace(0, end / length, num=int(end / interval)) samples += np.random.normal( 0.0, float(interval_noise) / float(length), samples.shape @@ -46,8 +48,8 @@ def samples_generator_interval( def generate_samples_and_local_frame( - samples: np.ndarray, shape: Callable -) -> Tuple[np.ndarray, np.ndarray]: + samples: NDArray, shape: Callable[[float], NDArray] +) -> Tuple[NDArray, NDArray]: points = [] tangents = [] for i in samples: @@ -60,8 +62,8 @@ def generate_samples_and_local_frame( def generate_samples_shifted( - samples: np.ndarray, shape: Callable, shift: float -) -> np.ndarray: + samples: NDArray, shape: Callable[[float], NDArray], shift: float +) -> NDArray: plane_points = [] for i in samples: point = shape(i) @@ -73,8 +75,8 @@ def generate_samples_shifted( def generate_z_plane( - samples: np.ndarray, shape: Callable, thickness: float -) -> np.ndarray: + samples: NDArray, shape: Callable[[float], NDArray], thickness: float +) -> NDArray: plane_points = [] for i in samples: point = shape(i) @@ -88,8 +90,8 @@ def generate_z_plane( def generate_xy_planes( - samples: np.ndarray, shape: Callable, z_size: float, y_size: float -) -> np.ndarray: + samples: NDArray, shape: Callable[[float], NDArray], z_size: float, y_size: float +) -> NDArray: xy1 = generate_samples_shifted(samples, shape, y_size) xy2 = generate_samples_shifted(samples, shape, -y_size) xy1 = np.insert(xy1, 2, values=np.random.rand(xy1.shape[0]) * z_size, axis=1) @@ -98,16 +100,16 @@ def generate_xy_planes( def generate_street( - samples: np.ndarray, shape: Callable, height: float, width: float -) -> Tuple[np.ndarray, np.ndarray]: + samples: NDArray, shape: Callable[[float], NDArray], height: float, width: float +) -> Tuple[NDArray, NDArray]: walls = generate_xy_planes(samples, shape, height, width) floor = generate_z_plane(samples, shape, width) return walls, floor def generate_cameras( - samples: np.ndarray, shape: Callable, height: float -) -> Tuple[np.ndarray, np.ndarray]: + samples: NDArray, shape: Callable[[float], NDArray], height: float +) -> Tuple[NDArray, NDArray]: positions, rotations = generate_samples_and_local_frame(samples, shape) positions = np.insert(positions, 2, values=height, axis=1) rotations = np.insert(rotations, 2, values=0, axis=2) @@ -116,8 +118,8 @@ def generate_cameras( def line_generator( - length: float, center_x: float, center_y: float, transpose: bool, point: np.ndarray -) -> np.ndarray: + length: float, center_x: float, center_y: float, transpose: bool, point: float +) -> NDArray: x = point * length if transpose: return np.transpose( @@ -132,13 +134,13 @@ def line_generator( return np.transpose(np.array([x + center_x, center_y])) -def ellipse_generator(x_size: float, y_size: float, point: float) -> np.ndarray: +def ellipse_generator(x_size: float, y_size: float, point: float) -> NDArray: y = np.sin(point * 2 * np.pi) * y_size / 2 x = np.cos(point * 2 * np.pi) * x_size / 2 return np.transpose(np.array([x, y])) -def perturb_points(points: np.ndarray, sigmas: List[float]) -> None: +def perturb_points(points: NDArray, sigmas: List[float]) -> None: eps = 1e-10 gaussian = np.array([max(s, eps) for s in sigmas]) for point in points: @@ -147,7 +149,7 @@ def perturb_points(points: np.ndarray, sigmas: List[float]) -> None: def generate_causal_noise( dimensions: int, sigma: float, n: int, scale: float -) -> List[np.ndarray]: +) -> List[NDArray]: dims = [np.arange(-scale, scale) for _ in range(dimensions)] mesh = np.meshgrid(*dims) dist = np.linalg.norm(mesh, axis=0) @@ -165,10 +167,6 @@ def generate_exifs( causal_gps_noise: bool = False, ) -> Dict[str, Any]: """Generate fake exif metadata from the reconstruction.""" - speed_ms = 10.0 - previous_pose = None - previous_time = 0 - exifs = {} def _gps_dop(shot: pymap.Shot) -> float: gps_dop = 15.0 @@ -178,6 +176,7 @@ def _gps_dop(shot: pymap.Shot) -> float: gps_dop = gps_noise[shot.camera.id] return gps_dop + exifs = {} per_sequence = defaultdict(list) for shot_name in sorted(reconstruction.shots.keys()): shot = reconstruction.shots[shot_name] @@ -193,13 +192,20 @@ def _gps_dop(shot: pymap.Shot) -> float: if shot.camera.projection_type in ["perspective", "fisheye"]: exif["focal_ratio"] = shot.camera.focal - pose = shot.pose.get_origin() + exifs[shot_name] = exif + + speed_ms = 10.0 + previous_pose = None + previous_time = 0.0 + for rig_instance in sorted( + reconstruction.rig_instances.values(), key=lambda x: x.id + ): + pose = rig_instance.pose.get_origin() if previous_pose is not None: previous_time += np.linalg.norm(pose - previous_pose) / speed_ms previous_pose = pose - exif["capture_time"] = previous_time - - exifs[shot_name] = exif + for shot_id in rig_instance.shots: + exifs[shot_id]["capture_time"] = previous_time for sequence_images in per_sequence.values(): if causal_gps_noise: @@ -214,6 +220,8 @@ def _gps_dop(shot: pymap.Shot) -> float: origin = shot.pose.get_origin() if causal_gps_noise: + # pyre-fixme[61]: `perturbations_2d` is undefined, or not always + # defined. gps_perturbation = [perturbations_2d[j][i] for j in range(2)] + [0] else: gps_noise = _gps_dop(shot) @@ -245,7 +253,7 @@ def _gps_dop(shot: pymap.Shot) -> float: return exifs -def perturb_rotations(rotations: np.ndarray, angle_sigma: float) -> None: +def perturb_rotations(rotations: NDArray, angle_sigma: float) -> None: for i in range(len(rotations)): rotation = rotations[i] rodrigues = cv2.Rodrigues(rotation)[0].ravel() @@ -256,7 +264,7 @@ def perturb_rotations(rotations: np.ndarray, angle_sigma: float) -> None: def add_points_to_reconstruction( - points: np.ndarray, color: np.ndarray, reconstruction: types.Reconstruction + points: NDArray, color: NDArray, reconstruction: types.Reconstruction ) -> None: shift = len(reconstruction.points) for i in range(points.shape[0]): @@ -266,8 +274,8 @@ def add_points_to_reconstruction( def add_shots_to_reconstruction( shots: List[List[str]], - positions: List[np.ndarray], - rotations: List[np.ndarray], + positions: List[NDArray], + rotations: List[NDArray], rig_cameras: List[pymap.RigCamera], cameras: List[pygeometry.Camera], reconstruction: types.Reconstruction, @@ -299,13 +307,13 @@ def add_shots_to_reconstruction( def create_reconstruction( - points: List[np.ndarray], - colors: List[np.ndarray], + points: List[NDArray], + colors: List[NDArray], cameras: List[List[pygeometry.Camera]], shot_ids: List[List[str]], rig_shots: List[List[List[Tuple[str, str]]]], - rig_positions: List[np.ndarray], - rig_rotations: List[np.ndarray], + rig_positions: List[NDArray], + rig_rotations: List[NDArray], rig_cameras: List[List[pymap.RigCamera]], reference: Optional[geo.TopocentricConverter], ) -> Reconstruction: @@ -323,8 +331,14 @@ def create_reconstruction( s_cameras, ) in enumerate(zip(rig_shots, rig_positions, rig_rotations, rig_cameras, cameras)): add_shots_to_reconstruction( + # pyre-fixme[6]: For 1st argument expected `List[List[str]]` but got + # `List[List[Tuple[str, str]]]`. s_rig_shots, + # pyre-fixme[6]: For 2nd argument expected `List[ndarray]` but got + # `ndarray`. s_rig_positions, + # pyre-fixme[6]: For 3rd argument expected `List[ndarray]` but got + # `ndarray`. s_rig_rotations, s_rig_cameras, s_cameras, @@ -340,7 +354,7 @@ def generate_track_data( projection_noise: float, gcp_noise: Tuple[float, float], gcps_count: Optional[int], - gcp_shift: Optional[np.ndarray], + gcp_shift: Optional[NDArray], on_disk_features_filename: Optional[str], ) -> Tuple[ sd.SyntheticFeatures, pymap.TracksManager, Dict[str, pymap.GroundControlPoint] @@ -475,7 +489,7 @@ def generate_track_data( return features, tracks_manager, gcps -def _is_in_front(point: np.ndarray, center: np.ndarray, z_axis: np.ndarray) -> bool: +def _is_in_front(point: NDArray, center: NDArray, z_axis: NDArray) -> bool: return ( (point[0] - center[0]) * z_axis[0] + (point[1] - center[1]) * z_axis[1] @@ -483,7 +497,7 @@ def _is_in_front(point: np.ndarray, center: np.ndarray, z_axis: np.ndarray) -> b ) > 0 -def _is_inside_camera(projection: np.ndarray, camera: pygeometry.Camera) -> bool: +def _is_inside_camera(projection: NDArray, camera: pygeometry.Camera) -> bool: w, h = float(camera.width), float(camera.height) w2 = float(2 * camera.width) h2 = float(2 * camera.height) diff --git a/opensfm/synthetic_data/synthetic_metrics.py b/opensfm/synthetic_data/synthetic_metrics.py index baabb3853..f405404ab 100644 --- a/opensfm/synthetic_data/synthetic_metrics.py +++ b/opensfm/synthetic_data/synthetic_metrics.py @@ -1,14 +1,17 @@ -from typing import Tuple, List, Dict +# pyre-strict +import copy +from typing import Dict, List, Tuple import cv2 import numpy as np import opensfm.transformations as tf -from opensfm import align, types, pymap, multiview +from numpy.typing import NDArray +from opensfm import align, geo, multiview, pymap, types def points_errors( reference: types.Reconstruction, candidate: types.Reconstruction -) -> np.ndarray: +) -> NDArray: common_points = set(reference.points.keys()).intersection( set(candidate.points.keys()) ) @@ -30,7 +33,7 @@ def completeness_errors( ) -def gps_errors(candidate: types.Reconstruction) -> np.ndarray: +def gps_errors(candidate: types.Reconstruction) -> NDArray: errors = [] for shot in candidate.shots.values(): bias = candidate.biases[shot.camera.id] @@ -42,7 +45,7 @@ def gps_errors(candidate: types.Reconstruction) -> np.ndarray: def gcp_errors( candidate: types.Reconstruction, gcps: Dict[str, pymap.GroundControlPoint] -) -> np.ndarray: +) -> NDArray: errors = [] for gcp in gcps.values(): if not gcp.lla: @@ -59,7 +62,7 @@ def gcp_errors( def position_errors( reference: types.Reconstruction, candidate: types.Reconstruction -) -> np.ndarray: +) -> NDArray: common_shots = set(reference.shots.keys()).intersection(set(candidate.shots.keys())) errors = [] for s in common_shots: @@ -71,7 +74,7 @@ def position_errors( def rotation_errors( reference: types.Reconstruction, candidate: types.Reconstruction -) -> np.ndarray: +) -> NDArray: common_shots = set(reference.shots.keys()).intersection(set(candidate.shots.keys())) errors = [] for s in common_shots: @@ -85,8 +88,8 @@ def rotation_errors( def find_alignment( - points0: List[np.ndarray], points1: List[np.ndarray] -) -> Tuple[float, np.ndarray, np.ndarray]: + points0: List[NDArray], points1: List[NDArray] +) -> Tuple[float, NDArray, NDArray]: """Compute similarity transform between point sets. Returns (s, A, b) such that ``points1 = s * A * points0 + b`` @@ -124,25 +127,39 @@ def aligned_to_reference( coords2.append(shot2.pose.get_origin()) s, A, b = find_alignment(coords1, coords2) - aligned = _copy_reconstruction(reconstruction) + aligned = copy.deepcopy(reconstruction) align.apply_similarity(aligned, s, A, b) return aligned -def _copy_reconstruction(reconstruction: types.Reconstruction) -> types.Reconstruction: - copy = types.Reconstruction() - for camera in reconstruction.cameras.values(): - copy.add_camera(camera) - for shot in reconstruction.shots.values(): - copy.add_shot(shot) - for point in reconstruction.points.values(): - copy.add_point(point) - return copy +def change_geo_reference( + reconstruction: types.Reconstruction, + latitude: float, + longitude: float, + altitude: float, +) -> types.Reconstruction: + """Change the geo reference of a reconstruction. + + This assumes that the new lla is close enough that rotation differences + between references can be ignored. + """ + t_old_new = reconstruction.reference.to_topocentric(latitude, longitude, altitude) + + s = 1.0 + A = np.eye(3) + b = -np.array(t_old_new) + aligned = copy.deepcopy(reconstruction) + aligned.reference = geo.TopocentricConverter(latitude, longitude, altitude) + align.apply_similarity(aligned, s, A, b) + for shot in aligned.shots.values(): + if shot.metadata.gps_position.has_value: + shot.metadata.gps_position.value = shot.metadata.gps_position.value + b + return aligned -def rmse(errors: np.ndarray) -> float: - return np.sqrt(np.mean(errors ** 2)) +def rmse(errors: NDArray) -> float: + return np.sqrt(np.mean(errors**2)) -def mad(errors: np.ndarray) -> float: +def mad(errors: NDArray) -> float: return np.median(np.absolute(errors - np.median(errors))) diff --git a/opensfm/synthetic_data/synthetic_scene.py b/opensfm/synthetic_data/synthetic_scene.py index 3c572d802..fd9846284 100644 --- a/opensfm/synthetic_data/synthetic_scene.py +++ b/opensfm/synthetic_data/synthetic_scene.py @@ -1,12 +1,15 @@ +# pyre-strict import functools import math -from typing import Dict, Optional, List, Any, Union, Tuple, Callable +from typing import Any, Callable, Dict, List, Optional, Tuple, Union import numpy as np import opensfm.synthetic_data.synthetic_dataset as sd import opensfm.synthetic_data.synthetic_generator as sg import opensfm.synthetic_data.synthetic_metrics as sm -from opensfm import pygeometry, types, pymap, geo +from numpy.typing import NDArray +from opensfm import geo, pygeometry, pymap, types +from opensfm.reconstruction_helpers import exif_to_metadata def get_camera( @@ -29,7 +32,9 @@ def get_camera( return camera -def get_scene_generator(type: str, length: float, **kwargs) -> functools.partial: +def get_scene_generator( + type: str, length: float, **kwargs: Any +) -> Callable[[float], NDArray]: generator = None if type == "circle": generator = functools.partial(sg.ellipse_generator, length, length) @@ -49,9 +54,7 @@ def get_scene_generator(type: str, length: float, **kwargs) -> functools.partial return generator -def camera_pose( - position: np.ndarray, lookat: np.ndarray, up: np.ndarray -) -> pygeometry.Pose: +def camera_pose(position: NDArray, lookat: NDArray, up: NDArray) -> pygeometry.Pose: """ Pose from position and look at direction @@ -63,7 +66,7 @@ def camera_pose( True """ - def normalized(x: np.ndarray) -> np.ndarray: + def normalized(x: NDArray) -> NDArray: return x / np.linalg.norm(x) ez = normalized(np.array(lookat) - np.array(position)) @@ -75,7 +78,7 @@ def normalized(x: np.ndarray) -> np.ndarray: return pose -class SyntheticScene(object): +class SyntheticScene: def get_reconstruction(self) -> types.Reconstruction: raise NotImplementedError() @@ -85,7 +88,7 @@ class SyntheticCubeScene(SyntheticScene): def __init__(self, num_cameras: int, num_points: int, noise: float) -> None: self.reconstruction = types.Reconstruction() - self.cameras = {} + self.cameras: Dict[str, pygeometry.Camera] = {} for i in range(num_cameras): camera = camera = pygeometry.Camera.create_perspective(0.9, -0.1, 0.01) camera.id = "camera%04d" % i @@ -145,30 +148,30 @@ class SyntheticStreetScene(SyntheticScene): the shape. """ - generator: Optional[Callable] - wall_points: Optional[np.ndarray] - floor_points: Optional[np.ndarray] + generator: Optional[Callable[[float], NDArray]] + wall_points: Optional[NDArray] + floor_points: Optional[NDArray] shot_ids: List[List[str]] cameras: List[List[pygeometry.Camera]] - instances_positions: List[np.ndarray] - instances_rotations: List[np.ndarray] + instances_positions: List[NDArray] + instances_rotations: List[NDArray] rig_instances: List[List[List[Tuple[str, str]]]] rig_cameras: List[List[pymap.RigCamera]] width: float def __init__( self, - generator: Optional[Callable], + generator: Optional[Callable[[float], NDArray]], reference: Optional[geo.TopocentricConverter] = None, ) -> None: self.generator = generator self.reference = reference - self.wall_points = None - self.floor_points = None + self.wall_points: Optional[NDArray] = None + self.floor_points: Optional[NDArray] = None self.shot_ids = [] self.cameras = [] - self.instances_positions = [] - self.instances_rotations = [] + self.instances_positions: List[NDArray] = [] + self.instances_rotations: List[NDArray] = [] self.rig_instances = [] self.rig_cameras = [] self.width = 0.0 @@ -176,9 +179,11 @@ def __init__( def combine(self, other_scene: "SyntheticStreetScene") -> "SyntheticStreetScene": combined_scene = SyntheticStreetScene(None) combined_scene.wall_points = np.concatenate( + # pyre-fixme[6]: For 1st argument expected `Union[_SupportsArray[dtype[ty... (self.wall_points, other_scene.wall_points) ) combined_scene.floor_points = np.concatenate( + # pyre-fixme[6]: For 1st argument expected `Union[_SupportsArray[dtype[ty... (self.floor_points, other_scene.floor_points) ) combined_scene.cameras = self.cameras + other_scene.cameras @@ -238,19 +243,17 @@ def _set_terrain_hill_single(self, height: float, radius: float) -> None: wall_points, floor_points = self.wall_points, self.floor_points assert wall_points is not None and floor_points is not None wall_points[:, 2] += height * np.exp( - -0.5 * np.linalg.norm(wall_points[:, :2], axis=1) ** 2 / radius ** 2 + -0.5 * np.linalg.norm(wall_points[:, :2], axis=1) ** 2 / radius**2 ) floor_points[:, 2] += height * np.exp( - -0.5 * np.linalg.norm(floor_points[:, :2], axis=1) ** 2 / radius ** 2 + -0.5 * np.linalg.norm(floor_points[:, :2], axis=1) ** 2 / radius**2 ) for positions in self.instances_positions: for position in positions: position[2] += height * np.exp( -0.5 - * np.linalg.norm( - (position[0] ** 2 + position[1] ** 2) / radius ** 2 - ) + * np.linalg.norm((position[0] ** 2 + position[1] ** 2) / radius**2) ) def _set_terrain_hill_repeated(self, height: float, radius: float) -> None: @@ -344,7 +347,9 @@ def add_rig_camera_sequence( for j, (rig_camera_p, rig_camera_r) in enumerate( zip(relative_positions, relative_rotations) ): + # pyre-fixme[6]: For 1st argument expected `ndarray` but got `List[float]`. pose_rig_camera = pygeometry.Pose(rig_camera_r) + # pyre-fixme[6]: For 1st argument expected `ndarray` but got `List[float]`. pose_rig_camera.set_origin(rig_camera_p) rotations = [] @@ -370,7 +375,9 @@ def add_rig_camera_sequence( for i, (rig_camera_p, rig_camera_r) in enumerate( zip(relative_positions, relative_rotations) ): + # pyre-fixme[6]: For 1st argument expected `ndarray` but got `List[float]`. pose_rig_camera = pygeometry.Pose(rig_camera_r) + # pyre-fixme[6]: For 1st argument expected `ndarray` but got `List[float]`. pose_rig_camera.set_origin(rig_camera_p) rig_camera_id = f"RigCamera {rig_camera_id_shift + i}" rig_camera = pymap.RigCamera(pose_rig_camera, rig_camera_id) @@ -395,7 +402,11 @@ def get_reconstruction(self) -> types.Reconstruction: wall_color = [10, 90, 130] return sg.create_reconstruction( - points=np.asarray([self.floor_points, self.wall_points]), + # pyre-fixme[6]: For 1st argument expected `List[ndarray[typing.Any, + # typing.Any]]` but got `ndarray[typing.Any, dtype[typing.Any]]`. + points=np.asarray([self.floor_points, self.wall_points], dtype=object), + # pyre-fixme[6]: For 2nd argument expected `List[ndarray[typing.Any, + # typing.Any]]` but got `ndarray[typing.Any, dtype[typing.Any]]`. colors=np.asarray([floor_color, wall_color]), cameras=self.cameras, shot_ids=self.shot_ids, @@ -430,7 +441,7 @@ def __init__( gcp_noise: Tuple[float, float], causal_gps_noise: bool, gcps_count: Optional[int] = None, - gcps_shift: Optional[np.ndarray] = None, + gcps_shift: Optional[NDArray] = None, on_disk_features_filename: Optional[str] = None, generate_projections: bool = True, ) -> None: @@ -443,6 +454,11 @@ def __init__( causal_gps_noise=causal_gps_noise, ) + for shot in self.reconstruction.shots.values(): + shot.metadata = exif_to_metadata( + self.exifs[shot.id], False, self.reconstruction.reference + ) + if generate_projections: (self.features, self.tracks_manager, self.gcps) = sg.generate_track_data( reconstruction, @@ -464,15 +480,18 @@ def compare( reconstruction: types.Reconstruction, ) -> Dict[str, float]: """Compare a reconstruction with reference groundtruth.""" + geo = reference.reference + completeness = sm.completeness_errors(reference, reconstruction) - absolute_position = sm.position_errors(reference, reconstruction) - absolute_rotation = sm.rotation_errors(reference, reconstruction) - absolute_points = sm.points_errors(reference, reconstruction) - absolute_gps = sm.gps_errors(reconstruction) - absolute_gcp = sm.gcp_errors(reconstruction, gcps) + geo_referenced = sm.change_geo_reference(reconstruction, geo.lat, geo.lon, geo.alt) + absolute_position = sm.position_errors(reference, geo_referenced) + absolute_rotation = sm.rotation_errors(reference, geo_referenced) + absolute_points = sm.points_errors(reference, geo_referenced) + absolute_gps = sm.gps_errors(geo_referenced) + absolute_gcp = sm.gcp_errors(geo_referenced, gcps) - aligned = sm.aligned_to_reference(reference, reconstruction) + aligned = sm.aligned_to_reference(reference, geo_referenced) aligned_position = sm.position_errors(reference, aligned) aligned_rotation = sm.rotation_errors(reference, aligned) aligned_points = sm.points_errors(reference, aligned) diff --git a/opensfm/test/conftest.py b/opensfm/test/conftest.py index b8cc2ed9a..bc6512e93 100644 --- a/opensfm/test/conftest.py +++ b/opensfm/test/conftest.py @@ -1,14 +1,15 @@ +# pyre-strict from collections import defaultdict -from distutils.version import LooseVersion from typing import Dict, List, Tuple import numpy as np import pytest -from opensfm import multiview, types, geo, pygeometry, pymap +from numpy.typing import NDArray +from opensfm import geo, multiview, pygeometry, pymap, types from opensfm.synthetic_data import ( + synthetic_dataset as sd, synthetic_examples, synthetic_scene, - synthetic_dataset as sd, ) @@ -17,9 +18,12 @@ def pytest_configure(config) -> None: def use_legacy_numpy_printoptions() -> None: - """Ensure numpy use legacy print formant.""" - if LooseVersion(np.__version__).version[:2] > [1, 13]: - np.set_printoptions(legacy="1.13") + """Ensure that numpy prints in a consistent way across versions. + + This is needed so that doctests are always tested using the same print format, + which matches the one used in the docstrings. + """ + np.set_printoptions(legacy="1.13") @pytest.fixture(scope="module") @@ -124,14 +128,16 @@ def scene_synthetic_triangulation() -> synthetic_scene.SyntheticInputData: @pytest.fixture(scope="module") -def pairs_and_poses() -> Tuple[ - Dict[Tuple[str, str], List[Tuple[List[np.ndarray]]]], - Dict[Tuple[str, str], List[Tuple[List[np.ndarray]]]], - pygeometry.Camera, - sd.SyntheticFeatures, - pymap.TracksManager, - types.Reconstruction, -]: +def pairs_and_poses() -> ( + Tuple[ + Dict[Tuple[str, str], List[Tuple[List[NDArray]]]], + Dict[Tuple[str, str], List[Tuple[List[NDArray]]]], + pygeometry.Camera, + sd.SyntheticFeatures, + pymap.TracksManager, + types.Reconstruction, + ] +): np.random.seed(42) data = synthetic_examples.synthetic_cube_scene() @@ -160,7 +166,7 @@ def pairs_and_poses() -> Tuple[ @pytest.fixture(scope="module") def pairs_and_their_E( pairs_and_poses, -) -> List[Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]]: +) -> List[Tuple[NDArray, NDArray, NDArray, pygeometry.Pose]]: pairs, poses, camera, _, _, _ = pairs_and_poses pairs = sorted(zip(pairs.values(), poses.values()), key=lambda x: -len(x[0])) @@ -192,7 +198,7 @@ def pairs_and_their_E( @pytest.fixture(scope="module") def shots_and_their_points( pairs_and_poses, -) -> List[Tuple[pygeometry.Pose, np.ndarray, np.ndarray]]: +) -> List[Tuple[pygeometry.Pose, NDArray, NDArray]]: _, _, _, _, tracks_manager, reconstruction = pairs_and_poses ret_shots = [] diff --git a/opensfm/test/data_generation.py b/opensfm/test/data_generation.py index 097d53c09..4f59d520e 100644 --- a/opensfm/test/data_generation.py +++ b/opensfm/test/data_generation.py @@ -1,4 +1,6 @@ +# pyre-strict import os +from typing import Any, Dict import opensfm.dataset import yaml @@ -8,12 +10,12 @@ try: from libfb.py import parutil - DATA_PATH = parutil.get_dir_path("mapillary/opensfm/opensfm/test/data") + DATA_PATH: str = parutil.get_dir_path("mapillary/opensfm/opensfm/test/data") except ImportError: DATA_PATH = os.path.abspath("data") -def create_berlin_test_folder(tmpdir) -> opensfm.dataset.DataSet: +def create_berlin_test_folder(tmpdir: Any) -> opensfm.dataset.DataSet: src = os.path.join(DATA_PATH, "berlin") dst = str(tmpdir.mkdir("berlin")) files = ["images", "masks", "config.yaml", "ground_control_points.json"] @@ -22,6 +24,6 @@ def create_berlin_test_folder(tmpdir) -> opensfm.dataset.DataSet: return opensfm.dataset.DataSet(dst) -def save_config(config, path) -> None: +def save_config(config: Dict[str, Any], path: str) -> None: with io.open_wt(os.path.join(path, "config.yaml")) as fout: yaml.safe_dump(config, fout, default_flow_style=False) diff --git a/opensfm/test/large/test_tools.py b/opensfm/test/large/test_tools.py index d7cc370b5..f5da01409 100644 --- a/opensfm/test/large/test_tools.py +++ b/opensfm/test/large/test_tools.py @@ -1,4 +1,3 @@ -import numpy as np from opensfm.large import tools diff --git a/opensfm/test/test_bundle.py b/opensfm/test/test_bundle.py index a3e12afaf..8195b2944 100644 --- a/opensfm/test/test_bundle.py +++ b/opensfm/test/test_bundle.py @@ -1,7 +1,9 @@ +# pyre-strict import copy import numpy as np import pytest +from numpy.typing import NDArray from opensfm import ( config, geometry, @@ -12,6 +14,7 @@ tracking, types, ) +from opensfm.synthetic_data import synthetic_scene def test_unicode_strings_in_bundle() -> None: @@ -88,14 +91,16 @@ def test_singleton_pan_tilt_roll(bundle_adjuster: pybundle.BundleAdjuster) -> No assert np.allclose(ptr, (pan, tilt, roll)) -def _projection_errors_std(points) -> float: +def _projection_errors_std(points: pymap.LandmarkView) -> float: all_errors = [] for p in points.values(): all_errors += p.reprojection_errors.values() return np.std(all_errors) -def test_bundle_projection_fixed_internals(scene_synthetic) -> None: +def test_bundle_projection_fixed_internals( + scene_synthetic: synthetic_scene.SyntheticInputData, +) -> None: reference = scene_synthetic.reconstruction camera_priors = dict(reference.cameras.items()) rig_priors = dict(reference.rig_cameras.items()) @@ -107,14 +112,27 @@ def test_bundle_projection_fixed_internals(scene_synthetic) -> None: color = g_obs["feature_color"] pt = g_obs["feature"] obs = pymap.Observation( + # pyre-fixme[6]: For 1st argument expected `str` but got `int`. pt[0], + # pyre-fixme[6]: For 1st argument expected `str` but got `int`. pt[1], + # pyre-fixme[6]: For 3rd argument expected `float` but got + # `Dict[str, typing.Any]`. g_obs["feature_scale"], + # pyre-fixme[6]: For 1st argument expected `str` but got `int`. color[0], + # pyre-fixme[6]: For 1st argument expected `str` but got `int`. color[1], + # pyre-fixme[6]: For 1st argument expected `str` but got `int`. color[2], + # pyre-fixme[6]: For 7th argument expected `int` but got + # `Dict[str, typing.Any]`. g_obs["feature_id"], + # pyre-fixme[6]: For 8th argument expected `int` but got + # `Dict[str, typing.Any]`. g_obs["feature_segmentation"], + # pyre-fixme[6]: For 9th argument expected `int` but got + # `Dict[str, typing.Any]`. g_obs["feature_instance"], ) reference.map.add_observation(shot_id, point_id, obs) @@ -124,7 +142,8 @@ def test_bundle_projection_fixed_internals(scene_synthetic) -> None: custom_config = config.default_config() custom_config["bundle_use_gps"] = False custom_config["optimize_camera_parameters"] = False - reconstruction.bundle(reference, camera_priors, rig_priors, [], custom_config) + reconstruction.bundle(reference, camera_priors, + rig_priors, [], 0, custom_config) assert _projection_errors_std(reference.points) < 5e-3 assert reference.cameras["1"].focal == orig_camera.focal @@ -181,7 +200,8 @@ def test_pair_with_points_priors(bundle_adjuster: pybundle.BundleAdjuster) -> No instance_id = str(i + 1) sa.add_rig_instance( instance_id, - pygeometry.Pose(np.array([1e-3, 1e-3, 1e-3]), np.array([1e-3, 1e-3, 1e-3])), + pygeometry.Pose(np.array([1e-3, 1e-3, 1e-3]), + np.array([1e-3, 1e-3, 1e-3])), {instance_id: "cam1"}, {instance_id: "rig_cam1"}, False, @@ -213,12 +233,20 @@ def test_pair_with_points_priors(bundle_adjuster: pybundle.BundleAdjuster) -> No ) std_dev = np.array([1, 1, 1]) - sa.add_point_projection_observation("1", "p1", np.array([0, 0]), 1) - sa.add_point_projection_observation("2", "p1", np.array([-0.5, 0]), 1) + sa.add_point_projection_observation( + shot="1", point="p1", observation=np.array([0, 0]), std_deviation=1 + ) + sa.add_point_projection_observation( + shot="2", point="p1", observation=np.array([-0.5, 0]), std_deviation=1 + ) sa.add_point_prior("p1", np.array([-0.5, 2, 2]), std_dev, True) - sa.add_point_projection_observation("2", "p2", np.array([0, 0]), 1) - sa.add_point_projection_observation("1", "p2", np.array([0.5, 0]), 1) + sa.add_point_projection_observation( + shot="2", point="p2", observation=np.array([0, 0]), std_deviation=1 + ) + sa.add_point_projection_observation( + shot="1", point="p2", observation=np.array([0.5, 0]), std_deviation=1 + ) sa.add_point_prior("p2", np.array([1.5, 2, 2]), std_dev, True) sa.run() @@ -581,7 +609,7 @@ def test_bundle_void_gps_ignored() -> None: shot.metadata.gps_accuracy.value = 1 shot.metadata.gps_position.reset() shot.pose.set_origin(np.ones(3)) - reconstruction.bundle(r, camera_priors, rig_priors, gcp, myconfig) + reconstruction.bundle(r, camera_priors, rig_priors, gcp, 0, myconfig) assert np.allclose(shot.pose.get_origin(), np.ones(3)) # Missing accuracy @@ -589,14 +617,14 @@ def test_bundle_void_gps_ignored() -> None: shot.metadata.gps_accuracy.value = 1 shot.metadata.gps_accuracy.reset() shot.pose.set_origin(np.ones(3)) - reconstruction.bundle(r, camera_priors, rig_priors, gcp, myconfig) + reconstruction.bundle(r, camera_priors, rig_priors, gcp, 0, myconfig) assert np.allclose(shot.pose.get_origin(), np.ones(3)) # Valid gps position and accuracy shot.metadata.gps_position.value = np.zeros(3) shot.metadata.gps_accuracy.value = 1 shot.pose.set_origin(np.ones(3)) - reconstruction.bundle(r, camera_priors, rig_priors, gcp, myconfig) + reconstruction.bundle(r, camera_priors, rig_priors, gcp, 0, myconfig) assert np.allclose(shot.pose.get_origin(), np.zeros(3)) @@ -618,7 +646,7 @@ def test_bundle_alignment_prior() -> None: gcp = [] myconfig = config.default_config() - reconstruction.bundle(r, camera_priors, rig_priors, gcp, myconfig) + reconstruction.bundle(r, camera_priors, rig_priors, gcp, 0, myconfig) shot = r.shots[shot.id] assert np.allclose(shot.pose.translation, np.zeros(3)) @@ -645,7 +673,7 @@ def test_heatmaps_position(bundle_adjuster: pybundle.BundleAdjuster) -> None: sa.add_reconstruction_instance("123", 1, "3") sa.set_scale_sharing("123", True) - def bell_heatmap(size, r, mu_x, mu_y): + def bell_heatmap(size: int, r: float, mu_x: float, mu_y: float) -> NDArray: sigma_x = r * 0.5 sigma_y = r * 0.5 x = np.linspace(-r, r, size) @@ -657,8 +685,8 @@ def bell_heatmap(size, r, mu_x, mu_y): / (2 * np.pi * sigma_x * sigma_y) * np.exp( -( - (x - mu_x) ** 2 / (2 * sigma_x ** 2) - + (y - mu_y) ** 2 / (2 * sigma_y ** 2) + (x - mu_x) ** 2 / (2 * sigma_x**2) + + (y - mu_y) ** 2 / (2 * sigma_y**2) ) ) ) @@ -670,7 +698,7 @@ def bell_heatmap(size, r, mu_x, mu_y): hmap_size, hmap_r = 101, 10 res = 2 * hmap_r / (hmap_size - 1) hmap = bell_heatmap(size=hmap_size, r=hmap_r, mu_x=hmap_x, mu_y=hmap_y) - sa.add_heatmap("hmap1", hmap.flatten(), hmap_size, res) + sa.add_heatmap("hmap1", hmap.flatten().tolist(), hmap_size, res) x1_offset, y1_offset = 2, 0 x2_offset, y2_offset = 0, 2 x3_offset, y3_offset = -2, 0 diff --git a/opensfm/test/test_commands.py b/opensfm/test/test_commands.py index 2e30159b8..649a7c2ad 100644 --- a/opensfm/test/test_commands.py +++ b/opensfm/test/test_commands.py @@ -1,18 +1,21 @@ +# pyre-strict import argparse from os.path import join +from types import ModuleType +from typing import Any, List from opensfm import commands, dataset from opensfm.test import data_generation, utils -def run_command(command, args) -> None: +def run_command(command: ModuleType, args: List[str]) -> None: parser = argparse.ArgumentParser() command.add_arguments(parser) parsed_args = parser.parse_args(args) command.run(dataset.DataSet(parsed_args.dataset), parsed_args) -def test_run_all(tmpdir) -> None: +def test_run_all(tmpdir: Any) -> None: data = data_generation.create_berlin_test_folder(tmpdir) run_all_commands = [ commands.extract_metadata, diff --git a/opensfm/test/test_dataset.py b/opensfm/test/test_dataset.py index 02202e45d..4c3257f85 100644 --- a/opensfm/test/test_dataset.py +++ b/opensfm/test/test_dataset.py @@ -1,9 +1,13 @@ +# pyre-strict +from typing import Any + import numpy as np + from opensfm import features from opensfm.test import data_generation -def test_dataset_load_features_sift(tmpdir) -> None: +def test_dataset_load_features_sift(tmpdir: Any) -> None: data = data_generation.create_berlin_test_folder(tmpdir) assert len(data.images()) == 3 @@ -25,6 +29,7 @@ def test_dataset_load_features_sift(tmpdir) -> None: after = data.load_features(image) assert after assert np.allclose(points, after.points) + # pyre-fixme[6]: For 2nd argument expected `Union[_SupportsArray[dtype[typing.Any... assert np.allclose(descriptors, after.descriptors) assert np.allclose(colors, after.colors) semantic = after.semantic @@ -33,4 +38,5 @@ def test_dataset_load_features_sift(tmpdir) -> None: segmentations, semantic.segmentation, ) + # pyre-fixme[6]: For 2nd argument expected `Union[_SupportsArray[dtype[typing.Any... assert np.allclose(instances, semantic.instances) diff --git a/opensfm/test/test_datastructures.py b/opensfm/test/test_datastructures.py index 2e8d1a25e..ae7072a02 100644 --- a/opensfm/test/test_datastructures.py +++ b/opensfm/test/test_datastructures.py @@ -1,29 +1,29 @@ +# pyre-strict import copy -from opensfm.pymap import RigCamera, RigInstance, Shot -from opensfm.types import Reconstruction -from typing import Tuple import random +from typing import Dict, Optional, Tuple, Union import numpy as np import pytest -from opensfm import pygeometry -from opensfm import pymap -from opensfm import types +from opensfm import pygeometry, pymap, types +from opensfm.pymap import RigCamera, RigInstance, Shot from opensfm.test.utils import ( - assert_maps_equal, assert_metadata_equal, assert_cameras_equal, + assert_maps_equal, + assert_metadata_equal, assert_shots_equal, ) +from opensfm.types import Reconstruction def _create_reconstruction( - n_cameras: int=0, - n_shots_cam=None, - n_pano_shots_cam=None, - n_points: int=0, - dist_to_shots: bool=False, - dist_to_pano_shots: bool=False, + n_cameras: int = 0, + n_shots_cam: Optional[Dict[str, int]] = None, + n_pano_shots_cam: Optional[Dict[str, int]] = None, + n_points: int = 0, + dist_to_shots: bool = False, + dist_to_pano_shots: bool = False, ) -> types.Reconstruction: """Creates a reconstruction with n_cameras random cameras and shots, where n_shots_cam is a dictionary, containing the @@ -127,7 +127,7 @@ def test_camera_iterators() -> None: visited_cams = set() for cam in rec.cameras.values(): visited_cams.add(cam.id) - focal = np.random.rand(1) + focal = np.random.rand(1).item() cam.focal = focal assert rec.cameras[cam.id].focal == focal assert cam is rec.cameras[cam.id] @@ -140,13 +140,15 @@ def test_camera_iterators() -> None: for cam_id, cam in rec.cameras.items(): assert cam_id == cam.id - focal = np.random.rand(1) + focal = np.random.rand(1).item() cam.focal = focal assert rec.cameras[cam.id].focal == focal assert cam is rec.cameras[cam.id] -def _check_common_cam_properties(cam1, cam2) -> None: +def _check_common_cam_properties( + cam1: pygeometry.Camera, cam2: pygeometry.Camera +) -> None: assert cam1.id == cam2.id assert cam1.width == cam2.width assert cam1.height == cam2.height @@ -253,7 +255,9 @@ def test_fisheye624_camera() -> None: focal = 0.6 aspect_ratio = 0.7 ppoint = np.array([0.51, 0.52]) - dist = np.array([-0.1, 0.09, 0.08, 0.01, 0.02, 0.05, 0.1, 0.2, 0.01, -0.003, 0.005, -0.007]) # [k1-k6, p1, p2, s0-s3] + dist = np.array( + [-0.1, 0.09, 0.08, 0.01, 0.02, 0.05, 0.1, 0.2, 0.01, -0.003, 0.005, -0.007] + ) # [k1-k6, p1, p2, s0-s3] cam_cpp = pygeometry.Camera.create_fisheye624(focal, aspect_ratio, ppoint, dist) cam_cpp.width = 800 cam_cpp.height = 600 @@ -325,7 +329,9 @@ def test_spherical_camera() -> None: # Test Metadata -def _help_measurement_test(measurement, attr, val) -> None: +def _help_measurement_test( + measurement: object, attr: str, val: Union[float, str] +) -> None: # Test metadata's has_value properties assert getattr(measurement, attr).has_value is False getattr(measurement, attr).value = val @@ -343,25 +349,25 @@ def _help_measurement_test(measurement, attr, val) -> None: def test_shot_measurement_setter_and_getter() -> None: m1 = pymap.ShotMeasurements() # Test basic functionality - _help_measurement_test(m1, "capture_time", np.random.rand(1)) + _help_measurement_test(m1, "capture_time", np.random.rand(1).item()) _help_measurement_test(m1, "gps_position", np.random.rand(3)) - _help_measurement_test(m1, "gps_accuracy", np.random.rand(1)) - _help_measurement_test(m1, "compass_accuracy", np.random.rand(1)) - _help_measurement_test(m1, "compass_angle", np.random.rand(1)) - _help_measurement_test(m1, "opk_accuracy", np.random.rand(1)) + _help_measurement_test(m1, "gps_accuracy", np.random.rand(1).item()) + _help_measurement_test(m1, "compass_accuracy", np.random.rand(1).item()) + _help_measurement_test(m1, "compass_angle", np.random.rand(1).item()) + _help_measurement_test(m1, "opk_accuracy", np.random.rand(1).item()) _help_measurement_test(m1, "opk_angles", np.random.rand(3)) _help_measurement_test(m1, "gravity_down", np.random.rand(3)) _help_measurement_test(m1, "orientation", random.randint(0, 100)) _help_measurement_test(m1, "sequence_key", "key_test") -def _helper_populate_metadata(m) -> None: - m.capture_time.value = np.random.rand(1) +def _helper_populate_metadata(m: pymap.ShotMeasurements) -> None: + m.capture_time.value = np.random.rand(1).item() m.gps_position.value = np.random.rand(3) - m.gps_accuracy.value = np.random.rand(1) - m.compass_accuracy.value = np.random.rand(1) - m.compass_angle.value = np.random.rand(1) - m.opk_accuracy.value = np.random.rand(1) + m.gps_accuracy.value = np.random.rand(1).item() + m.compass_accuracy.value = np.random.rand(1).item() + m.compass_angle.value = np.random.rand(1).item() + m.opk_accuracy.value = np.random.rand(1).item() m.opk_angles.value = np.random.rand(3) m.gravity_down.value = np.random.rand(3) m.orientation.value = random.randint(0, 100) @@ -967,7 +973,7 @@ def test_many_observations_delete() -> None: m.create_rig_camera(pymap.RigCamera(pygeometry.Pose(), cam.id)) for shot_id in range(n_shots): - cam_id = "cam" + str(int(np.random.rand(1) * 10 % n_cams)) + cam_id = "cam" + str(int(np.random.rand(1).item() * 10 % n_cams)) shot_id = str(shot_id) m.create_rig_instance(shot_id) m.create_shot(shot_id, cam_id, cam_id, shot_id, pygeometry.Pose()) @@ -1007,7 +1013,7 @@ def test_clean_landmarks_with_min_observations() -> None: m.create_rig_camera(pymap.RigCamera(pygeometry.Pose(), cam.id)) for shot_id in range(n_shots): - cam_id = "cam" + str(int(np.random.rand(1) * 10 % n_cams)) + cam_id = "cam" + str(int(np.random.rand(1).item() * 10 % n_cams)) m.create_rig_instance(str(shot_id)) m.create_shot(str(shot_id), cam_id, cam_id, str(shot_id), pygeometry.Pose()) @@ -1104,6 +1110,7 @@ def test_rec_deepcopy() -> None: assert_maps_equal(rec.map, rec2.map) + def test_gcp() -> None: gcp = [] for i in range(0, 10): diff --git a/opensfm/test/test_dense.py b/opensfm/test/test_dense.py index 5f2bb9789..cc3f95858 100644 --- a/opensfm/test/test_dense.py +++ b/opensfm/test/test_dense.py @@ -1,21 +1,20 @@ +# pyre-strict import numpy as np -from opensfm import dense -from opensfm import pygeometry -from opensfm import types +from opensfm import dense, pygeometry, types def test_angle_between_points() -> None: - origin = [0, 0, 0] - p1 = [1, 0, 0] - p2 = [0, 1, 0] + origin = [0.0, 0.0, 0.0] + p1 = [1.0, 0.0, 0.0] + p2 = [0.0, 1.0, 0.0] res = dense.angle_between_points(origin, p1, p2) assert np.allclose(res, np.pi / 2) - origin = [10, 15, 20] - p1 = [10, 16, 20] - p2 = [10, 16, 21] + origin = [10.0, 15.0, 20.0] + p1 = [10.0, 16.0, 20.0] + p2 = [10.0, 16.0, 21.0] res = dense.angle_between_points(origin, p1, p2) @@ -32,7 +31,9 @@ def test_depthmap_to_ply() -> None: r = types.Reconstruction() r.add_camera(camera) shot = r.create_shot( - "shot1", camera.id, pygeometry.Pose(np.array([0.0, 0.0, 0.0]), np.array([0.0, 0.0, 0.0])) + "shot1", + camera.id, + pygeometry.Pose(np.array([0.0, 0.0, 0.0]), np.array([0.0, 0.0, 0.0])), ) image = np.zeros((height, width, 3)) diff --git a/opensfm/test/test_exif.py b/opensfm/test/test_exif.py new file mode 100644 index 000000000..0bf1766d8 --- /dev/null +++ b/opensfm/test/test_exif.py @@ -0,0 +1,349 @@ + +import io +import pytest +import exifread +import datetime +import numpy as np +from opensfm import exif + + +@pytest.fixture +def dji_xmp_data_gimbal(): + return """ + + + + + + + """ + + +@pytest.fixture +def dji_xmp_data_flight(): + return """ + + + + + + + """ + + +@pytest.fixture +def dji_xmp_data_camera(): + return """ + + + + + + + """ + + +def create_exif_instance(xmp_content, monkeypatch): + content = xmp_content.encode('utf-8') + fileobj = io.BytesIO(content) + fileobj.name = "test.jpg" + + monkeypatch.setattr("exifread.process_file", lambda f, details=False: {}) + + return exif.EXIF(fileobj, lambda: (100, 100)) + + +def test_dji_parsing_latlon(dji_xmp_data_gimbal, monkeypatch): + e = create_exif_instance(dji_xmp_data_gimbal, monkeypatch) + assert e.has_dji_latlon() + lon, lat = e.extract_dji_lon_lat() + assert lon == 5.0 + assert lat == -6.0 + + +def test_dji_parsing_altitude(dji_xmp_data_gimbal, monkeypatch): + e = create_exif_instance(dji_xmp_data_gimbal, monkeypatch) + assert e.has_dji_altitude() + alt = e.extract_dji_altitude() + assert alt == 100.0 + + +def test_dji_parsing_public_geo(dji_xmp_data_gimbal, monkeypatch): + e = create_exif_instance(dji_xmp_data_gimbal, monkeypatch) + lon, lat = e.extract_lon_lat() + assert lon == 5.0 + assert lat == -6.0 + + +def test_dji_parsing_public_alt(dji_xmp_data_gimbal, monkeypatch): + e = create_exif_instance(dji_xmp_data_gimbal, monkeypatch) + alt = e.extract_altitude() + assert alt == 100.0 + + +def test_dji_parsing_opk(dji_xmp_data_gimbal, monkeypatch): + e = create_exif_instance(dji_xmp_data_gimbal, monkeypatch) + lon, lat = e.extract_lon_lat() + alt = e.extract_altitude() + geo_dict = {"latitude": lat, "longitude": lon, "altitude": alt} + + opk = e.extract_opk(geo_dict) + assert opk is not None + + +def test_dji_parsing_flight(dji_xmp_data_flight, monkeypatch): + e = create_exif_instance(dji_xmp_data_flight, monkeypatch) + + lon, lat = e.extract_dji_lon_lat() + assert lon == -5.0 + assert lat == 6.0 + + alt = e.extract_dji_altitude() + assert alt == 50.0 + + geo_dict = {"latitude": lat, "longitude": lon, "altitude": alt} + + opk = e.extract_opk(geo_dict) + assert opk is not None + + +def test_dji_parsing_camera(dji_xmp_data_camera, monkeypatch): + e = create_exif_instance(dji_xmp_data_camera, monkeypatch) + + geo_dict = {"latitude": 2.0, "longitude": 1.0, "altitude": 3.0} + + opk = e.extract_opk(geo_dict) + assert opk is not None + + +def test_dji_parsing_none(monkeypatch): + # No XMP data + content = b"dummy" + fileobj = io.BytesIO(content) + fileobj.name = "test.jpg" + monkeypatch.setattr("exifread.process_file", lambda f, details=False: {}) + + e = exif.EXIF(fileobj, lambda: (100, 100)) + + assert not e.has_dji_latlon() + assert not e.has_dji_altitude() + + geo_dict = {"latitude": 0, "longitude": 0, "altitude": 0} + opk = e.extract_opk(geo_dict) + assert opk is None + + +class MockTag: + def __init__(self, values): + self.values = values + + +def create_exif_with_tags(tags, monkeypatch): + monkeypatch.setattr("exifread.process_file", lambda f, details=False: tags) + fileobj = io.BytesIO(b"") + fileobj.name = "test.jpg" + + monkeypatch.setattr("opensfm.exif.get_xmp", lambda f: []) + return exif.EXIF(fileobj, lambda: (1000, 2000)) + + +def test_gps_parsing_standard(monkeypatch): + tags = { + "GPS GPSLatitude": MockTag([exifread.utils.Ratio(45, 1), exifread.utils.Ratio(30, 1), exifread.utils.Ratio(0, 1)]), + "GPS GPSLatitudeRef": MockTag("N"), + "GPS GPSLongitude": MockTag([exifread.utils.Ratio(10, 1), exifread.utils.Ratio(0, 1), exifread.utils.Ratio(0, 1)]), + "GPS GPSLongitudeRef": MockTag("W"), + "GPS GPSAltitude": MockTag([exifread.utils.Ratio(500, 1)]), + "GPS GPSAltitudeRef": MockTag([1]), + } + + e = create_exif_with_tags(tags, monkeypatch) + + lon, lat = e.extract_lon_lat() + assert lat == 45.5 + assert lon == -10.0 + + alt = e.extract_altitude() + assert alt == -500.0 + + +def test_focal_length_parsing(monkeypatch): + tags = { + "EXIF FocalLength": MockTag([exifread.utils.Ratio(24, 1)]), + "EXIF FocalLengthIn35mmFilm": MockTag([exifread.utils.Ratio(35, 1)]), + "EXIF ExifImageWidth": MockTag([4000]), + "EXIF ExifImageLength": MockTag([3000]), + } + + e = create_exif_with_tags(tags, monkeypatch) + + focal_35, focal_ratio = e.extract_focal() + assert focal_35 == 35.0 + + # Image is 4:3 (4000x3000), so film width is assumed 34mm + assert abs(focal_ratio - (35.0 / 34.0)) < 0.01 + + +def test_sensor_width_calculation(monkeypatch): + tags = { + # Focal 50mm + "EXIF FocalLength": MockTag([exifread.utils.Ratio(50, 1)]), + # 100 pixels per unit + "EXIF FocalPlaneXResolution": MockTag([exifread.utils.Ratio(100, 1)]), + "EXIF FocalPlaneResolutionUnit": MockTag([2]), # Inches + "EXIF ExifImageWidth": MockTag([1000]), # 10 inches wide + "EXIF ExifImageLength": MockTag([1000]), + } + + # Width in pixels = 1000 + # Pixels per inch = 100 + # Sensor width in inches = 10 + # Sensor width in mm = 10 * 25.4 = 254.0 + + e = create_exif_with_tags(tags, monkeypatch) + sensor_width = e.extract_sensor_width() + + assert abs(sensor_width - 254.0) < 0.01 + + _, focal_ratio = e.extract_focal() + + # Sensor width 254mm + # Ratio = 50 / 254 + assert abs(focal_ratio - (50.0 / 254.0)) < 0.001 + + +def test_orientation_parsing(monkeypatch): + tags = {"Image Orientation": MockTag([6])} + e = create_exif_with_tags(tags, monkeypatch) + assert e.extract_orientation() == 6 + + tags = {} + e = create_exif_with_tags(tags, monkeypatch) + assert e.extract_orientation() == 1 + + +def test_make_model_parsing_image_tags(monkeypatch): + tags = { + "Image Make": MockTag("TestMake"), + "Image Model": MockTag("TestModel"), + } + e = create_exif_with_tags(tags, monkeypatch) + assert e.extract_make() == "TestMake" + assert e.extract_model() == "TestModel" + + +def test_make_model_parsing_lens_tags(monkeypatch): + tags = { + "EXIF LensMake": MockTag("LensMake"), + "EXIF LensModel": MockTag("LensModel"), + } + e = create_exif_with_tags(tags, monkeypatch) + assert e.extract_make() == "LensMake" + assert e.extract_model() == "LensModel" + + +def test_capture_time_gps(monkeypatch): + tags = { + "GPS GPSDate": MockTag("2021:01:01"), + "GPS GPSTimeStamp": MockTag([exifread.utils.Ratio(12, 1), exifread.utils.Ratio(0, 1), exifread.utils.Ratio(0, 1)]), + } + e = create_exif_with_tags(tags, monkeypatch) + + delta = datetime.datetime(2021, 1, 1, 12, 0, 0) - \ + datetime.datetime(1970, 1, 1) + expected = delta.total_seconds() + assert e.extract_capture_time() == expected + + +def test_capture_time_exif(monkeypatch): + tags = { + "EXIF DateTimeOriginal": MockTag("2022:02:02 10:00:00"), + } + e = create_exif_with_tags(tags, monkeypatch) + + delta = datetime.datetime(2022, 2, 2, 10, 0, 0) - \ + datetime.datetime(1970, 1, 1) + expected = delta.total_seconds() + assert e.extract_capture_time() == expected + + +def test_focal35_to_focal_ratio_logic(): + # 3:2 ratio + ratio = exif.focal35_to_focal_ratio(35.0, 300, 200) + assert abs(ratio - 35.0 / 36.0) < 0.01 + + # 4:3 ratio + ratio = exif.focal35_to_focal_ratio(34.0, 400, 300) + assert abs(ratio - 34.0 / 34.0) < 0.01 + + # Inverse + f35 = exif.focal35_to_focal_ratio(35.0 / 36.0, 300, 200, inverse=True) + assert abs(f35 - 35.0) < 0.01 + + +def test_extract_geo_structure_coord(monkeypatch): + tags = { + "GPS GPSLatitude": MockTag([exifread.utils.Ratio(10, 1), exifread.utils.Ratio(0, 1), exifread.utils.Ratio(0, 1)]), + "GPS GPSLatitudeRef": MockTag("N"), + "GPS GPSLongitude": MockTag([exifread.utils.Ratio(20, 1), exifread.utils.Ratio(0, 1), exifread.utils.Ratio(0, 1)]), + "GPS GPSLongitudeRef": MockTag("E"), + } + + e = create_exif_with_tags(tags, monkeypatch) + geo = e.extract_geo() + + assert geo["latitude"] == 10.0 + assert geo["longitude"] == 20.0 + + +def test_extract_geo_structure_alt_dop(monkeypatch): + tags = { + "GPS GPSAltitude": MockTag([exifread.utils.Ratio(100, 1)]), + "GPS GPSDOP": MockTag([exifread.utils.Ratio(5, 10)]), # 0.5 + } + + e = create_exif_with_tags(tags, monkeypatch) + geo = e.extract_geo() + + assert geo["altitude"] == 100.0 + assert geo["dop"] == 0.5 + + +def test_integration_extract_exif_from_file(monkeypatch): + # This tests the module level function that wraps EXIF class + tags = { + "Image Make": MockTag("TestMake"), + "Image Model": MockTag("TestModel"), + "EXIF ExifImageWidth": MockTag([100]), + "EXIF ExifImageLength": MockTag([100]), + } + + monkeypatch.setattr("exifread.process_file", lambda f, details=False: tags) + monkeypatch.setattr("opensfm.exif.get_xmp", lambda f: []) + + fileobj = io.BytesIO(b"") + fileobj.name = "test.jpg" + + d = exif.extract_exif_from_file(fileobj, lambda: (100, 100), True) + + assert d["make"] == "TestMake" + assert d["width"] == 100 + assert "camera" in d diff --git a/opensfm/test/test_geo.py b/opensfm/test/test_geo.py index a6d534be4..6ed172411 100644 --- a/opensfm/test/test_geo.py +++ b/opensfm/test/test_geo.py @@ -1,3 +1,4 @@ +# pyre-strict import numpy as np from opensfm import geo, pygeo @@ -6,7 +7,7 @@ def test_ecef_lla_consistency() -> None: lla_before = [46.5274109, 6.5722075, 402.16] ecef = geo.ecef_from_lla(lla_before[0], lla_before[1], lla_before[2]) lla_after = geo.lla_from_ecef(ecef[0], ecef[1], ecef[2]) - assert np.allclose(lla_after, lla_before) + assert np.allclose(np.array(lla_after), lla_before) def test_ecef_lla_topocentric_consistency() -> None: @@ -18,7 +19,7 @@ def test_ecef_lla_topocentric_consistency() -> None: lla_after = geo.lla_from_topocentric( enu[0], enu[1], enu[2], lla_ref[0], lla_ref[1], lla_ref[2] ) - assert np.allclose(lla_after, lla_before) + assert np.allclose(np.array(lla_after), lla_before) def test_ecef_lla_consistency_pygeo() -> None: @@ -39,6 +40,22 @@ def test_ecef_lla_topocentric_consistency_pygeo() -> None: ) assert np.allclose(lla_after, lla_before) + def test_eq_geo() -> None: - assert geo.TopocentricConverter(40,30,0) == geo.TopocentricConverter(40,30,0) - assert geo.TopocentricConverter(40,32,0) != geo.TopocentricConverter(40,30,0) + assert geo.TopocentricConverter(40, 30, 0) == geo.TopocentricConverter(40, 30, 0) + assert geo.TopocentricConverter(40, 32, 0) != geo.TopocentricConverter(40, 30, 0) + + +def test_parse_projection() -> None: + from opensfm import io + proj_str = io._parse_projection_string("WGS84") + assert proj_str is None + + proj_str = io._parse_projection_string("WGS84 UTM 31N") + assert proj_str is not None + + proj = geo.construct_proj_transformer(proj_str) + easting, northing = 431760, 4582313.7 + lat, lon = 41.38946, 2.18378 + plat, plon = proj.transform(easting, northing) + assert np.allclose((lat, lon), (plat, plon)) diff --git a/opensfm/test/test_geometry.py b/opensfm/test/test_geometry.py index ccb5ec728..18704c607 100644 --- a/opensfm/test/test_geometry.py +++ b/opensfm/test/test_geometry.py @@ -1,3 +1,4 @@ +# pyre-strict import numpy as np from opensfm import geometry diff --git a/opensfm/test/test_io.py b/opensfm/test/test_io.py index 23c90c140..2b1e8bca8 100644 --- a/opensfm/test/test_io.py +++ b/opensfm/test/test_io.py @@ -1,13 +1,16 @@ +# pyre-strict import json import os.path from io import StringIO +from typing import List import numpy as np -from opensfm import io, pygeometry, types + +from opensfm import io, pygeometry, pymap, types from opensfm.test import data_generation, utils -filename = os.path.join( +filename: str = os.path.join( data_generation.DATA_PATH, "berlin", "reconstruction_example.json" ) @@ -15,7 +18,8 @@ def test_reconstructions_from_json_consistency() -> None: with open(filename) as fin: obj_before = json.loads(fin.read()) - obj_after = io.reconstructions_to_json(io.reconstructions_from_json(obj_before)) + obj_after = io.reconstructions_to_json( + io.reconstructions_from_json(obj_before)) assert obj_before[0]["cameras"] == obj_after[0]["cameras"] @@ -64,18 +68,6 @@ def test_reconstruction_to_ply() -> None: assert len(ply.splitlines()) > len(reconstructions[0].points) -def test_parse_projection() -> None: - proj = io._parse_projection("WGS84") - assert proj is None - - proj = io._parse_projection("WGS84 UTM 31N") - easting, northing = 431760, 4582313.7 - lat, lon = 41.38946, 2.18378 - assert proj - plat, plon = proj.transform(easting, northing) - assert np.allclose((lat, lon), (plat, plon)) - - def test_read_gcp_list() -> None: text = """WGS84 13.400740745 52.519134104 12.0792090446 2335.0 1416.7 01.jpg @@ -129,7 +121,7 @@ def test_read_write_ground_control_points() -> None: } """ - def check_points(points) -> None: + def check_points(points: List[pymap.GroundControlPoint]) -> None: assert len(points) == 2 p1, p2 = points if p1.id != "1": @@ -163,6 +155,8 @@ def test_json_to_and_from_metadata() -> None: "skey": "test", "gravity_down": [7, 8, 9], "compass": {"angle": 10, "accuracy": 45}, + "opk_angles": [1.0, 2.0, 3.0], + "opk_accuracy": 0.1, } m = io.json_to_pymap_metadata(obj) assert m.orientation.value == 10 @@ -173,6 +167,8 @@ def test_json_to_and_from_metadata() -> None: assert np.allclose(m.gravity_down.value, [7, 8, 9]) assert m.compass_angle.value == 10 assert m.compass_accuracy.value == 45 + assert np.allclose(m.opk_angles.value, [1.0, 2.0, 3.0]) + assert m.opk_accuracy.value == 0.1 assert obj == io.pymap_metadata_to_json(m) diff --git a/opensfm/test/test_matching.py b/opensfm/test/test_matching.py index c397b7ee1..0936772f4 100644 --- a/opensfm/test/test_matching.py +++ b/opensfm/test/test_matching.py @@ -1,15 +1,18 @@ +# pyre-strict from typing import Any, Dict, List, Set, Tuple import numpy as np -from opensfm import bow -from opensfm import config -from opensfm import matching -from opensfm import pairs_selection -from opensfm import pyfeatures -from opensfm.synthetic_data import synthetic_dataset +from numpy.typing import NDArray +from opensfm import bow, config, matching, pairs_selection, pyfeatures, pygeometry +from opensfm.synthetic_data import synthetic_dataset, synthetic_scene -def compute_words(features: np.ndarray, bag_of_words, num_words, bow_matcher_type) -> np.ndarray: +def compute_words( + features: NDArray, + bag_of_words: bow.BagOfWords, + num_words: int, + bow_matcher_type: str, +) -> NDArray: closest_words = bag_of_words.map_to_words(features, num_words, bow_matcher_type) if closest_words is None: return np.array([], dtype=np.int32) @@ -19,7 +22,7 @@ def compute_words(features: np.ndarray, bag_of_words, num_words, bow_matcher_typ def example_features( nfeatures: int, config: Dict[str, Any] -) -> Tuple[List[np.ndarray], List[np.ndarray]]: +) -> Tuple[List[NDArray], List[NDArray]]: words, frequencies = bow.load_bow_words_and_frequencies(config) bag_of_words = bow.BagOfWords(words, frequencies) @@ -80,7 +83,7 @@ def test_unfilter_matches() -> None: assert res[1][1] == 6 -def test_match_images(scene_synthetic) -> None: +def test_match_images(scene_synthetic: synthetic_scene.SyntheticInputData) -> None: reference = scene_synthetic.reconstruction synthetic = synthetic_dataset.SyntheticDataSet( reference, @@ -137,7 +140,9 @@ def test_ordered_pairs() -> None: } -def test_triangulation_inliers(pairs_and_their_E) -> None: +def test_triangulation_inliers( + pairs_and_their_E: List[Tuple[NDArray, NDArray, NDArray, pygeometry.Pose]], +) -> None: for f1, f2, _, pose in pairs_and_their_E: Rt = pose.get_cam_to_world()[:3] diff --git a/opensfm/test/test_multiview.py b/opensfm/test/test_multiview.py index ab943fe41..77dd7449f 100644 --- a/opensfm/test/test_multiview.py +++ b/opensfm/test/test_multiview.py @@ -1,18 +1,21 @@ +# pyre-strict import copy +from typing import List, Tuple import numpy as np -from opensfm import multiview -from opensfm import pygeometry -from opensfm import transformations as tf +from numpy.typing import NDArray +from opensfm import multiview, pygeometry, transformations as tf -def normalized(x: np.ndarray) -> np.ndarray: +def normalized(x: NDArray) -> NDArray: return x / np.linalg.norm(x) def test_motion_from_plane_homography() -> None: R = tf.random_rotation_matrix()[:3, :3] + # pyre-fixme[6]: For 1st argument expected `ndarray` but got `int`. t = normalized(2 * np.random.rand(3) - 1) + # pyre-fixme[6]: For 1st argument expected `ndarray` but got `int`. n = normalized(2 * np.random.rand(3) - 1) d = 2 * np.random.rand() - 1 scale = 2 * np.random.rand() - 1 @@ -33,10 +36,11 @@ def test_motion_from_plane_homography() -> None: assert any(goodness) -def test_essential_five_points(pairs_and_their_E) -> None: +def test_essential_five_points( + pairs_and_their_E: List[Tuple[NDArray, NDArray, NDArray, pygeometry.Pose]], +) -> None: exact_found = 0 for f1, f2, E, _ in pairs_and_their_E: - result = pygeometry.essential_five_points(f1[0:5, :], f2[0:5, :]) # run over the N solutions, looking for the exact one @@ -52,7 +56,9 @@ def test_essential_five_points(pairs_and_their_E) -> None: assert exact_found >= exacts -def test_absolute_pose_three_points(shots_and_their_points) -> None: +def test_absolute_pose_three_points( + shots_and_their_points: List[Tuple[pygeometry.Pose, NDArray, NDArray]], +) -> None: exact_found = 0 for pose, bearings, points in shots_and_their_points: result = pygeometry.absolute_pose_three_points(bearings, points) @@ -65,7 +71,9 @@ def test_absolute_pose_three_points(shots_and_their_points) -> None: assert exact_found >= exacts -def test_absolute_pose_n_points(shots_and_their_points) -> None: +def test_absolute_pose_n_points( + shots_and_their_points: List[Tuple[pygeometry.Pose, NDArray, NDArray]], +) -> None: for pose, bearings, points in shots_and_their_points: result = pygeometry.absolute_pose_n_points(bearings, points) @@ -73,7 +81,9 @@ def test_absolute_pose_n_points(shots_and_their_points) -> None: assert np.linalg.norm(expected - result, ord="fro") < 1e-5 -def test_absolute_pose_n_points_known_rotation(shots_and_their_points) -> None: +def test_absolute_pose_n_points_known_rotation( + shots_and_their_points: List[Tuple[pygeometry.Pose, NDArray, NDArray]], +) -> None: for pose, bearings, points in shots_and_their_points: R = pose.get_rotation_matrix() p_rotated = np.array([R.dot(p) for p in points]) @@ -82,9 +92,10 @@ def test_absolute_pose_n_points_known_rotation(shots_and_their_points) -> None: assert np.linalg.norm(pose.translation - result) < 1e-6 -def test_essential_n_points(pairs_and_their_E) -> None: +def test_essential_n_points( + pairs_and_their_E: List[Tuple[NDArray, NDArray, NDArray, NDArray]], +) -> None: for f1, f2, E, _ in pairs_and_their_E: - f1 /= np.linalg.norm(f1, axis=1)[:, None] f2 /= np.linalg.norm(f2, axis=1)[:, None] @@ -98,9 +109,10 @@ def test_essential_n_points(pairs_and_their_E) -> None: assert np.linalg.norm(E - E_found, ord="fro") < 1e-6 -def test_relative_pose_from_essential(pairs_and_their_E) -> None: +def test_relative_pose_from_essential( + pairs_and_their_E: List[Tuple[NDArray, NDArray, NDArray, pygeometry.Pose]], +) -> None: for f1, f2, E, pose in pairs_and_their_E: - result = pygeometry.relative_pose_from_essential(E, f1, f2) pose = copy.deepcopy(pose) @@ -110,9 +122,10 @@ def test_relative_pose_from_essential(pairs_and_their_E) -> None: assert np.allclose(expected, result, rtol=1e-10) -def test_relative_rotation(pairs_and_their_E) -> None: +def test_relative_rotation( + pairs_and_their_E: List[Tuple[NDArray, NDArray, NDArray, pygeometry.Pose]], +) -> None: for f1, _, _, _ in pairs_and_their_E: - vec_x = np.random.rand(3) vec_x /= np.linalg.norm(vec_x) vec_y = np.array([-vec_x[1], vec_x[0], 0.0]) @@ -129,7 +142,9 @@ def test_relative_rotation(pairs_and_their_E) -> None: assert np.allclose(rotation, result, rtol=1e-10) -def test_relative_pose_refinement(pairs_and_their_E) -> None: +def test_relative_pose_refinement( + pairs_and_their_E: List[Tuple[NDArray, NDArray, NDArray, pygeometry.Pose]], +) -> None: exact_found = 0 for f1, f2, _, pose in pairs_and_their_E: pose = copy.deepcopy(pose) diff --git a/opensfm/test/test_pairs_selection.py b/opensfm/test/test_pairs_selection.py index 3fdc04145..d9b22cb19 100644 --- a/opensfm/test/test_pairs_selection.py +++ b/opensfm/test/test_pairs_selection.py @@ -1,19 +1,21 @@ +# pyre-strict import argparse import os.path -from typing import Any, Dict, Generator +from typing import Any, Dict, Iterator import numpy as np import pytest -from opensfm import commands, dataset, feature_loader, pairs_selection, geo -from opensfm.test import data_generation + +from opensfm import commands, dataset, feature_loader, geo, pairs_selection from opensfm.dataset_base import DataSetBase +from opensfm.test import data_generation NEIGHBORS = 6 @pytest.fixture(scope="module", autouse=True) -def clear_cache() -> Generator[None, Any, Any]: +def clear_cache() -> Iterator[None]: """ Clear feature loader cache to avoid using cached masks etc from berlin dataset which has the same @@ -25,7 +27,7 @@ def clear_cache() -> Generator[None, Any, Any]: @pytest.fixture(scope="module", autouse=True) -def lund_path(tmpdir_factory) -> str: +def lund_path(tmpdir_factory: Any) -> str: """ Precompute exif and features to avoid doing it for every test which is time consuming. @@ -76,8 +78,8 @@ def match_candidates_from_metadata( assert count >= assert_count -def create_match_candidates_config(**kwargs) -> Dict[str, Any]: - config = { +def create_match_candidates_config(**kwargs: Any) -> Dict[str, Any]: + config: Dict[str, Any] = { "matcher_type": "BRUTEFORCE", "matching_gps_distance": 0, "matching_gps_neighbors": 0, @@ -94,37 +96,37 @@ def create_match_candidates_config(**kwargs) -> Dict[str, Any]: return config -def test_match_candidates_from_metadata_vlad(lund_path) -> None: +def test_match_candidates_from_metadata_vlad(lund_path: str) -> None: config = create_match_candidates_config(matching_vlad_neighbors=NEIGHBORS) data_generation.save_config(config, lund_path) data = dataset.DataSet(lund_path) match_candidates_from_metadata(data, assert_count=5) -def test_match_candidates_from_metadata_bow(lund_path) -> None: +def test_match_candidates_from_metadata_bow(lund_path: str) -> None: config = create_match_candidates_config( matching_bow_neighbors=NEIGHBORS, matcher_type="WORDS" ) data_generation.save_config(config, lund_path) data = dataset.DataSet(lund_path) - match_candidates_from_metadata(data, assert_count=5) + match_candidates_from_metadata(data, assert_count=4) -def test_match_candidates_from_metadata_gps(lund_path) -> None: +def test_match_candidates_from_metadata_gps(lund_path: str) -> None: config = create_match_candidates_config(matching_gps_neighbors=NEIGHBORS) data_generation.save_config(config, lund_path) data = dataset.DataSet(lund_path) match_candidates_from_metadata(data) -def test_match_candidates_from_metadata_time(lund_path) -> None: +def test_match_candidates_from_metadata_time(lund_path: str) -> None: config = create_match_candidates_config(matching_time_neighbors=NEIGHBORS) data_generation.save_config(config, lund_path) data = dataset.DataSet(lund_path) match_candidates_from_metadata(data) -def test_match_candidates_from_metadata_graph(lund_path) -> None: +def test_match_candidates_from_metadata_graph(lund_path: str) -> None: config = create_match_candidates_config(matching_graph_rounds=50) data_generation.save_config(config, lund_path) data = dataset.DataSet(lund_path) @@ -169,7 +171,7 @@ def test_find_best_altitude_convergent() -> None: "1": np.array([1.0, 0.0, -1.0]), } altitude = pairs_selection.find_best_altitude(origins, directions) - assert np.allclose([altitude], [2.0], atol=1e-2) + assert np.allclose([altitude], [1.0], atol=1e-2) def test_find_best_altitude_divergent() -> None: diff --git a/opensfm/test/test_reconstruction_alignment.py b/opensfm/test/test_reconstruction_alignment.py index fb2f156c4..008230ff5 100644 --- a/opensfm/test/test_reconstruction_alignment.py +++ b/opensfm/test/test_reconstruction_alignment.py @@ -1,18 +1,23 @@ +# pyre-strict import numpy as np -from opensfm import pybundle -from opensfm import pygeometry +from numpy.typing import NDArray +from opensfm import pybundle, pygeometry -def get_shot_origin(shot) -> np.ndarray: +def get_shot_origin(shot: pybundle.RAShot) -> NDArray: """Compute the origin of a shot.""" - pose = pygeometry.Pose([shot.rx, shot.ry, shot.rz], [shot.tx, shot.ty, shot.tz]) + pose = pygeometry.Pose( + np.array([shot.rx, shot.ry, shot.rz]), np.array([shot.tx, shot.ty, shot.tz]) + ) return pose.get_origin() -def get_reconstruction_origin(r) -> np.ndarray: +def get_reconstruction_origin(r: pybundle.RAReconstruction) -> NDArray: """Compute the origin of a reconstruction.""" s = r.scale - pose = pygeometry.Pose(np.array([r.rx, r.ry, r.rz]), np.array([r.tx / s, r.ty / s, r.tz / s])) + pose = pygeometry.Pose( + np.array([r.rx, r.ry, r.rz]), np.array([r.tx / s, r.ty / s, r.tz / s]) + ) return pose.get_origin() diff --git a/opensfm/test/test_reconstruction_incremental.py b/opensfm/test/test_reconstruction_incremental.py index 4ad885943..f74e23140 100644 --- a/opensfm/test/test_reconstruction_incremental.py +++ b/opensfm/test/test_reconstruction_incremental.py @@ -1,3 +1,4 @@ +# pyre-strict from opensfm import reconstruction from opensfm.synthetic_data import synthetic_dataset, synthetic_scene @@ -16,6 +17,8 @@ def test_reconstruction_incremental( dataset.config["bundle_compensate_gps_bias"] = True dataset.config["bundle_use_gcp"] = True + dataset.config["bundle_max_iterations"] = 20 + dataset.config["processes"] = 4 _, reconstructed_scene = reconstruction.incremental_reconstruction( dataset, scene_synthetic.tracks_manager ) @@ -31,20 +34,20 @@ def test_reconstruction_incremental( assert errors["ratio_cameras"] == 1.0 assert 0.7 < errors["ratio_points"] < 1.0 - assert 0 < errors["aligned_position_rmse"] < 0.03 - assert 0 < errors["aligned_rotation_rmse"] < 0.0022 + assert 0 < errors["aligned_position_rmse"] < 0.045 + assert 0 < errors["aligned_rotation_rmse"] < 0.0035 assert 0 < errors["aligned_points_rmse"] < 0.1 # Sanity check that GPS error is similar to the generated gps_noise assert 4.0 < errors["absolute_gps_rmse"] < 7.0 # Sanity check that GCP error is similar to the generated gcp_noise - assert 0.01 < errors["absolute_gcp_rmse_horizontal"] < 0.05 + assert 0.01 < errors["absolute_gcp_rmse_horizontal"] < 0.068 assert 0.08 < errors["absolute_gcp_rmse_vertical"] < 0.18 # Check that the GPS bias (only translation) is recovered translation = reconstructed_scene[0].biases["1"].translation - assert 9.9 < translation[0] < 10.312 + assert 9.9 < translation[0] < 10.34 assert 99.9 < translation[2] < 100.2 diff --git a/opensfm/test/test_reconstruction_resect.py b/opensfm/test/test_reconstruction_resect.py index 3a5e1f4dc..ca6703768 100644 --- a/opensfm/test/test_reconstruction_resect.py +++ b/opensfm/test/test_reconstruction_resect.py @@ -1,6 +1,10 @@ +# pyre-strict +from typing import Tuple + import numpy as np +from numpy.typing import NDArray from opensfm import config, multiview, pymap, reconstruction, types -from typing import Tuple + def test_corresponding_tracks() -> None: t1 = {"1": pymap.Observation(1.0, 1.0, 1.0, 0, 0, 0, 1, 1, 1)} @@ -46,7 +50,10 @@ def test_corresponding_tracks() -> None: def copy_cluster_points( - cluster: types.Reconstruction, tracks_manager: pymap.TracksManager, points, noise + cluster: types.Reconstruction, + tracks_manager: pymap.TracksManager, + points: pymap.LandmarkView, + noise: float, ) -> types.Reconstruction: for shot in cluster.shots: for point in tracks_manager.get_shot_observations(shot): @@ -58,13 +65,16 @@ def copy_cluster_points( def split_synthetic_reconstruction( - scene, tracks_manager: pymap.TracksManager, cluster_size, point_noise + scene: types.Reconstruction, + tracks_manager: pymap.TracksManager, + cluster_size: int, + point_noise: float, ) -> Tuple[types.Reconstruction, types.Reconstruction]: cluster1 = types.Reconstruction() cluster2 = types.Reconstruction() cluster1.cameras = scene.cameras cluster2.cameras = scene.cameras - for (i, shot) in zip(range(len(scene.shots)), scene.shots.values()): + for i, shot in zip(range(len(scene.shots)), scene.shots.values()): if i >= cluster_size: cluster2.add_shot(shot) if i <= cluster_size: @@ -75,7 +85,9 @@ def split_synthetic_reconstruction( return cluster1, cluster2 -def move_and_scale_cluster(cluster: types.Reconstruction)->Tuple[types.Reconstruction, np.ndarray, float]: +def move_and_scale_cluster( + cluster: types.Reconstruction, +) -> Tuple[types.Reconstruction, NDArray, float]: scale = np.random.rand(1) translation = np.random.rand(3) for point in cluster.points.values(): @@ -83,7 +95,9 @@ def move_and_scale_cluster(cluster: types.Reconstruction)->Tuple[types.Reconstru return cluster, translation, scale -def test_absolute_pose_generalized_shot(scene_synthetic_cube) -> None: +def test_absolute_pose_generalized_shot( + scene_synthetic_cube: Tuple[types.Reconstruction, pymap.TracksManager], +) -> None: """Whole reconstruction resection (generalized pose) on a toy reconstruction with 0.01 meter point noise and zero outliers.""" noise = 0.01 diff --git a/opensfm/test/test_reconstruction_shot_neighborhood.py b/opensfm/test/test_reconstruction_shot_neighborhood.py deleted file mode 100644 index d302a7fa7..000000000 --- a/opensfm/test/test_reconstruction_shot_neighborhood.py +++ /dev/null @@ -1,171 +0,0 @@ -import networkx as nx -from opensfm import pygeometry -from opensfm import pymap -from opensfm import pysfm -from opensfm import reconstruction -from opensfm import types - - -def _add_shot(rec, shot_id, cam) -> None: - rec.create_shot(shot_id, cam.id) - - -def _add_point(rec, point_id, observations) -> None: - rec.create_point(point_id) - for shot_id in observations: - obs = pymap.Observation(100, 200, 0.5, 255, 0, 0, int(point_id)) - rec.add_observation(shot_id, point_id, obs) - - -def test_shot_neighborhood_linear_graph() -> None: - rec = types.Reconstruction() - cam = pygeometry.Camera.create_perspective(0.5, 0, 0) - cam.id = "cam1" - rec.add_camera(cam) - _add_shot(rec, "im0", cam) - for i in range(1, 4): - p, n = "im" + str(i - 1), "im" + str(i) - _add_shot(rec, n, cam) - _add_point(rec, str(i), [p, n]) - - interior, boundary = reconstruction.shot_neighborhood( - rec, "im2", radius=1, min_common_points=1, max_interior_size=10 - ) - assert interior == {"im2"} - assert boundary == {"im1", "im3"} - - interior, boundary = reconstruction.shot_neighborhood( - rec, "im2", radius=2, min_common_points=1, max_interior_size=10 - ) - assert interior == {"im1", "im2", "im3"} - assert boundary == {"im0"} - - interior, boundary = reconstruction.shot_neighborhood( - rec, "im2", radius=3, min_common_points=1, max_interior_size=10 - ) - assert interior == {"im0", "im1", "im2", "im3"} - assert boundary == set() - - interior, boundary = reconstruction.shot_neighborhood( - rec, "im2", radius=3, min_common_points=1, max_interior_size=3 - ) - assert interior == {"im1", "im2", "im3"} - assert boundary == {"im0"} - - -def test_shot_neighborhood_linear_graph_cpp() -> None: - rec = types.Reconstruction() - cam = pygeometry.Camera.create_perspective(0.5, 0, 0) - cam.id = "cam1" - rec.add_camera(cam) - _add_shot(rec, "im0", cam) - for i in range(1, 4): - p, n = "im" + str(i - 1), "im" + str(i) - _add_shot(rec, n, cam) - _add_point(rec, str(i), [p, n]) - - interior1, boundary1 = pysfm.BAHelpers.shot_neighborhood_ids( - rec.map, "im2", 1, 1, 10 - ) - assert interior1 == {"im2"} - assert boundary1 == {"im1", "im3"} - - interior2, boundary2 = pysfm.BAHelpers.shot_neighborhood_ids( - rec.map, "im2", 2, 1, 10 - ) - assert interior2 == {"im1", "im2", "im3"} - assert boundary2 == {"im0"} - - interior3, boundary3 = pysfm.BAHelpers.shot_neighborhood_ids( - rec.map, "im2", 3, 1, 10 - ) - assert interior3 == {"im0", "im1", "im2", "im3"} - assert boundary3 == set() - - interior4, boundary4 = pysfm.BAHelpers.shot_neighborhood_ids( - rec.map, "im2", 3, 1, 3 - ) - assert interior4 == {"im1", "im2", "im3"} - assert boundary4 == {"im0"} - - -def test_shot_neighborhood_complete_graph() -> None: - rec = types.Reconstruction() - cam = pygeometry.Camera.create_perspective(0.5, 0, 0) - cam.id = "cam1" - rec.add_camera(cam) - for i in range(4): - _add_shot(rec, "im" + str(i), cam) - _add_point(rec, "1", rec.shots.keys()) - - interior, boundary = reconstruction.shot_neighborhood( - rec, "im2", radius=2, min_common_points=1, max_interior_size=10 - ) - assert interior == {"im0", "im1", "im2", "im3"} - assert boundary == set() - - -def test_shot_neighborhood_sorted_results() -> None: - rec = types.Reconstruction() - cam = pygeometry.Camera.create_perspective(0.5, 0, 0) - cam.id = "cam1" - rec.add_camera(cam) - _add_shot(rec, "im0", cam) - _add_shot(rec, "im1", cam) - _add_shot(rec, "im2", cam) - _add_point(rec, "1", ["im0", "im1"]) - _add_point(rec, "2", ["im0", "im1"]) - _add_point(rec, "3", ["im0", "im2"]) - - interior, boundary = reconstruction.shot_neighborhood( - rec, "im0", radius=2, min_common_points=1, max_interior_size=2 - ) - assert interior == {"im0", "im1"} - assert boundary == {"im2"} - - _add_point(rec, "4", ["im0", "im2"]) - _add_point(rec, "5", ["im0", "im2"]) - - interior, boundary = reconstruction.shot_neighborhood( - rec, "im0", radius=2, min_common_points=1, max_interior_size=2 - ) - assert interior == {"im0", "im2"} - assert boundary == {"im1"} - - -def test_shot_neighborhood_complete_graph_cpp() -> None: - rec = types.Reconstruction() - cam = pygeometry.Camera.create_perspective(0.5, 0, 0) - cam.id = "cam1" - rec.add_camera(cam) - for i in range(4): - _add_shot(rec, "im" + str(i), cam) - _add_point(rec, "1", rec.shots.keys()) - - interior, boundary = pysfm.BAHelpers.shot_neighborhood_ids(rec.map, "im2", 2, 1, 10) - assert interior == {"im0", "im1", "im2", "im3"} - assert boundary == set() - - -def test_shot_neighborhood_sorted_results_cpp() -> None: - rec = types.Reconstruction() - cam = pygeometry.Camera.create_perspective(0.5, 0, 0) - cam.id = "cam1" - rec.add_camera(cam) - _add_shot(rec, "im0", cam) - _add_shot(rec, "im1", cam) - _add_shot(rec, "im2", cam) - _add_point(rec, "1", ["im0", "im1"]) - _add_point(rec, "2", ["im0", "im1"]) - _add_point(rec, "3", ["im0", "im2"]) - - interior, boundary = pysfm.BAHelpers.shot_neighborhood_ids(rec.map, "im0", 2, 1, 2) - assert interior == {"im0", "im1"} - assert boundary == {"im2"} - - _add_point(rec, "4", ["im0", "im2"]) - _add_point(rec, "5", ["im0", "im2"]) - - interior, boundary = pysfm.BAHelpers.shot_neighborhood_ids(rec.map, "im0", 2, 1, 2) - assert interior == {"im0", "im2"} - assert boundary == {"im1"} diff --git a/opensfm/test/test_reconstruction_triangulation.py b/opensfm/test/test_reconstruction_triangulation.py index a05056681..e2dfba95b 100644 --- a/opensfm/test/test_reconstruction_triangulation.py +++ b/opensfm/test/test_reconstruction_triangulation.py @@ -1,3 +1,4 @@ +# pyre-strict from opensfm import reconstruction from opensfm.synthetic_data import synthetic_dataset, synthetic_scene @@ -39,7 +40,7 @@ def test_reconstruction_triangulation( assert 0.01 < errors["absolute_gps_rmse"] < 0.1 # Sanity check that GCP error is similar to the generated gcp_noise - assert 0.01 < errors["absolute_gcp_rmse_horizontal"] < 0.05 + assert 0.01 < errors["absolute_gcp_rmse_horizontal"] < 0.056 assert 0.005 < errors["absolute_gcp_rmse_vertical"] < 0.04 # Check that the GPS bias (only translation) is recovered diff --git a/opensfm/test/test_rig.py b/opensfm/test/test_rig.py index 56fd95161..3e41575b2 100644 --- a/opensfm/test/test_rig.py +++ b/opensfm/test/test_rig.py @@ -1,4 +1,6 @@ +# pyre-strict """Test the rig module.""" + import numpy as np from opensfm import pygeometry, rig, types @@ -87,10 +89,18 @@ def test_compute_relative_pose() -> None: rec.add_camera(camera4) # First rig instance - rec.create_shot("shot1", "camera1", pygeometry.Pose(np.array([0, 0, 0]), np.array([-2, -2, 0]))) - rec.create_shot("shot2", "camera2", pygeometry.Pose(np.array([0, 0, 0]), np.array([-3, -3, 0]))) - rec.create_shot("shot3", "camera3", pygeometry.Pose(np.array([0, 0, 0]), np.array([-1, -3, 0]))) - rec.create_shot("shot4", "camera4", pygeometry.Pose(np.array([0, 0, 0]), np.array([-2, -4, 0]))) + rec.create_shot( + "shot1", "camera1", pygeometry.Pose(np.array([0, 0, 0]), np.array([-2, -2, 0])) + ) + rec.create_shot( + "shot2", "camera2", pygeometry.Pose(np.array([0, 0, 0]), np.array([-3, -3, 0])) + ) + rec.create_shot( + "shot3", "camera3", pygeometry.Pose(np.array([0, 0, 0]), np.array([-1, -3, 0])) + ) + rec.create_shot( + "shot4", "camera4", pygeometry.Pose(np.array([0, 0, 0]), np.array([-2, -4, 0])) + ) # Second rig instance (rotated by pi/2 around Z) pose_instance = pygeometry.Pose(np.array([0, 0, -1.5707963])) diff --git a/opensfm/test/test_robust.py b/opensfm/test/test_robust.py index ed296dd4c..6954febc3 100644 --- a/opensfm/test/test_robust.py +++ b/opensfm/test/test_robust.py @@ -1,18 +1,20 @@ +# pyre-strict import copy -from typing import Tuple +from typing import List, Tuple import numpy as np -from opensfm import pyrobust, pygeometry +from numpy.typing import NDArray +from opensfm import pygeometry, pyrobust -def line_data() -> Tuple[int, int, np.ndarray, int]: +def line_data() -> Tuple[int, int, NDArray, int]: a, b = 2, 3 samples = 100 x = np.linspace(0, 100, samples) return a, b, x, samples -def similarity_data() -> Tuple[np.ndarray, np.ndarray, int, np.ndarray, int]: +def similarity_data() -> Tuple[NDArray, NDArray, int, NDArray, int]: rotation = np.array([0.1, 0.2, 0.3]) translation = np.array([4, 5, 6]) scale = 2 @@ -22,7 +24,7 @@ def similarity_data() -> Tuple[np.ndarray, np.ndarray, int, np.ndarray, int]: return rotation, translation, scale, x, samples -def add_outliers(ratio_outliers: float, x: np.ndarray, min: float, max: float) -> None: +def add_outliers(ratio_outliers: float, x: NDArray, min: float, max: float) -> None: for index in np.random.permutation(len(x))[: int(ratio_outliers * len(x))]: shape = x[index].shape noise = np.random.uniform(min, max, size=shape) @@ -185,7 +187,9 @@ def test_outliers_similarity_ransac() -> None: ) -def test_uniform_essential_ransac(pairs_and_their_E) -> None: +def test_uniform_essential_ransac( + pairs_and_their_E: List[Tuple[NDArray, NDArray, NDArray, pygeometry.Pose]], +) -> None: for f1, f2, _, _ in pairs_and_their_E: points = np.concatenate((f1, f2), axis=1) @@ -206,7 +210,9 @@ def test_uniform_essential_ransac(pairs_and_their_E) -> None: assert len(result.inliers_indices) == len(f1) == len(f2) -def test_outliers_essential_ransac(pairs_and_their_E) -> None: +def test_outliers_essential_ransac( + pairs_and_their_E: List[Tuple[NDArray, NDArray, NDArray, pygeometry.Pose]], +) -> None: for f1, f2, _, _ in pairs_and_their_E: points = np.concatenate((f1, f2), axis=1) @@ -232,7 +238,9 @@ def test_outliers_essential_ransac(pairs_and_their_E) -> None: assert np.isclose(len(result.inliers_indices), inliers_count, rtol=tolerance) -def test_outliers_relative_pose_ransac(pairs_and_their_E) -> None: +def test_outliers_relative_pose_ransac( + pairs_and_their_E: List[Tuple[NDArray, NDArray, NDArray, pygeometry.Pose]], +) -> None: for f1, f2, _, pose in pairs_and_their_E: points = np.concatenate((f1, f2), axis=1) @@ -260,12 +268,14 @@ def test_outliers_relative_pose_ransac(pairs_and_their_E) -> None: inliers_count = (1 - ratio_outliers) * len(points) assert np.isclose(len(result.inliers_indices), inliers_count, rtol=tolerance) + # pyre-fixme[61]: `expected` is undefined, or not always defined. assert np.linalg.norm(expected - result.lo_model, ord="fro") < 16e-2 -def test_outliers_relative_rotation_ransac(pairs_and_their_E) -> None: +def test_outliers_relative_rotation_ransac( + pairs_and_their_E: List[Tuple[NDArray, NDArray, NDArray, pygeometry.Pose]], +) -> None: for f1, _, _, _ in pairs_and_their_E: - vec_x = np.random.rand(3) vec_x /= np.linalg.norm(vec_x) vec_y = np.array([-vec_x[1], vec_x[0], 0.0]) @@ -303,7 +313,9 @@ def test_outliers_relative_rotation_ransac(pairs_and_their_E) -> None: assert np.linalg.norm(rotation - result.lo_model, ord="fro") < 8e-2 -def test_outliers_absolute_pose_ransac(shots_and_their_points) -> None: +def test_outliers_absolute_pose_ransac( + shots_and_their_points: List[Tuple[pygeometry.Pose, NDArray, NDArray]], +) -> None: for pose, bearings, points in shots_and_their_points: scale = 1e-3 bearings = copy.deepcopy(bearings) @@ -328,7 +340,9 @@ def test_outliers_absolute_pose_ransac(shots_and_their_points) -> None: assert np.linalg.norm(expected - result.lo_model, ord="fro") < 8e-2 -def test_outliers_absolute_pose_known_rotation_ransac(shots_and_their_points) -> None: +def test_outliers_absolute_pose_known_rotation_ransac( + shots_and_their_points: List[Tuple[pygeometry.Pose, NDArray, NDArray]], +) -> None: for pose, bearings, points in shots_and_their_points: scale = 1e-3 bearings = copy.deepcopy(bearings) diff --git a/opensfm/test/test_stats.py b/opensfm/test/test_stats.py index b486794eb..c7d6416dd 100644 --- a/opensfm/test/test_stats.py +++ b/opensfm/test/test_stats.py @@ -1,3 +1,4 @@ +# pyre-strict from opensfm import stats, types from opensfm.synthetic_data import synthetic_dataset, synthetic_scene @@ -129,7 +130,7 @@ def test_reconstruction_statistics_normal( ) assert reconstruction_statistics["components"] == 1 - assert not reconstruction_statistics["has_gps"] + assert reconstruction_statistics["has_gps"] assert not reconstruction_statistics["has_gcp"] assert 4900 < reconstruction_statistics["initial_points_count"] < 5000 assert reconstruction_statistics["initial_shots_count"] == 20 @@ -256,7 +257,9 @@ def test_gps_errors_normal( ) -> None: reference = scene_synthetic.reconstruction gps_errors = stats.gps_errors([reference]) - assert gps_errors == {} + assert set(gps_errors.keys()) == {"average_error", "error", "mean", "std"} + # scene_synthetic generated GPS noise is 5 meters + assert 3.0 < gps_errors["average_error"] < 7.0 def test_gps_errors_null( diff --git a/opensfm/test/test_synthetic_metrics.py b/opensfm/test/test_synthetic_metrics.py new file mode 100644 index 000000000..77644b160 --- /dev/null +++ b/opensfm/test/test_synthetic_metrics.py @@ -0,0 +1,36 @@ +# pyre-strict +import numpy as np +from opensfm.synthetic_data import synthetic_metrics, synthetic_scene + + +def test_change_geo_reference( + scene_synthetic: synthetic_scene.SyntheticInputData, +) -> None: + original = scene_synthetic.reconstruction + lat = original.reference.lat + 0.001 # about 111 m + lon = original.reference.lon + 0.002 + alt = original.reference.alt + 2.34 + + aligned = synthetic_metrics.change_geo_reference(original, lat, lon, alt) + + for shot_id in original.shots: + original_position = original.shots[shot_id].pose.get_origin() + aligned_position = aligned.shots[shot_id].pose.get_origin() + aligned_lla = aligned.reference.to_lla(*aligned_position) + aligned_in_original = original.reference.to_topocentric(*aligned_lla) + assert np.allclose(original_position, aligned_in_original, atol=0.01) + + assert original.shots[shot_id].metadata.gps_position.has_value + assert aligned.shots[shot_id].metadata.gps_position.has_value + original_gps_prior = original.shots[shot_id].metadata.gps_position.value + aligned_gps_prior = aligned.shots[shot_id].metadata.gps_position.value + aligned_gps_lla = aligned.reference.to_lla(*aligned_gps_prior) + aligned_gps_in_original = original.reference.to_topocentric(*aligned_gps_lla) + assert np.allclose(original_gps_prior, aligned_gps_in_original, atol=0.01) + + for point_id in original.points: + original_position = original.points[point_id].coordinates + aligned_position = aligned.points[point_id].coordinates + aligned_lla = aligned.reference.to_lla(*aligned_position) + aligned_in_original = original.reference.to_topocentric(*aligned_lla) + assert np.allclose(original_position, aligned_in_original, atol=0.01) diff --git a/opensfm/test/test_triangulation.py b/opensfm/test/test_triangulation.py index 1620931c9..b812afddf 100644 --- a/opensfm/test/test_triangulation.py +++ b/opensfm/test/test_triangulation.py @@ -1,8 +1,7 @@ +# pyre-strict import numpy as np -from opensfm import io -from opensfm import pygeometry -from opensfm import pymap -from opensfm import reconstruction +from numpy.typing import ArrayLike, NDArray +from opensfm import io, pygeometry, pymap, reconstruction def test_track_triangulator_spherical() -> None: @@ -30,8 +29,8 @@ def test_track_triangulator_spherical() -> None: }, "im2": { "camera": "theta", - "rotation": [0, 0, 0.0], - "translation": [-1, 0, 0.0], + "rotation": [0.0, 0.0, 0.0], + "translation": [-1.0, 0.0, 0.0], }, }, "points": {}, @@ -41,14 +40,69 @@ def test_track_triangulator_spherical() -> None: triangulator = reconstruction.TrackTriangulator( rec, reconstruction.TrackHandlerTrackManager(tracks_manager, rec) ) - triangulator.triangulate("1", 0.01, 2.0, 10) + triangulator.triangulate( + "1", + reproj_threshold=0.01, + min_ray_angle_degrees=2.0, + min_depth=0.001, + iterations=10, + ) assert "1" in rec.points p = rec.points["1"].coordinates assert np.allclose(p, [0, 0, 1.3763819204711]) assert len(rec.points["1"].get_observations()) == 2 -def unit_vector(x: object) -> np.ndarray: +def test_track_triangulator_coincident_camera_origins() -> None: + """Test triangulating tracks when two cameras have the same origin. + + Triangulation should fail and no points should be added to the reconstruction. + """ + tracks_manager = pymap.TracksManager() + tracks_manager.add_observation("im1", "1", pymap.Observation(0, 0, 1.0, 0, 0, 0, 0)) + tracks_manager.add_observation( + "im2", "1", pymap.Observation(-0.1, 0, 1.0, 0, 0, 0, 1) + ) + + rec = io.reconstruction_from_json( + { + "cameras": { + "theta": { + "projection_type": "spherical", + "width": 800, + "height": 400, + } + }, + "shots": { + "im1": { + "camera": "theta", + "rotation": [0.0, 0.0, 0.0], + "translation": [0.0, 0.0, 0.0], + }, + "im2": { + "camera": "theta", + "rotation": [0.0, 0.0, 0.0], + "translation": [0.0, 0.0, 0.0], + }, + }, + "points": {}, + } + ) + + triangulator = reconstruction.TrackTriangulator( + rec, reconstruction.TrackHandlerTrackManager(tracks_manager, rec) + ) + triangulator.triangulate( + "1", + reproj_threshold=0.01, + min_ray_angle_degrees=2.0, + min_depth=0.0001, + iterations=10, + ) + assert not rec.points + + +def unit_vector(x: ArrayLike) -> NDArray: return np.array(x) / np.linalg.norm(x) @@ -59,13 +113,28 @@ def test_triangulate_bearings_dlt() -> None: b2 = unit_vector([-1.0, 0, 1]) max_reprojection = 0.01 min_ray_angle = np.radians(2.0) + min_depth = 0.001 res, X = pygeometry.triangulate_bearings_dlt( - [rt1, rt2], np.asarray([b1, b2]), max_reprojection, min_ray_angle + [rt1, rt2], np.asarray([b1, b2]), max_reprojection, min_ray_angle, min_depth ) assert np.allclose(X, [0, 0, 1.0]) assert res is True +def test_triangulate_bearings_dlt_coincident_camera_origins() -> None: + rt1 = np.append(np.identity(3), [[0], [0], [0]], axis=1) + rt2 = np.append(np.identity(3), [[0], [0], [0]], axis=1) # same origin + b1 = unit_vector([0.0, 0, 1]) + b2 = unit_vector([-1.0, 0, 1]) + max_reprojection = 0.01 + min_ray_angle = np.radians(2.0) + min_depth = 0.001 + res, X = pygeometry.triangulate_bearings_dlt( + [rt1, rt2], np.asarray([b1, b2]), max_reprojection, min_ray_angle, min_depth + ) + assert res is False + + def test_triangulate_bearings_midpoint() -> None: o1 = np.array([0.0, 0, 0]) b1 = unit_vector([0.0, 0, 1]) @@ -73,17 +142,36 @@ def test_triangulate_bearings_midpoint() -> None: b2 = unit_vector([-1.0, 0, 1]) max_reprojection = 0.01 min_ray_angle = np.radians(2.0) + min_depth = 0.001 valid_triangulation, X = pygeometry.triangulate_bearings_midpoint( np.asarray([o1, o2]), np.asarray([b1, b2]), 2 * [max_reprojection], min_ray_angle, - np.pi - min_ray_angle, + min_depth, ) assert np.allclose(X, [0, 0, 1.0]) assert valid_triangulation is True +def test_triangulate_bearings_midpoint_coincident_camera_origins() -> None: + o1 = np.array([0.0, 0, 0]) + b1 = unit_vector([0.0, 0, 1]) + o2 = np.array([0.0, 0, 0]) # same origin + b2 = unit_vector([-1.0, 0, 1]) + max_reprojection = 0.01 + min_ray_angle = np.radians(2.0) + min_depth = 0.001 + valid_triangulation, X = pygeometry.triangulate_bearings_midpoint( + np.asarray([o1, o2]), + np.asarray([b1, b2]), + 2 * [max_reprojection], + min_ray_angle, + min_depth, + ) + assert valid_triangulation is False + + def test_triangulate_two_bearings_midpoint() -> None: o1 = np.array([0.0, 0, 0]) b1 = unit_vector([0.0, 0, 1]) diff --git a/opensfm/test/test_types.py b/opensfm/test/test_types.py index 01a27f560..39073a17b 100644 --- a/opensfm/test/test_types.py +++ b/opensfm/test/test_types.py @@ -1,14 +1,15 @@ +# pyre-strict import copy import sys import cv2 import numpy as np +from numpy.typing import NDArray from opensfm import pygeometry, pymap, types from scipy.stats import special_ortho_group def test_reconstruction_class_initialization() -> None: - # Instantiate Reconstruction reconstruction = types.Reconstruction() focal = 0.9722222222222222 @@ -26,11 +27,13 @@ def test_reconstruction_class_initialization() -> None: metadata.orientation.value = 1 metadata.capture_time.value = 0.0 metadata.gps_accuracy.value = 5.0 - metadata.gps_position.value = np.array([ - 1.0815875281451939, - -0.96510451436708888, - 1.2042133903991235, - ]) + metadata.gps_position.value = np.array( + [ + 1.0815875281451939, + -0.96510451436708888, + 1.2042133903991235, + ] + ) metadata.gravity_down.value = np.array([0.1, 0.9, 0.0]) metadata.compass_angle.value = 270.0 metadata.compass_accuracy.value = 15.0 @@ -80,7 +83,7 @@ def test_shot_measurement() -> None: assert m.value == 4 -def _helper_pose_equal_to_T(pose, T_cw) -> None: +def _helper_pose_equal_to_T(pose: pygeometry.Pose, T_cw: NDArray) -> None: assert np.allclose(pose.get_R_world_to_cam(), T_cw[0:3, 0:3]) assert np.allclose(pose.get_t_world_to_cam(), T_cw[0:3, 3].reshape(3)) assert np.allclose(pose.translation, T_cw[0:3, 3].reshape(3)) @@ -97,14 +100,16 @@ def _helper_pose_equal_to_T(pose, T_cw) -> None: assert np.allclose(pose.get_Rt(), T_cw[0:3, 0:4]) -def _helper_poses_equal_py_cpp(py_pose, cpp_pose) -> None: +def _helper_poses_equal_py_cpp( + py_pose: pygeometry.Pose, cpp_pose: pygeometry.Pose +) -> None: assert np.allclose(py_pose.translation, cpp_pose.translation) assert np.allclose(py_pose.rotation, cpp_pose.rotation) assert np.allclose(py_pose.get_rotation_matrix(), cpp_pose.get_rotation_matrix()) assert np.allclose(py_pose.get_origin(), cpp_pose.get_origin()) -def _heper_poses_equal(pose1, pose2) -> None: +def _heper_poses_equal(pose1: pygeometry.Pose, pose2: pygeometry.Pose) -> None: assert np.allclose(pose1.translation, pose2.translation) assert np.allclose(pose1.rotation, pose2.rotation) assert np.allclose(pose1.get_rotation_matrix(), pose2.get_rotation_matrix()) diff --git a/opensfm/test/test_undistort.py b/opensfm/test/test_undistort.py index 3ea656967..38b389925 100644 --- a/opensfm/test/test_undistort.py +++ b/opensfm/test/test_undistort.py @@ -1,7 +1,8 @@ +# pyre-strict import itertools import numpy as np -from opensfm import undistort, pygeometry, types +from opensfm import pygeometry, types, undistort def test_perspective_views_of_a_panorama() -> None: diff --git a/opensfm/test/test_vlad.py b/opensfm/test/test_vlad.py index c406195c0..843a02421 100644 --- a/opensfm/test/test_vlad.py +++ b/opensfm/test/test_vlad.py @@ -1,3 +1,4 @@ +# pyre-strict import numpy as np import pytest from opensfm import vlad diff --git a/opensfm/test/utils.py b/opensfm/test/utils.py index 0531458d9..2167314a7 100644 --- a/opensfm/test/utils.py +++ b/opensfm/test/utils.py @@ -1,10 +1,13 @@ # Test utils for python + +# pyre-strict import numpy as np from opensfm import pygeo, pygeometry, pymap def assert_cameras_equal(cam1: pygeometry.Camera, cam2: pygeometry.Camera) -> None: - assert np.allclose(cam1.get_parameters_values(), cam2.get_parameters_values()) + assert np.allclose(cam1.get_parameters_values(), + cam2.get_parameters_values()) assert cam1.projection_type == cam2.projection_type assert cam1.width == cam2.width assert cam1.height == cam2.height @@ -62,7 +65,8 @@ def assert_shots_equal( assert np.allclose(shot1.pose.get_Rt(), shot2.pose.get_Rt(), 1e-5) assert shot1.merge_cc == shot2.merge_cc assert np.allclose(shot1.covariance, shot2.covariance) - assert_metadata_equal(shot1.metadata, shot2.metadata, test_mapillary_specific) + assert_metadata_equal(shot1.metadata, shot2.metadata, + test_mapillary_specific) def assert_bias_equal( @@ -110,7 +114,8 @@ def assert_maps_equal( # Pano shots are different objects of same value for shot_id in map1.get_pano_shots(): - shot1, shot2 = map1.get_pano_shots()[shot_id], map2.get_pano_shots()[shot_id] + shot1, shot2 = map1.get_pano_shots()[shot_id], map2.get_pano_shots()[ + shot_id] assert shot1 is not shot2 assert_shots_equal(shot1, shot2, test_mapillary_specific) @@ -126,10 +131,10 @@ def assert_maps_equal( assert len(obs) == len(obs_cpy) # Observations are different objects of same value - for shot, obs_id in obs.items(): - obs1 = shot.get_observation(obs_id) + for shot, obs in obs.items(): + obs1 = shot.get_observation(obs.id) shot_cpy = map2.get_shots()[shot.id] - obs_cpy = shot_cpy.get_observation(obs_id) + obs_cpy = shot_cpy.get_observation(obs.id) assert obs1 is not obs_cpy # Topocentric reference are different objects of same value diff --git a/opensfm/tracking.py b/opensfm/tracking.py index 0cca3a87b..83a6b0b20 100644 --- a/opensfm/tracking.py +++ b/opensfm/tracking.py @@ -1,52 +1,73 @@ +# pyre-strict import logging import typing as t +from typing import Callable, cast, Dict, List, Tuple import networkx as nx import numpy as np -from opensfm import pymap +from numpy.typing import NDArray +from opensfm import context, pymap from opensfm.dataset_base import DataSetBase -from opensfm.unionfind import UnionFind from opensfm.pymap import TracksManager +from opensfm.unionfind import UnionFind logger: logging.Logger = logging.getLogger(__name__) def load_features( - dataset: DataSetBase, images: t.List[str] -) -> t.Tuple[ - t.Dict[str, np.ndarray], - t.Dict[str, np.ndarray], - t.Dict[str, np.ndarray], - t.Dict[str, np.ndarray], + dataset: DataSetBase, images: List[str] +) -> Tuple[ + Dict[str, NDArray[np.float64]], + Dict[str, NDArray[np.int32]], + Dict[str, NDArray[np.int32]], + Dict[str, NDArray[np.int32]], + Dict[str, NDArray[np.float64]], ]: - logging.info("reading features") + logging.info("Reading features") + + def load_one(im): + features_data = dataset.load_features(im) + if not features_data: + return im, None, None, None, None, None + features = features_data.points[:, :3] + colors = features_data.colors + segmentations = None + instances = None + if features_data.semantic: + segmentations = features_data.semantic.segmentation + if features_data.semantic.has_instances(): + instances = features_data.semantic.instances + depths = features_data.depths if features_data.depths is not None else None + return im, features, colors, segmentations, instances, depths + + # Use context.parallel_map for parallel loading + results = context.parallel_map( + load_one, images, dataset.config.get("processes", 1)) + features = {} colors = {} segmentations = {} instances = {} - for im in images: - features_data = dataset.load_features(im) - - if not features_data: - continue - - features[im] = features_data.points[:, :3] - colors[im] = features_data.colors - - semantic_data = features_data.semantic - if semantic_data: - segmentations[im] = semantic_data.segmentation - if semantic_data.has_instances(): - instances[im] = semantic_data.instances + depths = {} + for im, feat, color, seg, inst, depth in results: + if feat is not None: + features[im] = feat + colors[im] = color + if seg is not None: + segmentations[im] = seg + if inst is not None: + instances[im] = inst + if depth is not None: + depths[im] = depth - return features, colors, segmentations, instances + return features, colors, segmentations, instances, depths def load_matches( - dataset: DataSetBase, images: t.List[str] -) -> t.Dict[t.Tuple[str, str], t.List[t.Tuple[int, int]]]: - matches = {} + dataset: DataSetBase, images: List[str] +) -> t.Iterator[Tuple[Tuple[str, str], List[Tuple[int, int]]]]: + """Yield matches for each image pair""" for im1 in images: try: im1_matches = dataset.load_matches(im1) @@ -54,23 +75,27 @@ def load_matches( continue for im2 in im1_matches: if im2 in images: - matches[im1, im2] = im1_matches[im2] - return matches + yield (im1, im2), im1_matches[im2] -def create_tracks_manager( - features: t.Dict[str, np.ndarray], - colors: t.Dict[str, np.ndarray], - segmentations: t.Dict[str, np.ndarray], - instances: t.Dict[str, np.ndarray], - matches: t.Dict[t.Tuple[str, str], t.List[t.Tuple[int, int]]], +def create_tracks_manager_from_matches_iter( + features: Dict[str, NDArray[np.float64]], + colors: Dict[str, NDArray[np.int32]], + segmentations: Dict[str, NDArray[np.int32]], + instances: Dict[str, NDArray[np.int32]], + matches: Callable[[], t.Iterator[Tuple[Tuple[str, str], List[Tuple[int, int]]]]], min_length: int, + depths: Dict[str, NDArray[np.float64]], + depth_is_radial: bool = True, + depth_std_deviation: float = 1.0, ) -> TracksManager: """Link matches into tracks.""" logger.debug("Merging features onto tracks") + + logger.debug("Running union-find to find aggregated tracks") uf = UnionFind() - for im1, im2 in matches: - for f1, f2 in matches[im1, im2]: + for (im1, im2), pairs in matches(): + for f1, f2 in pairs: uf.union((im1, f1), (im2, f2)) sets = {} @@ -82,30 +107,102 @@ def create_tracks_manager( sets[p] = [i] tracks = [t for t in sets.values() if _good_track(t, min_length)] - logger.debug("Good tracks: {}".format(len(tracks))) + logger.debug("Constructing TracksManager from tracks") NO_VALUE = pymap.Observation.NO_SEMANTIC_VALUE tracks_manager = pymap.TracksManager() + num_observations = 0 + num_depth_priors = 0 for track_id, track in enumerate(tracks): for image, featureid in track: if image not in features: continue x, y, s = features[image][featureid] r, g, b = colors[image][featureid] - segmentation, instance = ( - segmentations[image][featureid] if image in segmentations else NO_VALUE, - instances[image][featureid] if image in instances else NO_VALUE, + segmentation = ( + int(segmentations[image][featureid]) + if image in segmentations + else NO_VALUE ) + instance = ( + int(instances[image][featureid] + ) if image in instances else NO_VALUE + ) + obs = pymap.Observation( - x, y, s, int(r), int(g), int(b), featureid, segmentation, instance + x, + y, + s, + int(r), + int(g), + int(b), + featureid, + segmentation, + instance, ) + if image in depths: + depth_value = depths[image][featureid] + if not np.isnan(depth_value) and not np.isinf(depth_value): + std = max( + depth_std_deviation * depth_value, # pyre-ignore + depth_std_deviation, + ) + obs.depth_prior = pymap.Depth( + value=depth_value, # pyre-ignore + std_deviation=std, + is_radial=depth_is_radial, + ) + num_depth_priors += 1 tracks_manager.add_observation(image, str(track_id), obs) + num_observations += 1 + logger.info( + f"{len(tracks)} tracks, {num_observations} observations," + f" {num_depth_priors} depth priors added to TracksManager" + ) return tracks_manager +def create_tracks_manager( + features: Dict[str, NDArray[np.float64]], + colors: Dict[str, NDArray[np.int32]], + segmentations: Dict[str, NDArray[np.int32]], + instances: Dict[str, NDArray[np.int32]], + matches: t.Union[ + Dict[Tuple[str, str], List[Tuple[int, int]]], + Callable[[], t.Iterator[Tuple[Tuple[str, str], List[Tuple[int, int]]]]] + ], + min_length: int, + depths: Dict[str, NDArray[np.float64]], + depth_is_radial: bool = True, + depth_std_deviation: float = 1.0, +) -> TracksManager: + """ + Link matches into tracks. + + If matches is a dict, it will be wrapped as an iterator. + If matches is a callable, it will be used directly. + """ + if callable(matches): + matches_iter = matches + else: + def matches_iter(): + return iter(matches.items()) + return create_tracks_manager_from_matches_iter( + features, + colors, + segmentations, + instances, + matches_iter, + min_length, + depths, + depth_is_radial, + depth_std_deviation, + ) + + def common_tracks( tracks_manager: pymap.TracksManager, im1: str, im2: str -) -> t.Tuple[t.List[str], np.ndarray, np.ndarray]: +) -> Tuple[List[str], NDArray[np.float64], NDArray[np.float64]]: """List of tracks observed in both images. Args: @@ -129,34 +226,26 @@ def common_tracks( return tracks, p1, p2 -TPairTracks = t.Tuple[t.List[str], np.ndarray, np.ndarray] - - -def all_common_tracks_with_features( - tracks_manager: pymap.TracksManager, - min_common: int = 50, -) -> t.Dict[t.Tuple[str, str], TPairTracks]: - tracks = all_common_tracks( - tracks_manager, include_features=True, min_common=min_common - ) - return t.cast(t.Dict[t.Tuple[str, str], TPairTracks], tracks) +TPairTracks = Tuple[List[str], NDArray[np.float64], NDArray[np.float64]] def all_common_tracks_without_features( tracks_manager: pymap.TracksManager, min_common: int = 50, -) -> t.Dict[t.Tuple[str, str], t.List[str]]: + processes: int = 1, +) -> Dict[Tuple[str, str], List[str]]: tracks = all_common_tracks( - tracks_manager, include_features=False, min_common=min_common + tracks_manager, include_features=False, min_common=min_common, processes=processes ) - return t.cast(t.Dict[t.Tuple[str, str], t.List[str]], tracks) + return cast(Dict[Tuple[str, str], List[str]], tracks) def all_common_tracks( tracks_manager: pymap.TracksManager, include_features: bool = True, min_common: int = 50, -) -> t.Dict[t.Tuple[str, str], t.Union[TPairTracks, t.List[str]]]: + processes: int = 1, +) -> Dict[Tuple[str, str], t.Union[TPairTracks, List[str]]]: """List of tracks observed by each image pair. Args: @@ -169,20 +258,33 @@ def all_common_tracks( tuple: im1, im2 -> tuple: tracks, features from first image, features from second image """ - common_tracks = {} - for (im1, im2), size in tracks_manager.get_all_pairs_connectivity().items(): + def process_pair(pair): + im1, im2, size = pair if size < min_common: - continue - - tuples = tracks_manager.get_all_common_observations(im1, im2) + return None + track_ids, points1, points2 = tracks_manager.get_all_common_observations_arrays( + im1, im2) if include_features: - common_tracks[im1, im2] = ( - [v for v, _, _ in tuples], - np.array([p.point for _, p, _ in tuples]), - np.array([p.point for _, _, p in tuples]), + return ( + (im1, im2), + (track_ids, points1, points2), ) else: - common_tracks[im1, im2] = [v for v, _, _ in tuples] + return ((im1, im2), track_ids,) + + logger.debug("Computing pairwise connectivity of images") + pairs = [(k[0], k[1], v) + for k, v in tracks_manager.get_all_pairs_connectivity().items()] + + logger.debug(f"Gathering pairwise tracks with {processes} processes") + batch_size = max(1, len(pairs) // (2 * processes)) + results = context.parallel_map(process_pair, pairs, processes, batch_size) + + common_tracks = {} + for result in results: + if result is not None: + k, v = result + common_tracks[k] = v return common_tracks diff --git a/opensfm/transformations.py b/opensfm/transformations.py index 96bcd6bdc..209aafc6f 100644 --- a/opensfm/transformations.py +++ b/opensfm/transformations.py @@ -1,5 +1,7 @@ # transformations.py +# pyre-strict + # Copyright (c) 2006-2013, Christoph Gohlke # Copyright (c) 2006-2013, The Regents of the University of California # Produced at the Laboratory for Fluorescence Dynamics @@ -186,16 +188,17 @@ """ import math -from typing import Dict, Optional, List, Tuple +from typing import Dict, List, Optional, Tuple import numpy +from numpy.typing import NDArray __version__ = "2013.06.29" __docformat__ = "restructuredtext en" -__all__ = [] +__all__: List[str] = [] -def identity_matrix() -> numpy.ndarray: +def identity_matrix() -> NDArray: """Return 4x4 identity/unit matrix. >>> I = identity_matrix() @@ -210,7 +213,7 @@ def identity_matrix() -> numpy.ndarray: return numpy.identity(4) -def translation_matrix(direction: numpy.ndarray) -> numpy.ndarray: +def translation_matrix(direction: NDArray) -> NDArray: """Return matrix to translate by direction vector. >>> v = numpy.random.random(3) - 0.5 @@ -223,7 +226,7 @@ def translation_matrix(direction: numpy.ndarray) -> numpy.ndarray: return M -def translation_from_matrix(matrix: numpy.ndarray) -> numpy.ndarray: +def translation_from_matrix(matrix: NDArray) -> NDArray: """Return translation vector from translation matrix. >>> v0 = numpy.random.random(3) - 0.5 @@ -232,10 +235,10 @@ def translation_from_matrix(matrix: numpy.ndarray) -> numpy.ndarray: True """ - return numpy.array(matrix, copy=False)[:3, 3].copy() + return numpy.asarray(matrix)[:3, 3].copy() -def reflection_matrix(point: numpy.ndarray, normal: numpy.ndarray) -> numpy.ndarray: +def reflection_matrix(point: NDArray, normal: NDArray) -> NDArray: """Return matrix to mirror at plane defined by point and normal vector. >>> v0 = numpy.random.random(4) - 0.5 @@ -262,8 +265,8 @@ def reflection_matrix(point: numpy.ndarray, normal: numpy.ndarray) -> numpy.ndar def reflection_from_matrix( - matrix: numpy.ndarray, -) -> Tuple[numpy.ndarray, numpy.ndarray]: + matrix: NDArray, +) -> Tuple[NDArray, NDArray]: """Return mirror plane point and normal vector from reflection matrix. >>> v0 = numpy.random.random(3) - 0.5 @@ -275,7 +278,7 @@ def reflection_from_matrix( True """ - M = numpy.array(matrix, dtype=numpy.float64, copy=False) + M = numpy.asarray(matrix, dtype=numpy.float64) # normal: unit eigenvector corresponding to eigenvalue -1 w, V = numpy.linalg.eig(M[:3, :3]) i = numpy.where(abs(numpy.real(w) + 1.0) < 1e-8)[0] @@ -294,9 +297,9 @@ def reflection_from_matrix( def rotation_matrix( angle: float, - direction: numpy.ndarray, - point: Optional[numpy.ndarray] = None, -) -> numpy.ndarray: + direction: NDArray, + point: Optional[NDArray] = None, +) -> NDArray: """Return matrix to rotate about axis defined by point and direction. >>> R = rotation_matrix(math.pi/2, [0, 0, 1], [1, 0, 0]) @@ -339,14 +342,14 @@ def rotation_matrix( M[:3, :3] = R if point is not None: # rotation not around origin - point = numpy.array(point[:3], dtype=numpy.float64, copy=False) + point = numpy.asarray(point[:3], dtype=numpy.float64) M[:3, 3] = point - numpy.dot(R, point) return M def rotation_from_matrix( - matrix: numpy.ndarray, -) -> Tuple[float, numpy.ndarray, numpy.ndarray]: + matrix: NDArray, +) -> Tuple[float, NDArray, NDArray]: """Return rotation angle and axis from rotation matrix. >>> angle = (numpy.random.random() - 0.5) * (2*math.pi) @@ -359,7 +362,7 @@ def rotation_from_matrix( True """ - R = numpy.array(matrix, dtype=numpy.float64, copy=False) + R = numpy.asarray(matrix, dtype=numpy.float64) R33 = R[:3, :3] # direction: unit eigenvector of R33 corresponding to eigenvalue of 1 w, W = numpy.linalg.eig(R33.T) @@ -387,10 +390,10 @@ def rotation_from_matrix( def scale_matrix( - factor: numpy.ndarray, - origin: Optional[numpy.ndarray] = None, - direction: Optional[numpy.ndarray] = None, -) -> numpy.ndarray: + factor: NDArray, + origin: Optional[NDArray] = None, + direction: Optional[NDArray] = None, +) -> NDArray: """Return matrix to scale by factor around origin in direction. Use factor -1 for point symmetry. @@ -409,6 +412,7 @@ def scale_matrix( """ if direction is None: # uniform scaling + # pyre-fixme[6]: For 1st argument expected `Union[_SupportsArray[dtype[typing... M = numpy.diag([factor, factor, factor, 1.0]) if origin is not None: M[:3, 3] = origin[:3] @@ -425,8 +429,8 @@ def scale_matrix( def scale_from_matrix( - matrix: numpy.ndarray, -) -> Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]: + matrix: NDArray, +) -> Tuple[NDArray, NDArray, NDArray]: """Return scaling factor, origin and direction from scaling matrix. >>> factor = numpy.random.random() * 10 - 5 @@ -444,7 +448,7 @@ def scale_from_matrix( True """ - M = numpy.array(matrix, dtype=numpy.float64, copy=False) + M = numpy.asarray(matrix, dtype=numpy.float64) M33 = M[:3, :3] factor = numpy.trace(M33) - 2.0 try: @@ -464,16 +468,20 @@ def scale_from_matrix( raise ValueError("no eigenvector corresponding to eigenvalue 1") origin = numpy.real(V[:, i[-1]]).squeeze() origin /= origin[3] + # pyre-fixme[7]: Expected `Tuple[ndarray[typing.Any, typing.Any], + # ndarray[typing.Any, typing.Any], ndarray[typing.Any, typing.Any]]` but got + # `Tuple[typing.Any, ndarray[typing.Any, dtype[typing.Any]], + # Optional[ndarray[typing.Any, dtype[typing.Any]]]]`. return factor, origin, direction def projection_matrix( - point: numpy.ndarray, - normal: numpy.ndarray, - direction: Optional[numpy.ndarray] = None, - perspective: Optional[numpy.ndarray] = None, + point: NDArray, + normal: NDArray, + direction: Optional[NDArray] = None, + perspective: Optional[NDArray] = None, pseudo: bool = False, -) -> numpy.ndarray: +) -> NDArray: """Return matrix to project onto plane defined by point and normal. Using either perspective point, projection direction, or none of both. @@ -505,11 +513,11 @@ def projection_matrix( """ M = numpy.identity(4) - point = numpy.array(point[:3], dtype=numpy.float64, copy=False) + point = numpy.asarray(point[:3], dtype=numpy.float64) normal = unit_vector(normal[:3]) if perspective is not None: # perspective projection - perspective = numpy.array(perspective[:3], dtype=numpy.float64, copy=False) + perspective = numpy.asarray(perspective[:3], dtype=numpy.float64) M[0, 0] = M[1, 1] = M[2, 2] = numpy.dot(perspective - point, normal) M[:3, :3] -= numpy.outer(perspective, normal) if pseudo: @@ -522,7 +530,7 @@ def projection_matrix( M[3, 3] = numpy.dot(perspective, normal) elif direction is not None: # parallel projection - direction = numpy.array(direction[:3], dtype=numpy.float64, copy=False) + direction = numpy.asarray(direction[:3], dtype=numpy.float64) scale = numpy.dot(direction, normal) M[:3, :3] -= numpy.outer(direction, normal) / scale M[:3, 3] = direction * (numpy.dot(point, normal) / scale) @@ -534,10 +542,8 @@ def projection_matrix( def projection_from_matrix( - matrix: numpy.ndarray, pseudo: bool = False -) -> Tuple[ - numpy.ndarray, numpy.ndarray, Optional[numpy.ndarray], Optional[numpy.ndarray], bool -]: + matrix: NDArray, pseudo: bool = False +) -> Tuple[NDArray, NDArray, Optional[NDArray], Optional[NDArray], bool]: """Return projection plane and perspective point from projection matrix. Return values are same as arguments for projection_matrix function: @@ -569,7 +575,7 @@ def projection_from_matrix( True """ - M = numpy.array(matrix, dtype=numpy.float64, copy=False) + M = numpy.asarray(matrix, dtype=numpy.float64) M33 = M[:3, :3] w, V = numpy.linalg.eig(M) i = numpy.where(abs(numpy.real(w) - 1.0) < 1e-8)[0] @@ -610,14 +616,14 @@ def projection_from_matrix( def clip_matrix( - left: numpy.ndarray, - right: numpy.ndarray, - bottom: numpy.ndarray, - top: numpy.ndarray, - near: numpy.ndarray, - far: numpy.ndarray, + left: NDArray, + right: NDArray, + bottom: NDArray, + top: NDArray, + near: NDArray, + far: NDArray, perspective: bool = False, -) -> numpy.ndarray: +) -> NDArray: """Return matrix to obtain normalized device coordinates from frustum. The frustum bounds are axis-aligned along x (left, right), @@ -675,10 +681,10 @@ def clip_matrix( def shear_matrix( angle: float, - direction: numpy.ndarray, - point: numpy.ndarray, - normal: numpy.ndarray, -) -> numpy.ndarray: + direction: NDArray, + point: NDArray, + normal: NDArray, +) -> NDArray: """Return matrix to shear by angle along direction vector on shear plane. The shear plane is defined by a point and normal vector. The direction @@ -710,8 +716,8 @@ def shear_matrix( def shear_from_matrix( - matrix: numpy.ndarray, -) -> Tuple[float, numpy.ndarray, numpy.ndarray, numpy.ndarray]: + matrix: NDArray, +) -> Tuple[float, NDArray, NDArray, NDArray]: """Return shear angle, direction and plane from shear matrix. # Test disabled because it fails from time to time. @@ -726,7 +732,7 @@ def shear_from_matrix( True """ - M = numpy.array(matrix, dtype=numpy.float64, copy=False) + M = numpy.asarray(matrix, dtype=numpy.float64) M33 = M[:3, :3] # normal: cross independent eigenvectors corresponding to the eigenvalue 1 w, V = numpy.linalg.eig(M33) @@ -758,8 +764,8 @@ def shear_from_matrix( def decompose_matrix( - matrix: numpy.ndarray, -) -> Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray]: + matrix: NDArray, +) -> Tuple[NDArray, NDArray, NDArray, NDArray, NDArray]: """Return sequence of transformations from transformation matrix. matrix : array_like @@ -845,12 +851,12 @@ def decompose_matrix( def compose_matrix( - scale: Optional[numpy.ndarray] = None, - shear: Optional[numpy.ndarray] = None, - angles: Optional[numpy.ndarray] = None, - translate: Optional[numpy.ndarray] = None, - perspective: Optional[numpy.ndarray] = None, -) -> numpy.ndarray: + scale: Optional[NDArray] = None, + shear: Optional[NDArray] = None, + angles: Optional[NDArray] = None, + translate: Optional[NDArray] = None, + perspective: Optional[NDArray] = None, +) -> NDArray: """Return transformation matrix from sequence of transformations. This is the inverse of the decompose_matrix function. @@ -902,9 +908,7 @@ def compose_matrix( return M -def orthogonalization_matrix( - lengths: numpy.ndarray, angles: numpy.ndarray -) -> numpy.ndarray: +def orthogonalization_matrix(lengths: NDArray, angles: NDArray) -> NDArray: """Return orthogonalization matrix for crystallographic cell coordinates. Angles are expected in degrees. @@ -935,12 +939,12 @@ def orthogonalization_matrix( def affine_matrix_from_points( - v0: numpy.ndarray, - v1: numpy.ndarray, + v0: NDArray, + v1: NDArray, shear: bool = True, scale: bool = True, usesvd: bool = True, -) -> numpy.ndarray: +) -> NDArray: r"""Return affine transform matrix to register two point sets. v0 and v1 are shape (ndims, \*) arrays of at least ndims non-homogeneous @@ -1053,8 +1057,8 @@ def affine_matrix_from_points( def superimposition_matrix( - v0: numpy.ndarray, v1: numpy.ndarray, scale: bool = False, usesvd: bool = True -) -> numpy.ndarray: + v0: NDArray, v1: NDArray, scale: bool = False, usesvd: bool = True +) -> NDArray: r"""Return matrix to transform given 3D point set into second point set. v0 and v1 are shape (3, \*) or (4, \*) arrays of at least 3 points. @@ -1099,12 +1103,12 @@ def superimposition_matrix( True """ - v0 = numpy.array(v0, dtype=numpy.float64, copy=False)[:3] - v1 = numpy.array(v1, dtype=numpy.float64, copy=False)[:3] + v0 = numpy.asarray(v0, dtype=numpy.float64)[:3] + v1 = numpy.asarray(v1, dtype=numpy.float64)[:3] return affine_matrix_from_points(v0, v1, shear=False, scale=scale, usesvd=usesvd) -def euler_matrix(ai: float, aj: float, ak: float, axes: str = "sxyz") -> numpy.ndarray: +def euler_matrix(ai: float, aj: float, ak: float, axes: str = "sxyz") -> NDArray: """Return homogeneous rotation matrix from Euler angles and axis sequence. ai, aj, ak : Euler's roll, pitch and yaw angles @@ -1126,11 +1130,15 @@ def euler_matrix(ai: float, aj: float, ak: float, axes: str = "sxyz") -> numpy.n try: firstaxis, parity, repetition, frame = _AXES2TUPLE[axes] except (AttributeError, KeyError): + # pyre-fixme[6]: For 1st argument expected `tuple[int, ...]` but got `str`. _TUPLE2AXES[axes] # validation firstaxis, parity, repetition, frame = axes i = firstaxis + # pyre-fixme[6]: For 1st argument expected `int` but got `Union[int, str]`. j = _NEXT_AXIS[i + parity] + # pyre-fixme[58]: `-` is not supported for operand types `Union[int, str]` and + # `Union[int, str]`. k = _NEXT_AXIS[i - parity + 1] if frame: @@ -1168,7 +1176,7 @@ def euler_matrix(ai: float, aj: float, ak: float, axes: str = "sxyz") -> numpy.n def euler_from_matrix( - matrix: numpy.ndarray, axes: str = "sxyz" + matrix: NDArray, axes: str = "sxyz" ) -> Tuple[float, float, float]: """Return Euler angles from rotation matrix for specified axis sequence. @@ -1191,32 +1199,69 @@ def euler_from_matrix( try: firstaxis, parity, repetition, frame = _AXES2TUPLE[axes.lower()] except (AttributeError, KeyError): + # pyre-fixme[6]: For 1st argument expected `tuple[int, ...]` but got `str`. _TUPLE2AXES[axes] # validation firstaxis, parity, repetition, frame = axes i = firstaxis + # pyre-fixme[6]: For 1st argument expected `int` but got `Union[int, str]`. j = _NEXT_AXIS[i + parity] + # pyre-fixme[58]: `-` is not supported for operand types `Union[int, str]` and + # `Union[int, str]`. k = _NEXT_AXIS[i - parity + 1] - M = numpy.array(matrix, dtype=numpy.float64, copy=False)[:3, :3] + M = numpy.asarray(matrix, dtype=numpy.float64)[:3, :3] if repetition: + # pyre-fixme[6]: For 1st argument expected `Union[ndarray[Any, dtype[Any]], + # tuple[ndarray[Any, dtype[Any]], ...]]` but got `Tuple[Union[int, str], + # int]`. sy = math.sqrt(M[i, j] * M[i, j] + M[i, k] * M[i, k]) if sy > _EPS: + # pyre-fixme[6]: For 1st argument expected `Union[ndarray[Any, + # dtype[Any]], tuple[ndarray[Any, dtype[Any]], ...]]` but got + # `Tuple[Union[int, str], int]`. ax = math.atan2(M[i, j], M[i, k]) + # pyre-fixme[6]: For 1st argument expected `Union[ndarray[Any, + # dtype[Any]], tuple[ndarray[Any, dtype[Any]], ...]]` but got + # `Tuple[Union[int, str], Union[int, str]]`. ay = math.atan2(sy, M[i, i]) + # pyre-fixme[6]: For 1st argument expected `Union[ndarray[Any, + # dtype[Any]], tuple[ndarray[Any, dtype[Any]], ...]]` but got `Tuple[int, + # Union[int, str]]`. az = math.atan2(M[j, i], -M[k, i]) else: ax = math.atan2(-M[j, k], M[j, j]) + # pyre-fixme[6]: For 1st argument expected `Union[ndarray[Any, + # dtype[Any]], tuple[ndarray[Any, dtype[Any]], ...]]` but got + # `Tuple[Union[int, str], Union[int, str]]`. ay = math.atan2(sy, M[i, i]) az = 0.0 else: + # pyre-fixme[6]: For 1st argument expected `Union[ndarray[Any, dtype[Any]], + # tuple[ndarray[Any, dtype[Any]], ...]]` but got `Tuple[Union[int, str], + # Union[int, str]]`. + # pyre-fixme[6]: For 1st argument expected `Union[ndarray[Any, dtype[Any]], + # tuple[ndarray[Any, dtype[Any]], ...]]` but got `Tuple[int, Union[int, + # str]]`. cy = math.sqrt(M[i, i] * M[i, i] + M[j, i] * M[j, i]) if cy > _EPS: ax = math.atan2(M[k, j], M[k, k]) + # pyre-fixme[6]: For 1st argument expected `Union[ndarray[Any, + # dtype[Any]], tuple[ndarray[Any, dtype[Any]], ...]]` but got `Tuple[int, + # Union[int, str]]`. ay = math.atan2(-M[k, i], cy) + # pyre-fixme[6]: For 1st argument expected `Union[ndarray[Any, + # dtype[Any]], tuple[ndarray[Any, dtype[Any]], ...]]` but got `Tuple[int, + # Union[int, str]]`. + # pyre-fixme[6]: For 1st argument expected `Union[ndarray[Any, + # dtype[Any]], tuple[ndarray[Any, dtype[Any]], ...]]` but got + # `Tuple[Union[int, str], Union[int, str]]`. az = math.atan2(M[j, i], M[i, i]) else: ax = math.atan2(-M[j, k], M[j, j]) + # pyre-fixme[6]: For 1st argument expected `Union[ndarray[Any, + # dtype[Any]], tuple[ndarray[Any, dtype[Any]], ...]]` but got `Tuple[int, + # Union[int, str]]`. ay = math.atan2(-M[k, i], cy) az = 0.0 @@ -1228,7 +1273,7 @@ def euler_from_matrix( def euler_from_quaternion( - quaternion: numpy.ndarray, axes: str = "sxyz" + quaternion: NDArray, axes: str = "sxyz" ) -> Tuple[float, float, float]: """Return Euler angles from quaternion for specified axis sequence. @@ -1241,8 +1286,8 @@ def euler_from_quaternion( def quaternion_from_euler( - ai: numpy.ndarray, aj: numpy.ndarray, ak: numpy.ndarray, axes: str = "sxyz" -) -> numpy.ndarray: + ai: NDArray, aj: NDArray, ak: NDArray, axes: str = "sxyz" +) -> NDArray: """Return quaternion from Euler angles and axis sequence. ai, aj, ak : Euler's roll, pitch and yaw angles @@ -1256,11 +1301,18 @@ def quaternion_from_euler( try: firstaxis, parity, repetition, frame = _AXES2TUPLE[axes.lower()] except (AttributeError, KeyError): + # pyre-fixme[6]: For 1st argument expected `tuple[int, ...]` but got `str`. _TUPLE2AXES[axes] # validation firstaxis, parity, repetition, frame = axes + # pyre-fixme[58]: `+` is not supported for operand types `Union[int, str]` and + # `int`. i = firstaxis + 1 + # pyre-fixme[58]: `+` is not supported for operand types `int` and `Union[int, + # str]`. j = _NEXT_AXIS[i + parity - 1] + 1 + # pyre-fixme[58]: `-` is not supported for operand types `int` and `Union[int, + # str]`. k = _NEXT_AXIS[i - parity] + 1 if frame: @@ -1299,7 +1351,7 @@ def quaternion_from_euler( return q -def quaternion_about_axis(angle: numpy.ndarray, axis: numpy.ndarray) -> numpy.ndarray: +def quaternion_about_axis(angle: NDArray, axis: NDArray) -> NDArray: """Return quaternion for rotation about axis. >>> q = quaternion_about_axis(0.123, [1, 0, 0]) @@ -1315,7 +1367,7 @@ def quaternion_about_axis(angle: numpy.ndarray, axis: numpy.ndarray) -> numpy.nd return q -def quaternion_matrix(quaternion: numpy.ndarray) -> numpy.ndarray: +def quaternion_matrix(quaternion: NDArray) -> NDArray: """Return homogeneous rotation matrix from quaternion. >>> M = quaternion_matrix([0.99810947, 0.06146124, 0, 0]) @@ -1345,9 +1397,7 @@ def quaternion_matrix(quaternion: numpy.ndarray) -> numpy.ndarray: ) -def quaternion_from_matrix( - matrix: numpy.ndarray, isprecise: bool = False -) -> numpy.ndarray: +def quaternion_from_matrix(matrix: NDArray, isprecise: bool = False) -> NDArray: """Return quaternion from rotation matrix. If isprecise is True, the input matrix is assumed to be a precise rotation @@ -1379,7 +1429,7 @@ def quaternion_from_matrix( True """ - M = numpy.array(matrix, dtype=numpy.float64, copy=False)[:4, :4] + M = numpy.asarray(matrix, dtype=numpy.float64)[:4, :4] if isprecise: q = numpy.empty((4,)) t = numpy.trace(M) @@ -1428,9 +1478,7 @@ def quaternion_from_matrix( return q -def quaternion_multiply( - quaternion1: numpy.ndarray, quaternion0: numpy.ndarray -) -> numpy.ndarray: +def quaternion_multiply(quaternion1: NDArray, quaternion0: NDArray) -> NDArray: """Return multiplication of two quaternions. >>> q = quaternion_multiply([4, 1, -2, 3], [8, -5, 6, 7]) @@ -1451,7 +1499,7 @@ def quaternion_multiply( ) -def quaternion_conjugate(quaternion: numpy.ndarray) -> numpy.ndarray: +def quaternion_conjugate(quaternion: NDArray) -> NDArray: """Return conjugate of quaternion. >>> q0 = random_quaternion() @@ -1465,7 +1513,7 @@ def quaternion_conjugate(quaternion: numpy.ndarray) -> numpy.ndarray: return q -def quaternion_inverse(quaternion: numpy.ndarray) -> numpy.ndarray: +def quaternion_inverse(quaternion: NDArray) -> NDArray: """Return inverse of quaternion. >>> q0 = random_quaternion() @@ -1479,7 +1527,7 @@ def quaternion_inverse(quaternion: numpy.ndarray) -> numpy.ndarray: return q / numpy.dot(q, q) -def quaternion_real(quaternion: numpy.ndarray) -> float: +def quaternion_real(quaternion: NDArray) -> float: """Return real part of quaternion. >>> quaternion_real([3, 0, 1, 2]) @@ -1489,7 +1537,7 @@ def quaternion_real(quaternion: numpy.ndarray) -> float: return float(quaternion[0]) -def quaternion_imag(quaternion: numpy.ndarray) -> numpy.ndarray: +def quaternion_imag(quaternion: NDArray) -> NDArray: """Return imaginary part of quaternion. >>> quaternion_imag([3, 0, 1, 2]) @@ -1500,12 +1548,12 @@ def quaternion_imag(quaternion: numpy.ndarray) -> numpy.ndarray: def quaternion_slerp( - quat0: numpy.ndarray, - quat1: numpy.ndarray, - fraction: numpy.ndarray, + quat0: NDArray, + quat1: NDArray, + fraction: NDArray, spin: int = 0, shortestpath: bool = True, -) -> numpy.ndarray: +) -> NDArray: """Return spherical linear interpolation between two quaternions. >>> q0 = random_quaternion() @@ -1546,7 +1594,7 @@ def quaternion_slerp( return q0 -def random_quaternion(rand: Optional[numpy.ndarray] = None) -> numpy.ndarray: +def random_quaternion(rand: Optional[NDArray] = None) -> NDArray: """Return uniform random unit quaternion. rand: array like or None @@ -1575,7 +1623,7 @@ def random_quaternion(rand: Optional[numpy.ndarray] = None) -> numpy.ndarray: ) -def random_rotation_matrix(rand: Optional[numpy.ndarray] = None) -> numpy.ndarray: +def random_rotation_matrix(rand: Optional[NDArray] = None) -> NDArray: """Return uniform random rotation matrix. rand: array like @@ -1628,7 +1676,7 @@ def random_rotation_matrix(rand: Optional[numpy.ndarray] = None) -> numpy.ndarra def vector_norm( - data: numpy.ndarray, axis: Optional[int] = None, out: Optional[numpy.ndarray] = None + data: NDArray, axis: Optional[int] = None, out: Optional[NDArray] = None ) -> float: """Return length, i.e. Euclidean norm, of ndarray along axis. @@ -1661,16 +1709,20 @@ def vector_norm( data *= data out = numpy.atleast_1d(numpy.sum(data, axis=axis)) numpy.sqrt(out, out) + # pyre-fixme[7]: Expected `float` but got `ndarray[typing.Any, + # dtype[typing.Any]]`. return out else: data *= data numpy.sum(data, axis=axis, out=out) + # pyre-fixme[7]: Expected `float` but got `ndarray[typing.Any, + # dtype[typing.Any]]`. return numpy.sqrt(out, out) def unit_vector( - data: numpy.ndarray, axis: Optional[int] = None, out: Optional[numpy.ndarray] = None -) -> numpy.ndarray: + data: NDArray, axis: Optional[int] = None, out: Optional[NDArray] = None +) -> NDArray: """Return ndarray normalized by length, i.e. Euclidean norm, along axis. >>> v0 = numpy.random.random(3) @@ -1703,7 +1755,7 @@ def unit_vector( return data else: if out is not data: - out[:] = numpy.array(data, copy=False) + out[:] = numpy.asarray(data) data = out length = numpy.atleast_1d(numpy.sum(data * data, axis)) numpy.sqrt(length, length) @@ -1713,7 +1765,7 @@ def unit_vector( return data -def random_vector(size: int) -> numpy.ndarray: +def random_vector(size: int) -> NDArray: """Return array of random doubles in the half-open interval [0.0, 1.0). >>> v = random_vector(10000) @@ -1728,9 +1780,7 @@ def random_vector(size: int) -> numpy.ndarray: return numpy.random.random(size) -def vector_product( - v0: numpy.ndarray, v1: numpy.ndarray, axis: int = 0 -) -> numpy.ndarray: +def vector_product(v0: NDArray, v1: NDArray, axis: int = 0) -> NDArray: """Return vector perpendicular to vectors. >>> v = vector_product([2, 0, 0], [0, 3, 0]) @@ -1752,7 +1802,7 @@ def vector_product( def angle_between_vectors( - v0: numpy.ndarray, v1: numpy.ndarray, directed: bool = True, axis: int = 0 + v0: NDArray, v1: NDArray, directed: bool = True, axis: int = 0 ) -> float: """Return angle between vectors. @@ -1777,15 +1827,15 @@ def angle_between_vectors( True """ - v0 = numpy.array(v0, dtype=numpy.float64, copy=False) - v1 = numpy.array(v1, dtype=numpy.float64, copy=False) + v0 = numpy.asarray(v0, dtype=numpy.float64) + v1 = numpy.asarray(v1, dtype=numpy.float64) dot = numpy.sum(v0 * v1, axis=axis) dot /= vector_norm(v0, axis=axis) * vector_norm(v1, axis=axis) dot = numpy.clip(dot, -1.0, 1.0) return numpy.arccos(dot if directed else numpy.fabs(dot)) -def inverse_matrix(matrix: numpy.ndarray) -> numpy.ndarray: +def inverse_matrix(matrix: NDArray) -> NDArray: """Return inverse of square transformation matrix. >>> M0 = random_rotation_matrix() @@ -1801,7 +1851,7 @@ def inverse_matrix(matrix: numpy.ndarray) -> numpy.ndarray: return numpy.linalg.inv(matrix) -def concatenate_matrices(*matrices: List[numpy.ndarray]) -> numpy.ndarray: +def concatenate_matrices(*matrices: List[NDArray]) -> NDArray: """Return concatenation of series of transformation matrices. >>> M = numpy.random.rand(16).reshape((4, 4)) - 0.5 @@ -1817,7 +1867,7 @@ def concatenate_matrices(*matrices: List[numpy.ndarray]) -> numpy.ndarray: return M -def is_same_transform(matrix0: numpy.ndarray, matrix1: numpy.ndarray) -> numpy.ndarray: +def is_same_transform(matrix0: NDArray, matrix1: NDArray) -> NDArray: """Return True if two matrices perform same transformation. >>> is_same_transform(numpy.identity(4), numpy.identity(4)) @@ -1830,10 +1880,17 @@ def is_same_transform(matrix0: numpy.ndarray, matrix1: numpy.ndarray) -> numpy.n matrix0 /= matrix0[3, 3] matrix1 = numpy.array(matrix1, dtype=numpy.float64, copy=True) matrix1 /= matrix1[3, 3] + # pyre-fixme[7]: Expected `ndarray[typing.Any, typing.Any]` but got `bool`. return numpy.allclose(matrix0, matrix1) -def _import_module(name: str, package: Optional[str]=None, warn: bool=True, prefix: str="_py_", ignore: str="_") -> Optional[bool]: +def _import_module( + name: str, + package: Optional[str] = None, + warn: bool = True, + prefix: str = "_py_", + ignore: str = "_", +) -> Optional[bool]: """Try import all public attributes from module into global namespace. Existing attributes with name clashes are renamed with prefix. @@ -1870,7 +1927,6 @@ def _import_module(name: str, package: Optional[str]=None, warn: bool=True, pref if __name__ == "__main__": import doctest - import random # used in doctests numpy.set_printoptions(suppress=True, precision=5) doctest.testmod() diff --git a/opensfm/types.py b/opensfm/types.py index 1d2a877d1..edcea1dd0 100644 --- a/opensfm/types.py +++ b/opensfm/types.py @@ -1,7 +1,10 @@ +# pyre-strict """Basic types for building a reconstruction.""" -from typing import Dict, Optional + +from typing import Any, Dict, Optional import numpy as np +from numpy.typing import NDArray from opensfm import pygeometry, pymap from opensfm.geo import TopocentricConverter @@ -9,20 +12,7 @@ PANOSHOT_RIG_PREFIX = "panoshot_" -class ShotMesh(object): - """Triangular mesh of points visible in a shot - - Attributes: - vertices: (list of vectors) mesh vertices - faces: (list of triplets) triangles' topology - """ - - def __init__(self): - self.vertices = None - self.faces = None - - -class Reconstruction(object): +class Reconstruction: """Defines the reconstructed scene. Attributes: @@ -36,7 +26,7 @@ def __init__(self) -> None: """Defaut constructor""" self._setup_from_map(pymap.Map()) - def _setup_from_map(self, map_obj: pymap.Map): + def _setup_from_map(self, map_obj: pymap.Map) -> None: self.map = map_obj self.camera_view = pymap.CameraView(self.map) self.bias_view = pymap.BiasView(self.map) @@ -46,7 +36,7 @@ def _setup_from_map(self, map_obj: pymap.Map): self.pano_shot_view = pymap.PanoShotView(self.map) self.landmark_view = pymap.LandmarkView(self.map) - def __repr__(self): + def __repr__(self) -> str: return ( " None: self.map.remove_pano_shot(shot_id) def create_point( - self, point_id: str, coord: Optional[np.ndarray] = None + self, point_id: str, coord: Optional[NDArray] = None ) -> pymap.Landmark: if coord is None: return self.map.create_landmark(point_id, np.array([0, 0, 0])) @@ -333,7 +323,7 @@ def add_observation( def remove_observation(self, shot_id: str, lm_id: str) -> None: self.map.remove_observation(shot_id, lm_id) - def __deepcopy__(self, d): + def __deepcopy__(self, d: Dict[str, Any]) -> "Reconstruction": rec_cpy = Reconstruction() copy_observations = False diff --git a/opensfm/undistort.py b/opensfm/undistort.py index c64f584f2..e615b9470 100644 --- a/opensfm/undistort.py +++ b/opensfm/undistort.py @@ -1,3 +1,4 @@ +# pyre-strict import itertools import logging import os @@ -5,14 +6,15 @@ import cv2 import numpy as np +from numpy.typing import NDArray from opensfm import ( features, + features_processing, log, pygeometry, pymap, transformations as tf, types, - features_processing, ) from opensfm.context import parallel_map from opensfm.dataset import UndistortedDataSet @@ -48,9 +50,15 @@ def undistort_reconstruction( elif shot.camera.projection_type == "brown": urec.add_camera(perspective_camera_from_brown(shot.camera)) subshots = [get_shot_with_different_camera(urec, shot, image_format)] - elif shot.camera.projection_type in ["fisheye", "fisheye_opencv"]: + elif shot.camera.projection_type == "fisheye": urec.add_camera(perspective_camera_from_fisheye(shot.camera)) subshots = [get_shot_with_different_camera(urec, shot, image_format)] + elif shot.camera.projection_type == "fisheye_opencv": + urec.add_camera(perspective_camera_from_fisheye_opencv(shot.camera)) + subshots = [get_shot_with_different_camera(urec, shot, image_format)] + elif shot.camera.projection_type == "fisheye62": + urec.add_camera(perspective_camera_from_fisheye62(shot.camera)) + subshots = [get_shot_with_different_camera(urec, shot, image_format)] elif pygeometry.Camera.is_panorama(shot.camera.projection_type): subshot_width = int(data.config["depthmap_resolution"]) subshots = perspective_views_of_a_panorama( @@ -235,10 +243,10 @@ def compute_camera_mapping_cached(camera, new_camera, width, height): def undistort_image( shot: pymap.Shot, undistorted_shots: List[pymap.Shot], - original: Optional[np.ndarray], - interpolation, + original: Optional[NDArray], + interpolation: int, max_size: int, -) -> Dict[str, np.ndarray]: +) -> Dict[str, NDArray]: """Undistort an image into a set of undistorted ones. Args: @@ -254,7 +262,13 @@ def undistort_image( return {} projection_type = shot.camera.projection_type - if projection_type in ["perspective", "brown", "fisheye", "fisheye_opencv"]: + if projection_type in [ + "perspective", + "brown", + "fisheye", + "fisheye_opencv", + "fisheye62", + ]: [undistorted_shot] = undistorted_shots new_camera = undistorted_shot.camera height, width = original.shape[:2] @@ -284,7 +298,7 @@ def undistort_image( ) -def scale_image(image: np.ndarray, max_size: int) -> np.ndarray: +def scale_image(image: NDArray, max_size: int) -> NDArray: """Scale an image not to exceed max_size.""" height, width = image.shape[:2] factor = max_size / float(max(height, width)) @@ -421,12 +435,12 @@ def perspective_views_of_a_panorama( def render_perspective_view_of_a_panorama( - image: np.ndarray, + image: NDArray, panoshot: pymap.Shot, perspectiveshot: pymap.Shot, - interpolation=cv2.INTER_LINEAR, - borderMode=cv2.BORDER_WRAP, -) -> np.ndarray: + interpolation: int = cv2.INTER_LINEAR, + borderMode: int = cv2.BORDER_WRAP, +) -> NDArray: """Render a perspective view of a panorama.""" # Get destination pixel coordinates dst_shape = (perspectiveshot.camera.height, perspectiveshot.camera.width) diff --git a/opensfm/unionfind.py b/opensfm/unionfind.py index bf0345bf5..abbcab63c 100644 --- a/opensfm/unionfind.py +++ b/opensfm/unionfind.py @@ -1,6 +1,11 @@ # Source: https://www.ics.uci.edu/~eppstein/PADS/UnionFind.py # Licence: MIT +# pyre-strict +from typing import Dict, Generic, Hashable, Iterator, TypeVar + +T = TypeVar("T", bound=Hashable) + # This is PADS, a library of Python Algorithms and Data Structures # implemented by David Eppstein of the University of California, Irvine. @@ -42,7 +47,7 @@ """ -class UnionFind: +class UnionFind(Generic[T]): """Union-find data structure. Each unionFind instance X maintains a family of disjoint sets of @@ -59,12 +64,12 @@ class UnionFind: in X, it is added to X as one of the members of the merged set. """ - def __init__(self): + def __init__(self) -> None: """Create a new empty union-find structure.""" - self.weights = {} - self.parents = {} + self.weights: Dict[T, int] = {} + self.parents: Dict[T, T] = {} - def __getitem__(self, object): + def __getitem__(self, object: T) -> T: """Find and return the name of the set containing the object.""" # check for previously unknown object @@ -85,11 +90,11 @@ def __getitem__(self, object): self.parents[ancestor] = root return root - def __iter__(self): + def __iter__(self) -> Iterator[T]: """Iterate through all items ever found or unioned by this structure.""" return iter(self.parents) - def union(self, *objects): + def union(self, *objects: T) -> None: """Find the sets containing the objects and merge them all.""" roots = [self[x] for x in objects] heaviest = max((self.weights[r], r) for r in roots)[1] diff --git a/opensfm/upright.py b/opensfm/upright.py index bfa2a3df2..9fbc5518f 100644 --- a/opensfm/upright.py +++ b/opensfm/upright.py @@ -1,15 +1,18 @@ -import numpy as np +# pyre-strict from typing import Optional +import numpy as np +from numpy.typing import NDArray + def opensfm_to_upright( - coords: np.ndarray, + coords: NDArray, width: int, height: int, orientation: int, new_width: Optional[int] = None, new_height: Optional[int] = None, -) -> np.ndarray: +) -> NDArray: """ Transform opensfm coordinates to upright coordinates, correcting for EXIF orientation. diff --git a/opensfm/video.py b/opensfm/video.py index a951240c2..0fab6ccec 100644 --- a/opensfm/video.py +++ b/opensfm/video.py @@ -1,21 +1,21 @@ +# pyre-strict import datetime import os -from subprocess import Popen, PIPE -from typing import List +from subprocess import PIPE, Popen +from typing import List, Optional import cv2 import dateutil.parser -from opensfm import context -from opensfm import geotag_from_gpx -from opensfm import io +from opensfm import context, geotag_from_gpx, io -def video_orientation(video_file) -> int: +def video_orientation(video_file: str) -> int: # Rotation - # pyre-fixme[16]: Optional type has no attribute `read`. - rotation = Popen( - ["exiftool", "-Rotation", "-b", video_file], stdout=PIPE - ).stdout.read() + process = Popen(["exiftool", "-Rotation", "-b", video_file], stdout=PIPE) + assert ( + process.stdout is not None + ), "stdout should not be None when stdout=PIPE is specified" + rotation = process.stdout.read().decode("utf-8").strip() if rotation: rotation = float(rotation) if rotation == 0: @@ -34,16 +34,14 @@ def video_orientation(video_file) -> int: def import_video_with_gpx( - video_file, - gpx_file, + video_file: str, + gpx_file: str, output_path: str, dx: float, - dt=None, - start_time=None, + dt: Optional[float] = None, + start_time: Optional[str] = None, visual: bool = False, - image_description=None, ) -> List[str]: - points = geotag_from_gpx.get_lat_lon_time(gpx_file) orientation = video_orientation(video_file) @@ -52,10 +50,11 @@ def import_video_with_gpx( video_start_time = dateutil.parser.parse(start_time) else: try: - # pyre-fixme[16]: Optional type has no attribute `read`. - exifdate = Popen( - ["exiftool", "-CreateDate", "-b", video_file], stdout=PIPE - ).stdout.read() + process = Popen(["exiftool", "-CreateDate", "-b", video_file], stdout=PIPE) + assert ( + process.stdout is not None + ), "stdout should not be None when stdout=PIPE is specified" + exifdate = process.stdout.read().decode("utf-8").strip() video_start_time = datetime.datetime.strptime(exifdate, "%Y:%m:%d %H:%M:%S") except Exception: print("Video recording timestamp not found. Using first GPS point time.") diff --git a/opensfm/vlad.py b/opensfm/vlad.py index 3a472f054..d6de6b510 100644 --- a/opensfm/vlad.py +++ b/opensfm/vlad.py @@ -1,14 +1,14 @@ +# pyre-strict from functools import lru_cache -from typing import List, Tuple, Iterable, Dict, Optional +from typing import Dict, Iterable, List, Optional, Tuple import numpy as np -from opensfm import pyfeatures, feature_loader, bow +from numpy.typing import NDArray +from opensfm import bow, feature_loader, pyfeatures from opensfm.dataset_base import DataSetBase -def unnormalized_vlad( - features: np.ndarray, centers: np.ndarray -) -> Optional[np.ndarray]: +def unnormalized_vlad(features: NDArray, centers: NDArray) -> Optional[NDArray]: """Compute unnormalized VLAD histograms from a set of features in relation to centers. @@ -21,7 +21,7 @@ def unnormalized_vlad( return pyfeatures.compute_vlad_descriptor(features, centers) -def signed_square_root_normalize(v: np.ndarray) -> np.ndarray: +def signed_square_root_normalize(v: NDArray) -> NDArray: """Compute Signed Square Root (SSR) normalization on a vector. @@ -33,7 +33,7 @@ def signed_square_root_normalize(v: np.ndarray) -> np.ndarray: def vlad_distances( - image: str, other_images: Iterable[str], histograms: Dict[str, np.ndarray] + image: str, other_images: Iterable[str], histograms: Dict[str, NDArray] ) -> Tuple[str, List[float], List[str]]: """Compute VLAD-based distance (L2 on VLAD-histogram) between an image and other images. @@ -41,24 +41,30 @@ def vlad_distances( Returns the image, the order of the other images, and the other images. """ + + # avoid passing a gigantic VLAD dictionary in case of preemption : copy instead + ratio_copy = 0.5 + need_copy = len(other_images) < ratio_copy*len(histograms) + candidates = {k: histograms[k] for k in other_images + [image]} if need_copy else histograms + distances, others = pyfeatures.compute_vlad_distances( - histograms, image, set(other_images) + candidates, image, set(other_images) ) return image, distances, others -class VladCache(object): +class VladCache: def clear_cache(self) -> None: self.load_words.cache_clear() self.vlad_histogram.cache_clear() @lru_cache(1) - def load_words(self, data: DataSetBase) -> np.ndarray: + def load_words(self, data: DataSetBase) -> NDArray: words, _ = bow.load_vlad_words_and_frequencies(data.config) return words @lru_cache(1000) - def vlad_histogram(self, data: DataSetBase, image: str) -> Optional[np.ndarray]: + def vlad_histogram(self, data: DataSetBase, image: str) -> Optional[NDArray]: words = self.load_words(data) features_data = feature_loader.instance.load_all_data( data, image, masked=True, segmentation_in_descriptor=False diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..c1259015b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,112 @@ +[build-system] +requires = ["scikit-build-core>=0.8.0", "pybind11>=2.10.0"] +build-backend = "scikit_build_core.build" + +[project] +name = "opensfm" +version = "0.5.2" +description = "A Structure from Motion library" +readme = "README.md" +requires-python = ">=3.8" +license = {text = "BSD"} +authors = [ + {name = "Mapillary"}, +] + +dependencies = [ + "bs4>=0.0.2", + "cloudpickle>=0.4.0", + "exifread>=2.1.2", + "flask>=2.3.2", + "fpdf2>=2.4.6", + "joblib>=1.0.0", + "matplotlib", + "networkx>=2.5", + "numpy>=1.19", + "Pillow>=8.1.1", + "pyproj>=1.9.5.1", + "python-dateutil>=2.7", + "pyyaml>=5.4", + "scipy>=1.10.0", + "rasterio>=1.4.4", + "rawpy>=0.25.1", + "xmltodict>=0.10.2", + "opencv-python>=4.8.0", + "rerun-sdk==0.27.2", + "vmem>=1.0.2", +] + +[project.optional-dependencies] +dev = [ + "pytest>=7.0.0", + "wheel", +] +docs = [ + "Sphinx>=4.2.0", + "sphinx_rtd_theme>=1.0.0", +] +test = [ + "pytest>=7.0.0", +] + +# Note: The original bin/opensfm and bin/opensfm_run_all are bash scripts +# For Phase 1, we'll install them as scripts using scikit-build's script handling +# TODO Phase 2: Create proper Python entry points by moving bin/opensfm_main.py +# into the opensfm package (e.g., opensfm/__main__.py or opensfm/cli.py) +# [project.scripts] +# opensfm = "opensfm.cli:main" +# opensfm_run_all = "opensfm.cli:run_all" + +[project.urls] +Homepage = "https://github.com/mapillary/OpenSfM" +Documentation = "https://docs.opensfm.org/" + +[tool.scikit-build] +# CMake source directory containing CMakeLists.txt +cmake.source-dir = "opensfm/src" +cmake.build-type = "Release" + +# Use a fixed build directory (useful for running C++ tests) +build-dir = "cmake_build" + +# Minimum CMake version +cmake.version = ">=3.15" + +# Specify where to install the Python package +wheel.install-dir = "opensfm" + +# Package discovery - automatically find Python packages +wheel.packages = ["opensfm"] + +# Package data (JSON, YAML, NPZ files in opensfm/data/) is automatically +# included because it's inside the opensfm package directory +# CMake only installs compiled .so files; Python packaging handles the rest + +# Note: Script installation +# The bin/opensfm and bin/opensfm_run_all bash scripts need to be installed +# This will be handled via CMake install() commands or Python entry points + +# Include files in the sdist (source distribution) +sdist.include = [ + "opensfm/data/**", # sensor_data.json, camera_calibration.yaml, bow/*.npz + "opensfm/src/**", # CMake source files + "bin/**", # All utility scripts +] + +# Exclude unnecessary files from sdist +sdist.exclude = [ + ".git/**", + ".github/**", + "cmake_build/**", + "**/__pycache__/**", + "**/*.pyc", +] + +# CMake configuration options +[tool.scikit-build.cmake.define] +# Build C++ tests (enabled for Docker/CI environments) +OPENSFM_BUILD_TESTS = "ON" + +# Ensure we're using the correct Python executable +# This will be automatically set by scikit-build-core +# PYTHON_EXECUTABLE will be set automatically diff --git a/viewer/legacy/js/jquery.js b/viewer/legacy/js/jquery.js index 45477c0b6..c4c6022f2 100644 --- a/viewer/legacy/js/jquery.js +++ b/viewer/legacy/js/jquery.js @@ -1,2 +1,2 @@ -/*! jQuery v1.8.3 jquery.com | jquery.org/license */ -(function(e,t){function _(e){var t=M[e]={};return v.each(e.split(y),function(e,n){t[n]=!0}),t}function H(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(P,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:+r+""===r?+r:D.test(r)?v.parseJSON(r):r}catch(s){}v.data(e,n,r)}else r=t}return r}function B(e){var t;for(t in e){if(t==="data"&&v.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function et(){return!1}function tt(){return!0}function ut(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function at(e,t){do e=e[t];while(e&&e.nodeType!==1);return e}function ft(e,t,n){t=t||0;if(v.isFunction(t))return v.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return v.grep(e,function(e,r){return e===t===n});if(typeof t=="string"){var r=v.grep(e,function(e){return e.nodeType===1});if(it.test(t))return v.filter(t,r,!n);t=v.filter(t,r)}return v.grep(e,function(e,r){return v.inArray(e,t)>=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write(""),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t
a",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="
t
",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="
",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;ti.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="
",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="

",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t0)for(i=r;i=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*\s*$/g,Nt={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X
","
"]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1>");try{for(;r1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]===""&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("
").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window); +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"
","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 1 ) { - var ambigious = false; + var ambiguous = false; var toChange = []; for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { @@ -28374,17 +28374,17 @@ THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { hole_unassigned = false; betterShapeHoles[s2Idx].push( ho ); } else { - ambigious = true; + ambiguous = true; } } } if ( hole_unassigned ) { betterShapeHoles[sIdx].push( ho ); } } } - // console.log("ambigious: ", ambigious); + // console.log("ambiguous: ", ambiguous); if ( toChange.length > 0 ) { // console.log("to change: ", toChange); - if (! ambigious) newShapeHoles = betterShapeHoles; + if (! ambiguous) newShapeHoles = betterShapeHoles; } } diff --git a/viewer/server.py b/viewer/server.py index bddd1ba13..af4d56df5 100644 --- a/viewer/server.py +++ b/viewer/server.py @@ -1,14 +1,9 @@ +# pyre-unsafe import argparse import os from typing import List -from flask import ( - Flask, - abort, - jsonify, - Response, - send_file, -) +from flask import abort, Flask, jsonify, Response, send_file app = Flask(__name__, static_folder="./", static_url_path="") @@ -23,6 +18,8 @@ @app.route("/") def index() -> Response: + # pyre-fixme[6]: For 1st argument expected `typing_extensions.LiteralString` but + # got `Optional[str]`. return send_file(os.path.join(app.static_folder, "index.html")) @@ -83,7 +80,6 @@ def verified_send(file) -> Response: if os.path.isfile(file): return send_file(file) else: - # pyre-fixme[7]: Expected `Response` but got implicit return value of `None`. abort(404)