複数の Zivid カメラを使用する際のパフォーマンスに関する考慮事項

To capture with multiple Zivid cameras at the best speed, capture sequentially if the cameras' fields of view overlap, or in parallel if they do not. Overlapping FOVs mean the cameras' projected light patterns interfere with each other, so only one camera can capture at a time; non-overlapping FOVs have no such interference, so all cameras can capture at the same time.

Capture Sequentially

順次キャプチャを利用するアプリケーションの例を 2 つ示します。

  • 複数のカメラで物体を異なる角度から撮像する(検査)

  • オクルージョンを回避するために複数のカメラで 1 つ以上のビンを撮像する(ビンピッキング / ピースピッキング)

このようなアプリケーションでは、カメラの FOV が重なり合います。そのため、投影された白色光パターンによるカメラ間の干渉を避けるために、一度に 1 台のカメラでキャプチャする必要があります。

For both the inspection and bin/piece-picking use cases above, we recommend the same capture strategy. To explain this strategy, let us take as an example the inspection of an object on a conveyor belt.

FOV が重なり合う複数のカメラを使用したコンベアベルト上の物体の検査

検査対象の物体がカメラの FOV 内に入ると、コンベアベルトが停止します。キャプチャプロセスの速度を最適化するには、キャプチャ関数を順番に呼び出すことを推奨します。これは、キャプチャ API が取得完了時に返されるためです。

ソースに移動

source

std::vector<Zivid::Frame> frames;

for(auto &camera : connectedCameras)
{
    std::cout << "Capturing frame with camera: " << camera.info().serialNumber() << std::endl;
    const auto settings =
        Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} },
                         Zivid::Settings::Color{ Zivid::Settings2D{
                             Zivid::Settings2D::Acquisitions{ Zivid::Settings2D::Acquisition{} } } } };
    const auto frame = camera.capture2D3D(settings);
    frames.push_back(frame);
}

Tip

3D キャプチャの代わりに 2D キャプチャを行う場合も、同じ戦略を推奨します。ただし、2D と 3D の両方が必要な場合は、 2D + 3D キャプチャ戦略 をご参照ください。

すべてのカメラのキャプチャが完了すると、シーンをカメラに対して相対的に移動できます。この例では、物体を乗せたコンベアベルトが動き始めることができます。

このタイミングで、データのコピー、点群のステッチ、物体の検査も開始できます。これらの操作は順次または並行して実行できます。以下の実装例では、データをコピーして ZDF ファイルに並行して保存する方法を示します。

注釈

速度を最適化するには、キャプチャ関数が返った後、点群を取得する API を呼び出す前にコンベアを移動することが重要です。

ソースに移動

source

namespace
{
    Zivid::Array2D<Zivid::PointXYZColorRGBA_SRGB> processAndSaveInThread(const Zivid::Frame &frame)
    {
        const auto pointCloud = frame.pointCloud();
        auto data = pointCloud.copyData<Zivid::PointXYZColorRGBA_SRGB>();

        // This is where you should run your processing

        const auto dataFile = "Frame_" + frame.cameraInfo().serialNumber().value() + ".zdf";
        std::cout << "Saving frame to file: " << dataFile << std::endl;
        frame.save(dataFile);

        return data;
    }

    std::vector<Zivid::Camera> connectToAllAvailableCameras(const std::vector<Zivid::Camera> &cameras)
    {
        std::vector<Zivid::Camera> connectedCameras;
        for(auto camera : cameras)
        {
            if(camera.state().status() == Zivid::CameraState::Status::available)
            {
                std::cout << "Connecting to camera: " << camera.info().serialNumber() << std::endl;
                camera.connect();
                connectedCameras.push_back(camera);
            }
            else
            {
                std::cout << "Camera " << camera.info().serialNumber() << "is not available. "
                          << "Camera status: " << camera.state().status() << std::endl;
            }
        }
        return connectedCameras;
    }
} // namespace

        std::vector<std::future<Zivid::Array2D<Zivid::PointXYZColorRGBA_SRGB>>> futureData;

        for(auto &frame : frames)
        {
            std::cout << "Starting to process and save (in a separate thread) the frame captured with camera: "
                      << frame.cameraInfo().serialNumber().value() << std::endl;
            futureData.emplace_back(std::async(std::launch::async, processAndSaveInThread, frame));
        }

