콘텐츠로 건너뛰기
STAGING SERVER
DEVELOPMENT SERVER

측정 결과 처리#

이 항목에서는 Basler blaze 및 Smart blaze 카메라의 측정 결과를 처리하는 방법을 설명합니다.

정보

이 항목의 모든 정보는 모든 blaze 및 Smart blaze 카메라 모델에 적용됩니다. 간결함을 위해 모든 모델을 통틀어 "blaze"로 지칭합니다.

개요#

Basler blaze 카메라는 각 센서 픽셀에 대해 빛이 이동한 거리를 측정합니다.

이 거리를 사용하여 카메라는 각 센서 픽셀에 대한 오른손잡이 좌표계의 x, y, z 좌표를 계산합니다. 좌표계의 원점은 카메라 하우징 내부에 위치한 카메라의 광학 중심에 있습니다. y축은 아래를 향하고 z축은 카메라에서 멀어지는 방향을 향합니다.

카메라는 선택한 Pixel Format에 따라 뎁스 맵 또는 포인트 클라우드 형식으로 3D 정보를 제공합니다. 뎁스 맵에서 z 좌표는 16비트 그레이값으로 인코딩됩니다. 아래 그림과 같이 모든 3D 좌표값은 그레이값으로부터 계산할 수 있습니다. 포인트 클라우드는 각 센서 픽셀에 대한 x, y, z 3D 좌표를 부동 소수점 숫자로 포함합니다. 단위는 mm입니다.

센서 픽셀에 대한 유효한 깊이 정보가 없는 경우(예: 이상치 제거 또는 광량이 부족한 경우, 즉 를 통과하기에는 빛이 충분히 강하지 않은 경우 신뢰 구간), 뎁스 맵 또는 포인트 클라우드의 해당 값은 Scan3dInvalidDataValue 파라미터에 의해 정의된 값으로 설정됩니다(기본 설정은 0).

뎁스 맵 처리#

뎁스 맵은 16비트 그레이값으로 구성됩니다. 카메라는 각 센서 픽셀에 대해 z 좌표값을 그레이값으로 변환하여 뎁스 맵에 저장합니다.

에서 제공하는 카메라 캘리브레이션 데이터와 결합하여 Scan3dCoordinateScale, Scan3dPrincipalPointU, Scan3dPrincipalPointV및 Scan3dFocalLength 파라미터를 사용하면 뎁스 맵에서 완전한 3D 정보를 검색할 수 있습니다.

정보

이 Scan3dCoordinateScale 파라미터값은 선택한 Pixel Format에 따라 다릅니다. blaze Viewer에서 카메라로 작업할 때 Pixel Format은 항상 Coord3D_ABC32f Pixel Format으로 설정됩니다. 이 설정은 변경할 수 없습니다. blaze Viewer에서 제공하는 뎁스 맵은 포인트 클라우드를 기반으로 생성됩니다. 이 경우 Scan3dCoordinateScale 파라미터값은 1입니다. pylon Viewer 외부에서 blaze 카메라로 작업하고 Pixel Format을 설정하면 Coord3D_C16, Scan3dCoordinateScale 파라미터값이 달라집니다. 서로 다른 Pixel Format에 대한 Scan3dCoordinateScale 파라미터값은 다음 표에 나열되어 있습니다.

Pixel Format Scan3dCoordinateScale[C] 파라미터값
Coord3D_ABC32f 1
Coord3D_C16 0.152588
Mono16 0.152588

카메라를 구성하여 깊이 맵(depth map)을 전송하고 깊이 맵 데이터에 액세스하는 방법은 GrabDepthMap C++ 샘플을 참조하십시오.

2D 뎁스 맵에서 3D 좌표 계산하기#

깊이 맵의 16비트 회색조 값을 mm 단위의 z 좌표로 변환하려면 다음 공식을 사용합니다.

z [mm] = gray2mm * g

여기서:

g = 깊이 맵의 회색조 값

gray2mm = 다음 항목의 값: Scan3dCoordinateScale parameter

x 및 y 좌표를 계산하려면 다음 공식을 사용합니다.

x [mm] = (u-cx) * z / f

y [mm] = (v-cy) * z / f

여기서:

(u,v) = 깊이 맵의 열 및 행

f = 다음 항목의 값: Scan3dFocalLength 파라미터, 즉 카메라 렌즈의 초점 거리

(cx,cy) = 다음 항목의 값: Scan3dPrincipalPointU 및 Scan3dPrincipalPointV 파라미터, 즉 주점(principal point)

C++ 샘플 코드#

    // Enable depth maps by enabling the Range component and setting the appropriate pixel format.
    camera.ComponentSelector.SetValue(ComponentSelector_Range);
    camera.ComponentEnable.SetValue(true);
    camera.PixelFormat.SetValue(PixelFormat_Coord3D_C16);

    // Query the conversion factor required to convert gray values to distances:
    // Choose the z axis first...
    camera.Scan3dCoordinateSelector.SetValue(Scan3dCoordinateSelector_CoordinateC);
    // ... then retrieve the conversion factor.
    const auto gray2mm = camera.Scan3dCoordinateScale.GetValue();

    // Configure the gray value used for indicating missing depth data.
    // Note: Before setting the value, the Scan3dCoordinateSelector parameter must be set to the axis the
    // value is to be configured for, in this case the z axis. This means that Scan3dCoordianteSelector must be set
    // to "CoordinateC". This has already been done a few lines above.
    camera.Scan3dInvalidDataValue.SetValue((double)missingDepth);

    // Retrieve calibration data from the camera.
    const auto cx = camera.Scan3dPrincipalPointU.GetValue();
    const auto cy = camera.Scan3dPrincipalPointV.GetValue();
    const auto f = camera.Scan3dFocalLength.GetValue();

    // ....

    // Access the data.
    const auto container = ptrGrabResult->GetDataContainer();
    const auto rangeComponent = container.GetDataComponent(0);
    const auto width = rangeComponent.GetWidth();
    const auto height = rangeComponent.GetHeight();

    // Calculate coordinates for pixel (u,v).
    const uint16_t g = ((uint16_t*)rangeComponent.GetData())[u + v * width];
    const double z = g * gray2mm;
    const double x = (u - cx) * z / f;
    const double y = (v - cy) * z / f;

