HAR 攝影機畫面

政府監管機構實施多項規定,確保間接後方視野提供足夠資訊,讓駕駛人能準確及時地操控車輛。這會影響駕駛人對周遭環境的認知。

如果是以攝影機監控系統 (CMS) 為基礎的後方視野系統,美國國家公路交通安全管理局 (NHTSA) 規定您必須符合下列要求 (UNECE46 參照的 S6.6.2.3):

  • S5.5.3 回應時間。根據 S14.2 測試時,符合 S5.5.1 (視野) 和 S5.5.2 (大小) 規定的後視影像,會在倒車事件開始後 2.0 秒內顯示。

  • S5.5.4 停留時間。倒車事件結束後,符合 S5.5.1 和 S5.5.2 規定的後視影像不會顯示。

  • S5.5.5 停用。在倒車事件期間,符合 S5.5.1 和 S5.5.2 規定的後視影像會持續顯示,直到駕駛人修改檢視畫面,或車輛方向選擇器離開倒車位置為止。

  • S6.6.2.3.3.5 構件。操作人員手冊應提及可能的構件,以及這些構件對視野和物體局部遮蔽的影響,這可能需要駕駛人特別警覺和專注。

  • S6.2.2.3.4.1 影格率。鏡頭前物體的動作會呈現平滑流暢的影像。系統的最低影格率至少為 30 Hz (相當於 30 FPS)。在低光源環境或低速行駛時,系統的最低畫面更新率至少為 15 Hz。

  • S6.2.2.3.4.2 影像形成時間。在攝氏 22 度 ± 攝氏 5 度的溫度下,螢幕的影像形成時間少於 55 毫秒。

  • S6.2.2.3.4.3 系統延遲。攝影機監控系統 (CMS) 的延遲時間夠短,可幾乎同時算繪風景。在攝氏 22 度 ± 攝氏 5 度的溫度下,延遲時間低於 200 毫秒。

我們推出了 Android Automotive OS (AAOS) 擴充檢視系統 (EVS),以符合裸機 AAOS 的這些規定。我們為 AAOS 裝置導入了類似的虛擬化服務,並搭配高可用性渲染器 (HAR),同樣符合這些需求。

攝影機預覽管道

這五個階段構成相機預覽管道:

相機預覽管道階段

圖 1. 相機預覽管道階段。

攝影機服務封鎖是指攝影機服務平台及其抽象層,可讓應用程式存取及使用可用的攝影機。顯示服務功能會向使用者顯示圖像資料。應用程式會透過 Camera 服務和 Display 服務,實作目標使用者歷程。

主要後方視野使用者歷程如下:

  1. 駕駛人將方向選擇器 (齒輪) 設為「倒車」,觸發倒車事件。

  2. 系統會廣播返回事件。應用程式會接收廣播,並初始化影像辨識輸入區塊 (攝影機服務) 和轉譯器 (顯示器服務)。

  3. 攝影機輸入區塊會初始化 Camera Service 平台,並將服務控制代碼傳回應用程式。

  4. Renderer 會初始化步驟 4 中影像辨識輸入內容的檢視視窗。

  5. 應用程式要求影像辨識輸入區塊開始傳送影格緩衝區和事件。

  6. 應用程式會透過回呼 (非同步) 將傳送的影格緩衝區加入佇列。 影格緩衝區由攝影機輸入區塊擁有,因此應用程式無法修改。

  7. 應用程式會將影格緩衝區出列 (如果佇列不是空白),並組合使用者檢視畫面。使用者可以複製內容並加以修改。

  8. 應用程式將緩衝區傳送至轉譯器。

  9. Renderer 會在螢幕上繪製收到的緩衝區內容。

  10. 如果備份事件仍處於進行中狀態,請前往步驟 7。支援事件完成後,應用程式會要求影像辨識輸入區塊停止傳送影格緩衝區和事件,然後向使用者隱藏檢視畫面。

  11. 應用程式可選擇關閉攝影機並釋放算繪器。

圖 1 說明流程。這張圖片使用 QNX Camera Library API 的元素,以使用 Camera Service 平台。

HAR 主要使用者歷程

圖 2. HAR 主要使用者歷程。

影像辨識輸入區塊會宣告三個介面:

  • CameraManager,宣告管理攝影機裝置的方法;舉例來說,應用程式會使用這個介面開啟 (預留) 目標攝影機裝置。

  • CameraDevice 宣告用來控制攝影機裝置的方法,例如啟動或停止資料串流。

  • CameraStreamListener 宣告單一方法,可接收目標攝影機的各種事件。

