Skip to content

Commit

Permalink
Samples: Update convert intrinsic sample
Browse files Browse the repository at this point in the history
This PR updates the `convert_intrinsics_opencv_to_halcon.py` sample,
and fixes the links to our support pages on help.zivid.com
by removing `/rst/` from the path.
  • Loading branch information
csu-bot-zivid authored and chrisasc committed Aug 16, 2022
1 parent a25d95b commit f621e11
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 40 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ To setup and use Zivid in one of these operating systems, please follow
their respective instructions in the following pages:

- [Install Zivid + HALCON for
Windows](https://support.zivid.com/latest/rst/api-reference/samples/halcon/install-zivid-halcon-for-windows.html)
Windows](https://support.zivid.com/latest/api-reference/samples/halcon/install-zivid-halcon-for-windows.html)
- [Install Zivid + HALCON for
LINUX](https://support.zivid.com/latest/rst/api-reference/samples/halcon/install-zivid-halcon-for-linux.html)
LINUX](https://support.zivid.com/latest/api-reference/samples/halcon/install-zivid-halcon-for-linux.html)
- [Create a HALCON "Hello World"
Program](https://support.zivid.com/latest/rst/api-reference/samples/halcon/create-a-halcon-hello-world.html)
Program](https://support.zivid.com/latest/api-reference/samples/halcon/create-a-halcon-hello-world.html)
- [How to Run a HALCON
Sample](https://support.zivid.com/latest/rst/api-reference/samples/halcon/how-to-run-a-halcon-sample.html)
Sample](https://support.zivid.com/latest/api-reference/samples/halcon/how-to-run-a-halcon-sample.html)
- [HALCON Sample
Videos](https://support.zivid.com/latest/rst/api-reference/samples/halcon/halcon-sample-videos.html)
Videos](https://support.zivid.com/latest/api-reference/samples/halcon/halcon-sample-videos.html)

The following HALCON versions have been tested and confirmed to work
with Zivid cameras:
Expand All @@ -106,7 +106,7 @@ We recommend to use one of the HALCON versions we have tested.
For more information about the Zivid cameras, please visit our
[Knowledge Base](https://support.zivid.com/latest). If you run into any
issues please check out
[Troubleshooting](https://support.zivid.com/latest/rst/support/troubleshooting.html).
[Troubleshooting](https://support.zivid.com/latest/support/troubleshooting.html).

## License

Expand Down
104 changes: 70 additions & 34 deletions source/SampleUtils/convert_intrinsics_opencv_to_halcon.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"""
Read Zivid camera intrinsic parameters (OpenCV model) from .yml file, convert them to
internal camera parameters (Halcon model), and save them to .dat file.
Read Zivid camera intrinsic parameters (OpenCV model) from .yml file or camera,
convert them to internal camera parameters (Halcon model), and save them to .dat file.
Example: python convert_intrinsics_opencv_to_halcon.py --input-file IntrinsicsOpenCV.yml
Example when reading from file: python convert_intrinsics_opencv_to_halcon.py
--input-file IntrinsicsOpenCV.yml --model-name "zivid two"
--output-file "IntrinsicsHalcon"
Example when reading from camera: python convert_intrinsics_opencv_to_halcon.py
--model-name "zivid two" --output-file "IntrinsicsHalcon"
The .dat file can be read with read_cam_par HALCON operator which creates a camera parameter
Expand All @@ -20,8 +24,8 @@

import cv2
import numpy as np
import numpy.typing as npt
import yaml
from numpy.typing import NDArray
from scipy.optimize import minimize


Expand All @@ -37,30 +41,52 @@ class CameraIntrinsics:
p1: float
p2: float

def __init__(self, filepath: Path):
@classmethod
def from_file(cls, filepath: Path):
data = yaml.safe_load(filepath.read_text(encoding="utf-8"))
self.cx = data["CameraIntrinsics"]["CameraMatrix"]["CX"]
self.cy = data["CameraIntrinsics"]["CameraMatrix"]["CY"]
self.fx = data["CameraIntrinsics"]["CameraMatrix"]["FX"]
self.fy = data["CameraIntrinsics"]["CameraMatrix"]["FY"]
self.k1 = data["CameraIntrinsics"]["Distortion"]["K1"]
self.k2 = data["CameraIntrinsics"]["Distortion"]["K2"]
self.k3 = data["CameraIntrinsics"]["Distortion"]["K3"]
self.p1 = data["CameraIntrinsics"]["Distortion"]["P1"]
self.p2 = data["CameraIntrinsics"]["Distortion"]["P2"]

def _camera_matrix(self) -> NDArray[np.float64]:
cx = data["CameraIntrinsics"]["CameraMatrix"]["CX"]
cy = data["CameraIntrinsics"]["CameraMatrix"]["CY"]
fx = data["CameraIntrinsics"]["CameraMatrix"]["FX"]
fy = data["CameraIntrinsics"]["CameraMatrix"]["FY"]
k1 = data["CameraIntrinsics"]["Distortion"]["K1"]
k2 = data["CameraIntrinsics"]["Distortion"]["K2"]
k3 = data["CameraIntrinsics"]["Distortion"]["K3"]
p1 = data["CameraIntrinsics"]["Distortion"]["P1"]
p2 = data["CameraIntrinsics"]["Distortion"]["P2"]
return cls(cx, cy, fx, fy, k1, k2, k3, p1, p2)

@classmethod
def from_camera(cls):
# pylint: disable=import-outside-toplevel
import zivid
from zivid.experimental import calibration

app = zivid.Application()
camera = app.connect_camera()
intrinsics = calibration.intrinsics(camera)
cx = intrinsics.camera_matrix.cx
cy = intrinsics.camera_matrix.cy
fx = intrinsics.camera_matrix.fx
fy = intrinsics.camera_matrix.fy
k1 = intrinsics.distortion.k1
k2 = intrinsics.distortion.k2
k3 = intrinsics.distortion.k3
p1 = intrinsics.distortion.p1
p2 = intrinsics.distortion.p2
return cls(cx, cy, fx, fy, k1, k2, k3, p1, p2)

def _camera_matrix(self) -> npt.NDArray[np.float64]:
K = np.eye(3)
K[0, 0] = self.fx
K[1, 1] = self.fy
K[0, 2] = self.cx
K[1, 2] = self.cy
return K

def _distortion_coefficients(self) -> NDArray[np.float64]:
def _distortion_coefficients(self) -> npt.NDArray[np.float64]:
return np.array([self.k1, self.k2, self.p1, self.p2, self.k3])

def undistorted_pixels(self, pixels: NDArray) -> NDArray[np.float64]:
def undistorted_pixels(self, pixels: npt.NDArray) -> npt.NDArray[np.float64]:
undistorted_normalized = cv2.undistortPoints(
pixels,
cameraMatrix=self._camera_matrix(),
Expand All @@ -76,14 +102,14 @@ class HalconInternalCameraParameters:
sx: float
sy: float
focus: float
poly: NDArray
image_size: NDArray
poly: npt.NDArray
image_size: npt.NDArray

def __init__(
self,
intrinsics: CameraIntrinsics,
pixel_size: NDArray[np.float64],
image_size: NDArray[np.int32],
pixel_size: npt.NDArray[np.float64],
image_size: npt.NDArray[np.int32],
):
self.cx = intrinsics.cx
self.cy = intrinsics.cy
Expand All @@ -93,7 +119,7 @@ def __init__(
self.poly = np.zeros((5))
self.image_size = image_size

def undistorted_pixels(self, pixels: NDArray) -> NDArray:
def undistorted_pixels(self, pixels: npt.NDArray) -> npt.NDArray:
x = (pixels[:, :, 0] - self.cx) * self.sx
y = (pixels[:, :, 1] - self.cy) * self.sy

Expand Down Expand Up @@ -190,8 +216,8 @@ def write_to_dat_file(self, filepath: Path) -> None:

@dataclass
class CameraParameters:
pixel_size: NDArray[np.float64]
image_size: NDArray[np.int32]
pixel_size: npt.NDArray[np.float64]
image_size: npt.NDArray[np.int32]

def __init__(self, model_name: str):
if model_name in [
Expand All @@ -208,10 +234,10 @@ def __init__(self, model_name: str):


def _cost_function(
poly: NDArray,
poly: npt.NDArray,
halcon_parameters: HalconInternalCameraParameters,
intrinsics: CameraIntrinsics,
pixels: NDArray,
pixels: npt.NDArray,
) -> float:
halcon_parameters.poly = poly
halcon_pixels_undistorted = halcon_parameters.undistorted_pixels(pixels=pixels)
Expand All @@ -221,7 +247,7 @@ def _cost_function(

def _create_pixels(
camera_parameters: CameraParameters,
) -> NDArray:
) -> npt.NDArray:
samples_x = 25
x = np.linspace(0, camera_parameters.image_size[0] - 1, samples_x)
y = np.linspace(
Expand All @@ -234,20 +260,19 @@ def _create_pixels(


def _estimate_halcon_internal_camera_parameters_from_opencv(
zivid_yaml_path: Path, model_name: str
intrinsics: CameraIntrinsics, model_name: str
) -> HalconInternalCameraParameters:
"""Estimate internal camera parameters (Halcon model) from intrinsic parameters (OpenCV model).
Args:
zivid_yaml_path: Path to Zivid camera intrinsic parameters (OpenCV model) .yml file
intrinsics: Zivid camera intrinsic parameters (OpenCV model)
model_name: Zivid camera model
Returns:
Estimated internal camera parameters (Halcon model)
"""

intrinsics = CameraIntrinsics(filepath=zivid_yaml_path)
camera_parameters = CameraParameters(model_name=model_name)
halcon_parameters = HalconInternalCameraParameters(
intrinsics=intrinsics,
Expand All @@ -271,8 +296,10 @@ def _args() -> argparse.Namespace:
parser.add_argument(
"--input-file",
type=Path,
required=True,
help="Path to Zivid camera intrinsic parameters (openCV model) as .yml file",
help=(
"Path to Zivid camera intrinsic parameters (openCV model) as .yml file. "
"Don't use argument to read intrinsics from connected camera (requires zivid-python)."
),
)
parser.add_argument(
"--model-name",
Expand All @@ -287,13 +314,22 @@ def _args() -> argparse.Namespace:
required=True,
help="Path to .dat file to save HALCON internal camera parameters",
)

return parser.parse_args()


def _main():
args = _args()

if args.input_file:
print(f"Reading intrinsics from file '{args.input_file}'")
intrinsics = CameraIntrinsics.from_file(args.input_file)
else:
print("Reading intrinsics from camera")
intrinsics = CameraIntrinsics.from_camera()

halcon_internal_camera_parameters = _estimate_halcon_internal_camera_parameters_from_opencv(
args.input_file, args.model_name
intrinsics, args.model_name
)
halcon_internal_camera_parameters.write_to_dat_file(args.output_file)
print(f"Halcon internal camera parameters saved to {args.output_file}")
Expand Down

0 comments on commit f621e11

Please sign in to comment.