저장된 뎁스 맵 다루기#

blaze Legacy SDK, pylon SDK 또는 blaze ROS 드라이버로 획득한 깊이 맵의 경우 다음 공식을 사용해야 합니다.

측정된 거리 [mm] = Pixel_Value x Scan3dCoordinateScale[C]

blaze Viewer로 저장된 깊이 맵의 경우 다음 공식을 사용합니다.

측정된 거리 [mm] = DepthMin_parameter + (Pixel_Value x (DepthMax_Parameter - DepthMin_parameter)) / 65535

포인트 클라우드 처리#

포인트 클라우드는 카메라 좌표계 내의 x, y, z 좌표 삼중항(triples)으로 구성되므로, 포인트 클라우드에서 3D 정보를 추출하기 위한 추가 처리는 필요하지 않습니다.

카메라를 구성하여 포인트 클라우드를 전송하고 데이터에 액세스하는 방법은 FirstSample C++ 샘플을 참조하십시오.

포인트 클라우드 외에도 깊이 맵이 필요한 경우, 포인트 클라우드에서 회색조 및 RGB 깊이 맵을 연산하는 방법을 보여주는 ConvertPointCloud2DepthMap C++ 샘플을 참조하십시오.

좌표계의 원점을 카메라 하우징 전면으로 이동#

카메라 좌표계의 원점은 카메라 하우징 내부에 있는 카메라 광학 중심에 위치합니다. z축을 따라 평행 이동된, 즉 카메라 하우징 전면에 위치한 좌표계의 좌표를 선호하는 경우 장치 고유의 상수 오프셋을 z 좌표에서 빼야 합니다. 필요한 오프셋은 다음의 값을 가져와 카메라에서 검색할 수 있습니다. ZOffsetOriginToCameraFront 파라미터:

const double offset = camera.ZOffsetOriginToCameraFront.GetValue();

(x, y, z)가 카메라 좌표계 내 한 점의 좌표인 경우, z축을 따라 카메라 하우징 전면으로 이동된 좌표계에서의 해당 좌표 (x', y', z')는 다음 공식을 사용하여 결정할 수 있습니다.

x' = x

y' = y

z' = z - offset

거리 계산#

포인트 좌표가 (x,y,z) mm 단위로 주어질 때, 카메라 광학 중심에서 해당 포인트까지의 거리는 다음 공식을 사용하여 계산할 수 있습니다.

d = sqrt( xx + yy + z*z )

거리 d' 카메라 하우징 전면까지의 거리는 다음과 같이 계산할 수 있습니다:

z' = z - offset

d' = sqrt( xx + yy + z'*z')

깊이 정보를 RGB 이미지로 시각화#

레인보우 컬러 맵핑을 위한 다음 방식을 사용하여 z 좌표 또는 거리 값에서 RGB 값을 계산할 수 있습니다. 이는 데이터 시각화를 개선하는 데 유용할 수 있습니다.

먼저, 다음의 [minDepth..maxDepth] 값 범위에서 깊이 값을 10비트 값으로 변환합니다. 이 10비트 깊이 값은 각각 8비트의 해상도를 갖는 4개의 색상 범위에 맵핑됩니다.

minDepth 및 maxDepth = 다음 항목의 값: DepthMin 및 DepthMax 즉, 카메라의 현재 Depth ROI

Depth 값 색상 범위에 맵핑됨
0..255 빨강에서 노랑으로
(255,0,0) -> (255,255,0)
256..511 노랑에서 초록으로
(255,255,0) -> (0, 255, 0)
512..767 초록에서 아쿠아로
(0,255,0) -> (0,255,255)
768..1023 아쿠아에서 파랑으로
(0,255,255) -> (0, 0, 255)

다음 코드 조각에서 depth z 값 또는 mm 단위의 거리 값입니다.

const int minDepth = (int)m_camera.DepthMin.GetValue();
const int maxDepth = (int)m_camera.DepthMax.GetValue();

const double scale = 65536.0 / (maxDepth - minDepth);

for each pixel {
    // Set depth either to the corresponding z value or
    // a distance value calculated from the z value.
    // Clip depth if required.
    if (depth < minDepth)
        depth = minDepth;
    else if (depth > maxDepth)
        depth = maxDepth;
    // Compute RGB values.
    const uint16_t g = (uint16_t)((depth - minDepth) * scale);
    const uint16_t val = g >> 6 & 0xff;
    const uint16_t sel = g >> 14;
    uint32_t res = val << 8 | 0xff;
    if (sel & 0x01)
    {
        res = (~res) >> 8 & 0xffff;
    }
    if (sel & 0x02)
    {
        res = res << 8;
    }
    const uint8_t r = res & 0xff;
    res = res >> 8;
    const uint8_t g = res & 0xff;
    res = res >> 8;
    const uint8_t b = res & 0xff;
}