プロセス全体の速度をさらに最適化するには、次の操作を行います。キャプチャ関数が返るとすぐに、フレームに対して以下の操作を行う別スレッドを開始します。

  • 点群を取得する

  • 点群を CPU メモリにコピーする

  • 必要に応じて、点群のその他の操作(例:点群を共通の座標系に変換する)

以下の実装例をご覧ください。

ソースに移動

source

namespace
{
    Zivid::Array2D<Zivid::PointXYZColorRGBA_SRGB> processAndSaveInThread(const Zivid::Frame &frame)
    {
        const auto pointCloud = frame.pointCloud();
        auto data = pointCloud.copyData<Zivid::PointXYZColorRGBA_SRGB>();

        // This is where you should run your processing

        const auto dataFile = "Frame_" + frame.cameraInfo().serialNumber().value() + ".zdf";
        std::cout << "Saving frame to file: " << dataFile << std::endl;
        frame.save(dataFile);

        return data;
    }

    std::vector<Zivid::Camera> connectToAllAvailableCameras(const std::vector<Zivid::Camera> &cameras)
    {
        std::vector<Zivid::Camera> connectedCameras;
        for(auto camera : cameras)
        {
            if(camera.state().status() == Zivid::CameraState::Status::available)
            {
                std::cout << "Connecting to camera: " << camera.info().serialNumber() << std::endl;
                camera.connect();
                connectedCameras.push_back(camera);
            }
            else
            {
                std::cout << "Camera " << camera.info().serialNumber() << "is not available. "
                          << "Camera status: " << camera.state().status() << std::endl;
            }
        }
        return connectedCameras;
    }
} // namespace

        std::vector<std::future<Zivid::Array2D<Zivid::PointXYZColorRGBA_SRGB>>> futureData;

        for(auto &camera : connectedCameras)
        {
            std::cout << "Capturing frame with camera: " << camera.info().serialNumber() << std::endl;
            const auto settings =
                Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} },
                                 Zivid::Settings::Color{ Zivid::Settings2D{
                                     Zivid::Settings2D::Acquisitions{ Zivid::Settings2D::Acquisition{} } } } };
            const auto frame = camera.capture2D3D(settings);

            std::cout << "Starting to process and save (in a separate thread) the frame captured with camera: "
                      << frame.cameraInfo().serialNumber().value() << std::endl;
            futureData.emplace_back(std::async(std::launch::async, processAndSaveInThread, frame));
        }

その後、点群のステッチなどのさらなる処理を実行できるよう、すべてのデータが利用可能になるまで待機する必要があります。

ソースに移動

source

std::vector<Zivid::Array2D<Zivid::PointXYZColorRGBA_SRGB>> allData;

for(size_t i = 0; i < cameras.size(); ++i)
{
    std::cout << "Waiting for processing and saving to finish for camera " << cameras[i].info().serialNumber()
              << std::endl;
    const auto data = futureData[i].get();
    allData.push_back(data);
}
Capture in Parallel

並行キャプチャを利用するアプリケーションの例を 1 つ示します。

  • 1 台のカメラの FOV に収まらない物体を複数のカメラで撮像する(検査)

このようなアプリケーションでは、カメラの FOV は重なり合いません(複数のカメラを使用する目的は FOV を拡張することです)。そのため、時間を節約するために並行してキャプチャすることができます。

推奨するキャプチャ戦略を説明するために、コンベアベルト上の大型物体の検査を例に挙げます。

FOV が重なり合わない複数のカメラを使用したコンベアベルト上の物体の検査