設計

本節將詳細說明系統設計。

使用者體驗

駕駛人將檔位切換至倒車時,儀表板螢幕會顯示後置鏡頭的預覽畫面。駕駛人將檔位從倒車檔移開時,螢幕會停止預覽攝影機畫面。

可啟用其他使用者歷程。舉例來說,駕駛人啟動方向燈時,可以預覽後視鏡看不到的區域。

啟動相機預覽畫面

使用相機時,應用程式會列舉並評估可用的相機,找出最適合預期用途的相機。舉例來說,為了提供後方視野,應用程式會從可用攝影機清單中,尋找可顯示車輛後方的攝影機。

應用程式會檢查每個攝影機的特性 (例如位置、鏡頭朝向、影格率、輸出解析度和輸出格式),如果多部攝影機具有相同的必要特徵,應用程式可能會檢查其他特徵,例如視野和焦距。

這張圖片顯示使用靜態攝影機設定啟動攝影機預覽畫面的順序:

使用靜態攝影機設定啟動攝影機預覽畫面

圖 3. 使用靜態攝影機設定啟動攝影機預覽畫面。

停止顯示攝影機預覽畫面

倒車事件結束後,應用程式會停止提供後方視野。為避免顯示空白畫面或靜態圖片,應用程式會先對使用者隱藏檢視畫面,然後要求攝影機輸入區塊停止傳送事件。

這張圖片顯示停止目標攝影機裝置資料串流的順序:

停止從目標攝影機裝置串流資料

圖 4. 停止從目標攝影機裝置串流資料。

錯誤

攝影機裝置可能會意外停止傳送新的影格緩衝區。為偵測這類事件,攝影機輸入區塊可能會實作計時器,在收到新影格時到期,並在計時器到期時傳送通知。

應用程式收到通知時,會通知使用者相機預覽畫面已停止即時顯示,並嘗試關閉相機裝置再重新開啟,藉此還原相機預覽畫面。圖 5 顯示應用程式如何處理逾時:

處理逾時

圖 5. 處理逾時 (資料串流停止)。

影像辨識輸入區塊可以回報資料串流停止以外的事件,並在緩衝區中嵌入更多詳細資料。原始設備製造商 (OEM) 可使用這項事件中繼資料,處理平台上的事件。

活動

這個 API 供主機上執行的應用程式使用,可透過 HAR (下圖中的藍色方塊) 管理儀表板螢幕。

圖 5 顯示系統架構圖:

系統圖

圖 6. 系統圖。

服務

API 呼叫預計會在呼叫程序的環境中執行。

API

這項新版 API 僅適用於透過 HAR 管理儀表板螢幕上攝影機預覽畫面的應用程式。您可以透過平台抽象層存取 API,並動態連結。

CameraInputBlock 介面會宣告方法,用於初始化攝影機功能及取得輸入區塊管理工具。應用程式會使用傳回的 CameraManager 執行個體管理攝影機裝置。

// This class represents a camera input block for the application that manages the
// instrument cluster display with Harry.
public class CameraInputBlock : public InputBlock {
    public:
        // Clean up the resources.
        virtual ~CameraInputBlock();

        // A method inherited from InputBlock class. This method initializes
        // CameraInputBlock instance; e.g. checking the platform camera service
        // is live.
        //
        // @return CAMERA_EPERM if the platform camera service is not
        //                      available.
        //         CAMERA_OK otherwise.
        virtual CameraError init() override;

        // A method inherited from InputBlock class. This method release all
        // resources held by this CameraInputBlock instance.
        virtual void release() override;

        // This method returns a CameraManager instance. The caller uses
        // this instance to manage camera devices.
        //
        // @param out If this method is successful, this points to a valid
        //            CameraManager instance.
        // @return CAMERA_EACCESS when we fail to create CameraManager instance
        //         to return.
        //         CAMERA_OK otherwise.
        virtual CameraError getCameraManager(
            std::shared_ptr<CameraManager>* out) = 0;

    private:
        // Handle to manage camera devices.
        std::shared_ptr<CameraManager> mMgr;

        // Handle to manage camera devices that have been opened by clients.
        std::set<CameraDevice> mCameras;
};

CameraManager 類別會宣告開啟 (或擁有) 攝影機的方法,並在應用程式完成攝影機作業時釋出攝影機。應用程式可以開啟多部攝影機,並使用攝影機的串流建立更廣闊的視野或多重檢視畫面體驗。

// This pure virtual class declares methods to manage camera devices.
public class CameraManager {
    public:
        // This method returns a list of CameraDescriptor objects representing
        // available cameras.
        //
        // @param out A list of CameraDescriptor instances. This list may be
        //            empty if the platform camera service does not list any
        //            camera.
        // @return CAMERA_EACCESS if we failed to build a camera list.
        //         CAMERA_OK otherwise.
        virtual CameraError getCameraList(
            std::vector<CameraDescriptor>* out) = 0;

        // Open a camera device associated with a given string identifier.
        //
        // @param ID A string identifier of a camera device to request.
        // @param out A pointer to CameraDevice shared pointer object. This
        //            is null when we fail to open a target device.
        // @return CAMERA_ENODEV if no camera is associated with a given id.
        //         CAMERA_EACCESS if it fails to open a target device.
        //         CAMERA_OK otherwise.
        virtual CameraError open(
            std::string ID, std::shared_ptr<CameraDevice>* out) = 0;

        // Close a camera device associated with a given string identifier.
        // This method is assumed to be always successful.
        //
        // @param id A string identifier of a camera device to close.
        virtual void close(std::string id) = 0;
};

如果應用程式無法偵測要使用的攝影機,可以選擇最適合當下情境的攝影機。CameraManager::getCameraList() 會傳回 CameraDescriptor 執行個體清單,其中提供每部攝影機的特徵。

CameraDevice 類別代表單一攝影機裝置,並宣告啟動及停止資料串流的方法。如果攝影機特性不是靜態已知,用戶端會從描述元取得並剖析這些特性。

舉例來說,用戶端可以從目標攝影機裝置的中繼資料取得串流設定清單,並選擇屬性最佳的設定 (例如影格速率、解析度和輸出格式)。

// This class represents a single camera device.
public class CameraDevice {
    public:
        // Start a data stream that attributes are matching to given
        // configuration best.
        // If a selected configuration is not given (null), a data stream is
        // initiated in its default configuration and return.
        //
        // @param configuration Selected attributes of the imagery data stream.
        // @param listener An object to listen to an active data stream.
        // @param effective Actual attributes of started data stream.
        // @return CAMERA_EINVAL if a listener object is invalid.
        //         CAMERA_EIO if we failed to start a video stream.
        //         CAMERA_OK otherwise.
        virtual CameraError start(
                std::shared_ptr<CameraStreamConfiguration>& configuration,
                std::shared_ptr<CameraStreamListener>& listener,
                std::shared_ptr<CameraStreamConfiguration>* effective) = 0;

        // Stop a data stream.
        virtual void stop() = 0;

        // Get a camera descriptor.
        //
        // @param desc A set of attributes that defines this camera device.
        // @return CAMERA_ENODATA if a descriptor is not available.
        //         CAMERA_OK otherwise.
        CameraError getDescriptor(std::shared_ptr<CameraDescriptor>* desc) = 0;

        // Return a consumed buffer to the camera device. A client of active
        // stream must return a frame buffer explicitly by calling this method.
        virtual void doneWithFrame(std::shared_ptr<FrameBuffer>& buffer) = 0;

    private:
        // Describe this camera device.
        CameraDescriptor mDescriptor;

        // A weak reference to a listening client.
        std::weak_ptr<CameraStreamListener> mClient;
};

// This class declares attributes that characterize a camera device.
public class CameraDescriptor {
    public:
        // Unique std::string object to identify a single camera device.
        std::string mId;

        // A set of stream configurations this camera device is capable of. A
        // camera must have at least one stream configuration.
        std::set<CameraStreamConfiguration> mConfigurations;

        // Are more attributes needed to exist, such as locations, lens
        // facing directions, and intrinsic/extrinsic parameters?
};

// This class declares attributes that characterize an imagery data stream.
public class CameraStreamConfiguration {
    public:
        // Width of output of this stream in pixels.
        unsigned int mWidthInPixels;

        // Height of output of this stream in pixels.
        unsigned int mHeightInPixels;

        // An average number of frames per second.
        double mFrameRate;

        // A format of this stream's output. A client could calculate a
        // byte-per-pixel (bpp) from this.
        CameraColorFormat mFormat;
};

// This class represents a listener/callback object to listen to frames and
// events.
public class CameraStreamListener {
    public:
        // A listener method to receive various stream events including a new
        // frame buffer.
        //
        // @param event CameraStreamEvent object that represents a single event
        //              such as an arrival of a new frame buffer, camera stream
        //              is terminated, and so forth.
        virtual void onEvent(std::shared_ptr<CameraStreamEvent>* event) = 0;
};