検査対象の物体がカメラの複合 FOV 内に入ると、コンベアベルトが停止します。キャプチャプロセスの速度を最適化するには、各カメラのキャプチャ関数のみを別スレッドで呼び出し、すべてのスレッドの実行が終了するまでブロックすることを推奨します。キャプチャ関数のみを呼び出すことを推奨するのは、キャプチャ API が取得完了時に返されるためです。

ソースに移動

source

namespace
{
    Zivid::Frame captureInThread(Zivid::Camera &camera)
    {
        const auto settings =
            Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} },
                             Zivid::Settings::Color{ Zivid::Settings2D{
                                 Zivid::Settings2D::Acquisitions{ Zivid::Settings2D::Acquisition{} } } } };

        std::cout << "Capturing frame with camera: " << camera.info().serialNumber().value() << std::endl;
        auto frame = camera.capture2D3D(settings);

        return frame;
    }

} // namespace

        std::vector<std::future<Zivid::Frame>> futureFrames;

        for(auto &camera : connectedCameras)
        {
            std::cout << "Starting to capture (in a separate thread) with camera: "
                      << camera.info().serialNumber().value() << std::endl;
            futureFrames.emplace_back(std::async(std::launch::async, captureInThread, std::ref(camera)));
        }

        std::vector<Zivid::Frame> frames;

        for(size_t i = 0; i < connectedCameras.size(); ++i)
        {
            std::cout << "Waiting for camera " << connectedCameras[i].info().serialNumber() << " to finish capturing"
                      << std::endl;
            const auto frame = futureFrames[i].get();
            frames.push_back(frame);
        }

Tip

3D キャプチャの代わりに 2D キャプチャを行う場合も、同じ戦略を推奨します。ただし、2D と 3D の両方が必要な場合は、 2D + 3D キャプチャ戦略 をご参照ください。

すべてのカメラのキャプチャが完了すると、シーンをカメラに対して相対的に移動できます。この例では、物体を乗せたコンベアベルトが動き始めることができます。

このタイミングで、データのコピー、点群のステッチ、物体の検査も開始できます。これらの操作は順次または並行して実行できます。以下の実装例では、データをコピーして ZDF ファイルに並行して保存する方法を示します。

注釈

速度を最適化するには、キャプチャ関数が返った後、点群を取得する API を呼び出す前にコンベアを移動することが重要です。

ソースに移動

source

namespace
{
    Zivid::Array2D<Zivid::PointXYZColorRGBA_SRGB> processAndSaveInThread(const Zivid::Frame &frame)
    {
        const auto pointCloud = frame.pointCloud();
        auto data = pointCloud.copyData<Zivid::PointXYZColorRGBA_SRGB>();

        // This is where you should run your processing

        const auto dataFile = "Frame_" + frame.cameraInfo().serialNumber().value() + ".zdf";
        std::cout << "Saving frame to file: " << dataFile << std::endl;
        frame.save(dataFile);

        return data;
    }

} // namespace

        std::vector<std::future<Zivid::Array2D<Zivid::PointXYZColorRGBA_SRGB>>> futureData;

        for(auto &frame : frames)
        {
            std::cout << "Starting to process and save (in a separate thread) the frame captured with camera: "
                      << frame.cameraInfo().serialNumber().value() << std::endl;
            futureData.emplace_back(std::async(std::launch::async, processAndSaveInThread, frame));
        }

その後、点群のステッチなどのさらなる処理を実行できるよう、すべてのデータが利用可能になるまで待機する必要があります。

ソースに移動

source

std::vector<Zivid::Array2D<Zivid::PointXYZColorRGBA_SRGB>> allData;

for(size_t i = 0; i < frames.size(); ++i)
{
    std::cout << "Waiting for processing and saving to finish for camera "
              << frames[i].cameraInfo().serialNumber().value() << std::endl;
    const auto data = futureData[i].get();
    allData.push_back(data);
}

バージョン履歴

SDK

変更点

2.11.0

バージョン 2 および 2+ では、3D ➞ 2D の同時処理と同時取得がサポートされ、キャプチャモード間の切り替えが最適化されました。