CameraDevice::start() 會使用三個引數:

  • 呼叫端選擇的串流設定。

  • 用來接收串流事件的監聽器。

  • 指向有效串流設定的指標。強烈建議呼叫端檢查這個值,以便按照預期方式處理即將到來的影格緩衝區。

CameraDevice::start() 使用 Camera Service 平台啟動資料串流時,會保留對呼叫端監聽器物件的弱參照,以偵測呼叫端是否意外終止。

用戶端完成緩衝區的作業後,必須呼叫 CameraDevice::doneWithFrame() 方法,通知攝影機裝置不再需要緩衝區。

串流開始時,用戶端會收到事件訊息。常見的訊息是新的訊框緩衝區。透過已註冊的回呼函式,用戶端會收到 kNewFrameBuffer 事件,其中包含圖像資料和影格緩衝區中繼資料。StreamEventType 宣告更多型別,以處理其他串流事件。例如停止或暫停資料串流。

// This class lists events possibly occurring while a data stream is active.
enum class CameraStreamEventType {
    // A delivery of a new frame buffer.
    kNewFrameBuffer,
    // A data stream has been stopped.
    kStreamStopped,
    // No new frame buffer arrives for a while.
    kStreamHang,
    // Add more.
    ...
};

// This class represents a single instance of StreamEventType.
public class CameraStreamEvent {
    public:
        // Return a type of this event.
        //
        // @return CameraStreamEventType enum value.
        CameraStreamEventType getType() { return mType; }

        // Return a pointer to data associated with this event.
        //
        // @return A shared pointer object of the buffer that contains data for
        //         this event.
        std::shared_ptr<void> getData() { return mData; }

    private:
        // Describe a type of this event.
        CameraStreamEventType mType;

        // A pointer to the data buffer.
        std::shared_ptr<void> mData;
};

// This class inherits StreamEvent class and has additional fields to represent
// the frame buffer.
public class FrameBufferEvent : public CameraStreamEvent {
    public:
        // Return an identifier of this frame buffer.
        //
        // @return A unique integer value to identify this frame buffer.
        int getBufferID() { return mBufferID; }

        // Give access to frame buffer metadata.
        //
        // @return A shared pointer to the buffer that contains data besides
        //         the imagery data.
        std::shared_ptr<void> getMetadata() { return mMetadata; }

    private:
        // Unique integer to identify this buffer.
        int mBufferID;

        // A pointer to metadata of this frame buffer.
        std::shared_ptr<void> mMetadata;
};

這個範例會顯示 CameraInputBlock 介面及其應用程式的實作方式:

CameraError getCameraManager(std::shared_ptr<CameraManager>* out) {
    // During an instantiation, CameraManager will retrieve a list of camera
    // devices from the platform camera service and identify their attributes.
    *out = std::make_shared<CameraManager>();
    return CAMERA_OK;
}

// This method returns a list of CameraDescriptor objects representing available
// cameras.
CameraError CameraManager::getCameraList(std::vector<CameraDescriptor>* out) {
    if (mCameraList.size() < 1) {
        // Query a list of cameras and get their attributes.
    }
    *out = mCameraList;
    return CAMERA_OK;
}

// Open a camera device associated with a given string identifier.
CameraError CameraManager::open(std::string id, std::shared_ptr<CameraDevice>* out) {
    if (!mCameraList.contains(id)) {
        // We cannot identify any camera with a given value.
        return CAMERA_NODEV;
    }

    // During a construction, CameraDevice will obtain a handle of a target
    // camera device from the platform camera service.
    std::shared_ptr<CameraDevice> h = std::make_shared<CameraDevice>(id);
    if (!h) {
        // We fail to open a camera device.
        return CAMERA_EACCESS;
    }

    *out = h;
    return CAMERA_OK;
}

// Close a camera device associated with a given string identifier. This method
// is assumed to be always successful.
void CameraManager::close(std::string id) {
    if (!mCameraList.contains(id)) {
        // We ignore calls with unknown identifiers.
        return;
    }

    // mCameraList.remove() returns an object removed from the list.
    std::shared_ptr<CameraDevice> device = mCameraList.remove(id);

    // Ensure a device stops streaming.
    device->stop();
}

// Start a data stream that attributes are matching to given configuration
// best.
// If a selected configuration is not given (null), a data stream will be
// initiated in its default configuration and return.
CameraError CameraDevice::start(
        std::shared_ptr<CameraStreamConfiguration>& configuration,
        std::shared_ptr<CameraStreamListener>& listener,
        std::shared_ptr<CameraStreamConfiguration>* effective) {
    if (!listener) {
        return CAMERA_EINVAL;
    }

    // selectStreamConfiguration examines this camera's stream configurations
    // and returns the one closest to the selected configuration.
    CameraStreamConfiguration config = selectStreamConfiguration(configuration);

    // mDevice refers to the camera handle for the platform camera service. We
    // may need to translate CameraStreamConfiguration for the platform service.
    mDevice->configure(
        configuration.mWidth, configuration.mHeight, configuration.mFormat);

    // Start a data stream with a callback object.
    if (!mDevice->startStream(mCallback)) {
        // We failed to start a data stream.
        return CAMERA_EIO;
    }

    return CAMERA_OK;
}

// Stop a data stream.
void CameraDevice::stop() {
    if (!mDevice) {
        // Nothing to do if we don't have a valid camera handle for the
        // platform camera service.
        return;
    }

    mDevice->stopStream();
}

// Get a camera descriptor.
CameraError CameraDevice::getDescriptor(std::shared_ptr<CameraDescriptor>* desc) {
    if (!mDescriptor) {
        return CAMERA_ENODATA;
    }

    *desc = *mDescriptor;
    return CAMERA_OK;
}

// Return a consumed buffer to the camera device. A client of active stream
// must return a frame buffer explicitly by calling this method.
void CameraDevice::doneWithFrame(std::shared_ptr<FrameBuffer>& buffer) {
    if (!mBufferRecords.contains(buffer.getId())) {
        // Ignore a call with unknown frame buffer.
        return;
    }

    // Simply remove from the record.
    (void)mBufferRecords.remove(buffer.getId());
}

// This method handles gear-shift events.
void Application::handleGearShift(GearSelection selection) {
    switch (selection) {
        case GEAR_SELECTION_REVERSE:
            // Upon the reverse gear selection, we are going to start a video
            // stream and show its preview on the instrument cluster display.
            (void)startStream(mCameraInputBlock);

            // FIXME: Exact method to control the camera preview window on the
            // instrument display is to be determined.
            show(mRearVisibilityWindow);
            break;

        default:
            // Upon all other gear selection, we are going to stop a video
            // stream (if it's running) and hide the preview.
            stopStream(mCameraInputBlock);

            // FIXME: Exact method to control the camera preview window on the
            // instrument display is to be determined.
            hide(mRearVisibilityWindow);
            break;
    }
}

bool Application::startStream(std::shared_ptr<CameraInputBlock> handle) {
    return handle->start(std::bind(&Application::handleStreamCallback, this);
}

void Application::stopStream(std::shared_ptr<CameraInputBlock> handle) {
    handle->stop();
}

// This method handles a stream callback.
void Application::handleStreamCallback(StreamEvent& event) {
    switch (event.getType()) {
        case StreamEventType::kNewFrameBuffer:
            // Handle a new frame buffer. We may just enqueue it for the
            // future or forward to CameraInputBlock client.
            break;

        case StreamEventType::kStreamStopped:
            // Handle as an incident if this event is not expected.
            break;

        // More cases to be added.
    }
}

void Application::handleNewFrameBuffer(StreamEvent& event) {
    // Enqueue a new frame buffer for the further processing. A buffer
    // must be returned explicitly by calling
    // CameraDevice.doneWithFrame(FrameBuffer&) method.
}

void Application::handleStreamEvent(StreamEvent& event) {
    // Handle a received stream event except a new frame buffer's
    // arrival; e.g. a video stream is terminated unexpectedly.
}

效能

後方視野符合政府法規。

法規
回覆時間 CFR 571.111 S5.5.3
影格速率 UNECE R46 6.2.2.3.4
影像形成時間 UNECE R46 6.2.2.3.4.2
系統延遲時間 UNECE R46 6.2.2.3.4.3

隱私權

隱私權相關規定:

  • API 不會要求實作項目收集、記錄或儲存個人識別資訊 (PII)。不過,由於擷取的圖像資料 (或相關聯的中繼資料) 可能含有 PII,因此使用 API 的應用程式必須取得使用者的明確同意聲明。

  • 由於攝影機涉及安全關鍵角色,因此使用者無法控制攝影機裝置,在儀表板螢幕上預覽畫面。OEM 會在設定期間或向駕駛人取得使用者同意聲明。

  • 這項 API 不支援背景攝影機用戶端。因此,隱私權指標 (可讓使用者瞭解攝影機裝置正在擷取資料) 不在適用範圍內。