從0開始使用面對對象C語言搭建一個基于OLED的圖形顯示框架(繪圖設備封裝)

目錄

圖像層的底層抽象——繪圖設備抽象

如何抽象一個繪圖設備?

橋接繪圖設備,特化為OLED設備

題外話:設備的屬性,與設計一個相似函數化簡的通用辦法

使用函數指針來操作設備

總結一下


圖像層的底層抽象——繪圖設備抽象

在上一篇博客中,我們完成了對設備層的抽象。現在,我們終于可以賣出雄心壯志的一步了!那就是嘗試去完成一個最為基礎的圖形庫。我們要做的,就是設計一個更加復雜的繪圖設備。

為什么是繪圖設備呢?我們程序員都是懶蛋,想要最大程度的復用代碼,省最大的力氣干最多的事情。所以,我們的圖像框架在未來,還會使用LCD繪制,還會使用其他形形色色的繪制設備來繪制我們的圖像。而不僅限于OLED。所以,讓我們抽象一個可以繪制的設備而不是一個OLED設備,是非常重要的。

一個繪圖設備,是OLED設備的的子集。他可以開啟關閉,完成繪制操作,刷新繪制操作,清空繪制操作。僅此而已。

typedef void* ? CCDeviceRawHandle;
typedef void* ? CCDeviceRawHandleConfig;
?
// 初始化設備,設備需要做一定的初始化后才能繪制圖形
typedef void(*Initer)(CCDeviceHandler* handler, CCDeviceRawHandleConfig config);
?
// 清空設備
typedef void(*ClearDevice)(CCDeviceHandler* handler);
?
// 更新設備
typedef void(*UpdateDevice)(CCDeviceHandler* handler);
?
// 反色設備
typedef void(*ReverseDevice)(CCDeviceHandler* handler);
?
// 繪制點
typedef void(*SetPixel)(CCDeviceHandler* handler, uint16_t x, uint16_t y);
?
// 繪制面
typedef void(*DrawArea)(CCDeviceHandler* handler, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t* sources
);
?
// 面操作(清空,反色,更新等等,反正不需要外來提供繪制資源的操作)
typedef void(*AreaOperation)(CCDeviceHandler* handler, uint16_t x, uint16_t y, uint16_t width, uint16_t height
);
?
// 這個比較新,筆者后面講
typedef enum{CommonProperty_WIDTH,CommonProperty_HEIGHT,CommonProperty_SUPPORT_RGB
}CommonProperty;
?
// 獲取資源的屬性
typedef void(*FetchProperty)(CCDeviceHandler*, void*, CommonProperty p);
?
// 一個繪圖設備可以完成的操作
// 提示,其實可以化簡,一些函數指針(或者說方法)是沒有必要存在的,思考一下如何化簡呢?
typedef struct __DeviceOperations 
{Initer ? ? ? ?  init_function;ClearDevice ? ? clear_device_function;UpdateDevice ?  update_device_function;SetPixel ? ? ?  set_pixel_device_function;ReverseDevice ? reverse_device_function;DrawArea ? ? ?  draw_area_device_function;AreaOperation ? clearArea_function;AreaOperation ? updateArea_function;AreaOperation ? reverseArea_function;FetchProperty ? property_function;
}CCDeviceOperations;
?
// 一個繪圖設備的最終抽象
typedef struct __DeviceProperty
{/* device type */CCDeviceType ? ? ? ? ?  device_type;/* device raw data handle */CCDeviceRawHandle ? ? ? handle;/* device functions */CCDeviceOperations ? ?  operations;
}CCDeviceHandler;

設計上筆者是自底向上設計的,筆者現在打算自頂向下帶大伙解讀一下我的代碼。

如何抽象一個繪圖設備?

這個設備是什么?是一個OLED?還是一個LCD?

/* device type */
CCDeviceType ? ? ? ? ?  device_type;

這個設備的底層保存資源是什么?當我們動手準備操作的時候,需要拿什么進行操作呢?

 ?  /* device raw data handle */CCDeviceRawHandle ? ? ? handle;

你不需要在使用的時候關心他到底是什么,因為我們從頭至尾都在使用接口進行操作,你只需要知道,一個繪圖設備可以繪制圖像,這就足夠了

 ?  /* device functions */CCDeviceOperations ? ?  operations;

這里是我們的命根子,一個繪圖設備可以完成的操作。我們在之后的設計會大量的見到operations這個操作。

筆者的operations借鑒了Linux是如何抽象文件系統的代碼。顯然,一個良好的面對對象C編寫規范的參考代碼就是Linux的源碼

下一步,就是DeviceType有哪些呢?目前,我們開發的是OLED,也就意味著只有OLED是一個合法的DeviceType

typedef enum{OLED_Type
}CCDeviceType;

最后,我們需要思考的是,如何定義一個繪圖設備的行為呢?我們知道我們現在操作的就是一個OLED,所以,我們的問題實際上就轉化成為:

當我們給定了一個明確的,是OLED設備的繪圖設備的時候,怎么聯系起來繪圖設備和OLED設備呢?

答案還是回到我們如何抽象設備層的代碼上,那就是根據我們的類型來選擇我們的方法。

/* calling this is not encouraged! */
void __register_paintdevice(CCDeviceHandler* blank_handler, CCDeviceRawHandle raw_handle, CCDeviceRawHandleConfig config, CCDeviceType type);
?
#define register_oled_paintdevice(handler, raw, config) \__register_paintdevice(handler, raw, config, OLED_Type)

所以,我們注冊一個OLED的繪圖設備,只需要調用接口register_oled_paintdevice就好了,提供一個干凈的OLED_HANDLE和初始化OLED_HANDLE所需要的資源,我們的設備也就完成了初始化。

#include "Graphic/device_adapter/CCGraphic_device_oled_adapter.h"
#include "Graphic/CCGraphic_device_adapter.h"
?
void __register_paintdevice(CCDeviceHandler* blank_handler, CCDeviceRawHandle raw_handle, CCDeviceRawHandleConfig config, CCDeviceType type)
{blank_handler->handle = raw_handle;blank_handler->device_type = type;switch(type){case OLED_Type:{blank_handler->operations.init_function = (Initer)init_device_oled;blank_handler->operations.clear_device_function =clear_device_oled;blank_handler->operations.set_pixel_device_function = setpixel_device_oled;blank_handler->operations.update_device_function = update_device_oled;blank_handler->operations.clearArea_function =clear_area_device_oled;blank_handler->operations.reverse_device_function =reverse_device_oled;blank_handler->operations.reverseArea_function = reversearea_device_oled;blank_handler->operations.updateArea_function = update_area_device_oled;blank_handler->operations.draw_area_device_function =draw_area_device_oled;blank_handler->operations.property_function = property_fetcher_device_oled;}break;}blank_handler->operations.init_function(blank_handler, config);
}

這個仍然是最空泛的代碼,我們只是簡單的橋接了一下,聲明我們的設備是OLED,還有真正完成橋接的文件:CCGraphic_device_oled_adapter文件沒有給出來。所以,讓我們看看實際上是如何真正的完成橋接的。

橋接繪圖設備,特化為OLED設備

什么是橋接?什么是特化?橋接指的是講一個抽象結合過度到另一個抽象上,在這里,我們講繪圖設備引渡到我們的OLED設備而不是其他更加寬泛的設備上去,而OLED設備屬于繪圖設備的一個子集,看起來,我們就像是把虛無縹緲的“繪圖設備”落地了,把一個抽象的概念更加具體了。我們的聊天從“用繪圖設備完成XXX”轉向了“使用一個OLED作為繪圖設備完成XXX”了。這就是特化,將一個概念明晰起來。

#include "Graphic/CCGraphic_device_adapter.h"
#include "OLED/Driver/oled_config.h"
?
/* * 提供用于 OLED 設備的相關操作函數 */
?
/*** @struct CCGraphic_OLED_Config* @brief OLED 設備的配置結構體*/
typedef struct {OLED_Driver_Type ?  createType; ? ?  // OLED 驅動類型(軟 I2C、硬 I2C 等)void* ? ? ? ? ? ? ? related_configs; // 與驅動相關的具體配置
} CCGraphic_OLED_Config;
?
/*** @brief 初始化 OLED 設備* @param blank 空的設備句柄,初始化后填充* @param onProvideConfigs OLED 配置參數指針,包含驅動類型及配置* * @note 調用此函數時需要傳遞初始化好的配置(軟 I2C 或硬 I2C 配置等)*/
void init_device_oled(CCDeviceHandler* blank, CCGraphic_OLED_Config* onProvideConfigs);
?
/*** @brief 刷新整個 OLED 屏幕內容* @param handler 設備句柄*/
void update_device_oled(CCDeviceHandler* handler);
?
/*** @brief 清空 OLED 屏幕內容* @param handler 設備句柄*/
void clear_device_oled(CCDeviceHandler* handler);
?
/*** @brief 設置指定位置的像素點* @param handler 設備句柄* @param x 橫坐標* @param y 縱坐標*/
void setpixel_device_oled(CCDeviceHandler* handler, uint16_t x, uint16_t y);
?
/*** @brief 清除指定區域的顯示內容* @param handler 設備句柄* @param x 區域起點的橫坐標* @param y 區域起點的縱坐標* @param width 區域寬度* @param height 區域高度*/
void clear_area_device_oled(CCDeviceHandler* handler, uint16_t x, uint16_t y, uint16_t width, uint16_t height);
?
/*** @brief 更新指定區域的顯示內容* @param handler 設備句柄* @param x 區域起點的橫坐標* @param y 區域起點的縱坐標* @param width 區域寬度* @param height 區域高度*/
void update_area_device_oled(CCDeviceHandler* handler, uint16_t x, uint16_t y, uint16_t width, uint16_t height);
?
/*** @brief 反轉整個屏幕的顯示顏色* @param handler 設備句柄*/
void reverse_device_oled(CCDeviceHandler* handler);
?
/*** @brief 反轉指定區域的顯示顏色* @param handler 設備句柄* @param x 區域起點的橫坐標* @param y 區域起點的縱坐標* @param width 區域寬度* @param height 區域高度*/
void reversearea_device_oled(CCDeviceHandler* handler, uint16_t x, uint16_t y, uint16_t width, uint16_t height);
?
/*** @brief 繪制指定區域的圖像* @param handler 設備句柄* @param x 區域起點的橫坐標* @param y 區域起點的縱坐標* @param width 區域寬度* @param height 區域高度* @param sources 圖像數據源指針*/
void draw_area_device_oled(CCDeviceHandler* handler, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t* sources
);
?
/*** @brief 獲取設備屬性* @param handler 設備句柄* @param getter 屬性獲取指針* @param p 屬性類型*/
void property_fetcher_device_oled(CCDeviceHandler* handler, void* getter, CommonProperty p
);
?

好在代碼實際上并不困難,具體的代碼含義我寫在下面了,可以參考看看

#include "Graphic/device_adapter/CCGraphic_device_oled_adapter.h"
#include "OLED/Driver/oled_base_driver.h"
?
/*** @brief 初始化 OLED 設備* * 根據提供的配置(軟 I2C、硬 I2C、軟 SPI、硬 SPI)初始化 OLED 設備。* * @param blank 空的設備句柄,初始化后填充* @param onProvideConfigs OLED 配置參數指針,包含驅動類型及具體配置*/
void init_device_oled(CCDeviceHandler* blank, CCGraphic_OLED_Config* onProvideConfigs)
{OLED_Handle* handle = (OLED_Handle*)(blank->handle);OLED_Driver_Type type = onProvideConfigs->createType;
?switch(type){case OLED_SOFT_IIC_DRIVER_TYPE:oled_init_softiic_handle(handle,(OLED_SOFT_IIC_Private_Config*) (onProvideConfigs->related_configs));break;
?case OLED_HARD_IIC_DRIVER_TYPE:oled_init_hardiic_handle(handle, (OLED_HARD_IIC_Private_Config*)(onProvideConfigs->related_configs));break;
?case OLED_SOFT_SPI_DRIVER_TYPE:oled_init_softspi_handle(handle,(OLED_SOFT_SPI_Private_Config*)(onProvideConfigs->related_configs));break;
?case OLED_HARD_SPI_DRIVER_TYPE:oled_init_hardspi_handle(handle,(OLED_HARD_SPI_Private_Config*)(onProvideConfigs->related_configs));break;}
}
?
/*** @brief 刷新整個 OLED 屏幕內容* * @param handler 設備句柄*/
void update_device_oled(CCDeviceHandler* handler)
{OLED_Handle* handle = (OLED_Handle*)handler->handle;oled_helper_update(handle);
}
?
/*** @brief 清空 OLED 屏幕內容* * @param handler 設備句柄*/
void clear_device_oled(CCDeviceHandler* handler)
{OLED_Handle* handle = (OLED_Handle*)handler->handle;oled_helper_clear_frame(handle);
}
?
/*** @brief 設置指定位置的像素點* * @param handler 設備句柄* @param x 橫坐標* @param y 縱坐標*/
void setpixel_device_oled(CCDeviceHandler* handler, uint16_t x, uint16_t y)
{OLED_Handle* handle = (OLED_Handle*)handler->handle;oled_helper_setpixel(handle, x, y);
}
?
/*** @brief 清除指定區域的顯示內容* * @param handler 設備句柄* @param x 區域起點的橫坐標* @param y 區域起點的縱坐標* @param width 區域寬度* @param height 區域高度*/
void clear_area_device_oled(CCDeviceHandler* handler, uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{OLED_Handle* handle = (OLED_Handle*)handler->handle;oled_helper_clear_area(handle, x, y, width, height);
}
?
/*** @brief 更新指定區域的顯示內容* * @param handler 設備句柄* @param x 區域起點的橫坐標* @param y 區域起點的縱坐標* @param width 區域寬度* @param height 區域高度*/
void update_area_device_oled(CCDeviceHandler* handler, uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{OLED_Handle* handle = (OLED_Handle*)handler->handle;oled_helper_update_area(handle, x, y, width, height);
}
?
/*** @brief 反轉整個屏幕的顯示顏色* * @param handler 設備句柄*/
void reverse_device_oled(CCDeviceHandler* handler)
{OLED_Handle* handle = (OLED_Handle*)handler->handle;oled_helper_reverse(handle);
}
?
/*** @brief 反轉指定區域的顯示顏色* * @param handler 設備句柄* @param x 區域起點的橫坐標* @param y 區域起點的縱坐標* @param width 區域寬度* @param height 區域高度*/
void reversearea_device_oled(CCDeviceHandler* handler, uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{OLED_Handle* handle = (OLED_Handle*)handler->handle;oled_helper_reversearea(handle, x, y, width, height);
}
?
/*** @brief 繪制指定區域的圖像* * @param handler 設備句柄* @param x 區域起點的橫坐標* @param y 區域起點的縱坐標* @param width 區域寬度* @param height 區域高度* @param sources 圖像數據源指針*/
void draw_area_device_oled(CCDeviceHandler* handler, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t* sources
){OLED_Handle* handle = (OLED_Handle*)handler->handle;oled_helper_draw_area(handle, x, y, width, height, sources);
}
?
/*** @brief 獲取 OLED 設備屬性* * @param handler 設備句柄* @param getter 屬性獲取指針* @param p 屬性類型(如:高度、寬度、是否支持 RGB 等)*/
void property_fetcher_device_oled(CCDeviceHandler* handler, void* getter, CommonProperty p
)
{OLED_Handle* handle = (OLED_Handle*)handler->handle;switch (p){case CommonProperty_HEIGHT:{ ? int16_t* pHeight = (int16_t*)getter;*pHeight = oled_height(handle);} break;
?case CommonProperty_WIDTH:{int16_t* pWidth = (int16_t*)getter;*pWidth = oled_width(handle);} break;
?case CommonProperty_SUPPORT_RGB:{uint8_t* pSupportRGB = (uint8_t*)getter;*pSupportRGB = oled_support_rgb(handle);} break;
?default:break;}
}
題外話:設備的屬性,與設計一個相似函數化簡的通用辦法

繪圖設備有自己的屬性,比如說告知自己的可繪圖范圍,是否支持RGB色彩繪圖等等,我們的辦法是提供一個對外暴露的可以訪問的devicePropertyEnum

typedef enum{CommonProperty_WIDTH,CommonProperty_HEIGHT,CommonProperty_SUPPORT_RGB
}CommonProperty;

設計一個接口,這個接口函數就是FetchProperty

typedef void(*FetchProperty)(CCDeviceHandler*, void*, CommonProperty p);

上層框架代碼提供一個承接返回值的void*和查詢的設備以及查詢類型,我們就返回這個設備的期望屬性

/*** @brief 獲取 OLED 設備屬性* * @param handler 設備句柄* @param getter 屬性獲取指針* @param p 屬性類型(如:高度、寬度、是否支持 RGB 等)*/
void property_fetcher_device_oled(CCDeviceHandler* handler, void* getter, CommonProperty p
)
{OLED_Handle* handle = (OLED_Handle*)handler->handle;switch (p){case CommonProperty_HEIGHT:{ ? int16_t* pHeight = (int16_t*)getter;*pHeight = oled_height(handle);} break;
?case CommonProperty_WIDTH:{int16_t* pWidth = (int16_t*)getter;*pWidth = oled_width(handle);} break;
?case CommonProperty_SUPPORT_RGB:{uint8_t* pSupportRGB = (uint8_t*)getter;*pSupportRGB = oled_support_rgb(handle);} break;
?default:break;}
}

這個就是一種設計返回相似內容的數據的設計思路,將過多相同返回的函數簡化為一個函數,將差異縮小到使用枚舉宏而不是一大坨函數到處拉屎的設計方式

任務提示:筆者這里實際上做的不夠好,你需要知道的是,我在這里是沒有做錯誤處理的。啥意思?你必須讓人家知道你返回的值是不是合法的,人家才知道這個值敢不敢用。

筆者提示您,兩種辦法:

  1. 返回值上動手腳:這個是筆者推介的,也是Linux設備代碼中使用的,那就是將屬性獲取的函數簽名返回值修改為uint8_t,或者更進一步的封裝:

    typedef enum {FETCH_PROPERTY_FAILED;  // 0, YOU CAN USE AS FALSE, BUT NOT RECOMMENDED!FETCH_PROPERTY_SUCCESS; // 1, YOU CAN USE AS TRUE, BUT NOT RECOMMENDED!
    }FetchPropertyStatus;
    ?
    /*** @brief 獲取 OLED 設備屬性* * @param handler 設備句柄* @param getter 屬性獲取指針* @param p 屬性類型(如:高度、寬度、是否支持 RGB 等)* @return */
    FetchPropertyStatus property_fetcher_device_oled(CCDeviceHandler* handler, void* getter, CommonProperty p
    )
    {OLED_Handle* handle = (OLED_Handle*)handler->handle;switch (p){case CommonProperty_HEIGHT:{ ? int16_t* pHeight = (int16_t*)getter;*pHeight = oled_height(handle);} break;
    ?case CommonProperty_WIDTH:{int16_t* pWidth = (int16_t*)getter;*pWidth = oled_width(handle);} break;
    ?case CommonProperty_SUPPORT_RGB:{uint8_t* pSupportRGB = (uint8_t*)getter;*pSupportRGB = oled_support_rgb(handle);} break;
    ?default:return FETCH_PROPERTY_FAILED; // not supported property}return FETCH_PROPERTY_SUCCESS; // fetched value can be used for further
    }

    使用上,事情也就變得非常的簡單,筆者后面的一個代碼

     ?  int16_t device_width = 0;device_handle->operations.property_function(device_handle, &device_width, CommonProperty_WIDTH);int16_t device_height = 0;device_handle->operations.property_function(device_handle, &device_height, CommonProperty_HEIGHT);

    也就可以更加合理的修改為

     ?  FetchPropertyStatus status;// fetch the width propertyint16_t device_width = 0;status = device_handle->operations.property_function(device_handle, &device_width, CommonProperty_WIDTH);// check if the value validif(!statue){// handling error, or enter HAL_Hard_Fault... anyway!}int16_t device_height = 0;statue = device_handle->operations.property_function(device_handle, &device_height, CommonProperty_HEIGHT);// check if the value validif(!statue){// handling error, or enter HAL_Hard_Fault... anyway!}// now pass the check// use the variable directly...

  2. 選取一個非法值。比如說

    #define INVALID_PROPERTY_VALUE      -1
    ...
    default:{   (int8_t*)value = (int8_t*)getter;value = INVALID_PROPERTY_VALUE;}

    但是顯然不好!我們沒辦法區分:是不支持這個屬性呢?還是設備的返回值確實就是-1呢?誰知道呢?所以筆者很不建議在這樣的場景下這樣做!甚至更糟糕的,如果是返回設備的長度,我們使用的是uint16_t接受,那么我們完全沒辦法區分究竟是設備是0xFFFF長,還是是非法值呢?我們一不小心把判斷值的非法和值的含義本身混淆在一起了!

現在,我們就可以完成對一整個設備的抽象了。

使用函數指針來操作設備

筆者之前的代碼已經反反復復出現了使用函數指針而不是調用函數來進行操作,從開銷分析上講,我們多了若干次的解引用操作,但是從封裝上,我們明確的歸屬了函數隸屬于繪圖設備的方法,在極大量的代碼下,這樣起到了一種自說明的效果。

比起來,在業務層次(拿庫做應用的層次,比如說開發一個OLED菜單,做一個恐龍奔跑小游戲,或者是繪制電棍突臉尖叫的動畫),我們只需要強調是這個設備在繪圖

device_handle->operations.updateArea_function(...);

而不是我們讓繪圖的是這個設備

updateArea_device(device_handle, ...);

顯然前者更加的自然。

總結一下

其實,就是完成了對繪圖設備的特化,現在,我們終于可以直接使用Device作為繪圖設備而不是OLED_Handle,下一步,我們就開始真正的手搓設備繪制了。

目錄導覽

總覽

協議層封裝

OLED設備封裝

繪圖設備抽象

基礎圖形庫封裝

基礎組件實現

動態菜單組件實現

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/67567.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/67567.shtml
英文地址,請注明出處:http://en.pswp.cn/web/67567.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Git 版本控制:基礎介紹與常用操作

目錄 Git 的基本概念 Git 安裝與配置 Git 常用命令與操作 1. 初始化本地倉庫 2. 版本控制工作流程 3. 分支管理 4. 解決沖突 5. 回退和撤銷 6. 查看提交日志 前言 在軟件開發過程中,開發者常常需要在現有程序的基礎上進行修改和擴展。但如果不加以管理&am…

(筆記+作業)書生大模型實戰營春節卷王班---L0G2000 Python 基礎知識

學員闖關手冊:https://aicarrier.feishu.cn/wiki/QtJnweAW1iFl8LkoMKGcsUS9nld 課程視頻:https://www.bilibili.com/video/BV13U1VYmEUr/ 課程文檔:https://github.com/InternLM/Tutorial/tree/camp4/docs/L0/Python 關卡作業:htt…

仿真設計|基于51單片機的高速路口貨車稱重系統仿真

目錄 具體實現功能 設計介紹 51單片機簡介 資料內容 仿真實現(protues8.7) 程序(Keil5) 全部內容 資料獲取 具體實現功能 (1)LCD1602液晶第一行顯示當前的車輛重量,第二行顯示車輛重量…

Ubuntu Server 安裝 XFCE4桌面

Ubuntu Server沒有桌面環境,一些軟件有桌面環境使用起來才更加方便,所以我嘗試安裝桌面環境。常用的桌面環境有:GNOME、KDE Plasma、XFCE4等。這里我選擇安裝XFCE4桌面環境,主要因為它是一個極輕量級的桌面環境,適合內…

2025:影刀RPA使用新實踐--CSDN博客下載

文章目錄 一鍵CSDN博客下載器程序說明指導說明使用步驟 獲取方法 一鍵CSDN博客下載器 程序說明 配置信息:CSDN賬號(手機號/郵箱/用戶名)、密碼、博客文件類型支持markdown格式、html格式(默認值markdown格式)、博客保…

深度學習的應用

目錄 一、機器視覺 1.1 應用場景 1.2 常見的計算機視覺任務 1.2.1 圖像分類 1.2.2 目標檢測 1.2.3 圖像分割 二、自然語言處理 三、推薦系統 3.1 常用的推薦系統算法實現方案 四、圖像分類實驗補充 4.1 CIFAR-100 數據集實驗 實驗代碼 4.2 CIFAR-10 實驗代碼 深…

前端js高級25.1.30

原型:函數的組成結構 通過這個圖我們需要知道。 假設我們創建了一個Foo函數。 規則:Function.protoType是函數顯示原型。__proto__是隱式對象。 Function、Object、Foo函數的__proto__指向了Function.protoType說明。這三個都依托function函數來創建。…

android 音視頻系列引導

音視頻這塊的知識點自己工作中有用到,一直沒有好好做一個總結,原因有客觀和主觀的。 客觀是工作太忙,沒有成段時間做總結。 主觀自己懶。 趁著這次主動離職拿了n1的錢,休息一下,對自己的人生做一下總結,…

為AI聊天工具添加一個知識系統 之80 詳細設計之21 符號邏輯 之1

本文要點 要點 前面我們討論了本項目中的正則表達式。現在我們將前面討論的正則表達式視為狹義的符號文本及其符號規則rule(認識的原則--認識上認識對象的約束),進而在更廣泛的視角下將其視為符號邏輯及其符號原則principle(知識…

.NET Core緩存

目錄 緩存的概念 客戶端響應緩存 cache-control 服務器端響應緩存 內存緩存(In-memory cache) 用法 GetOrCreateAsync 緩存過期時間策略 緩存的過期時間 解決方法: 兩種過期時間策略: 絕對過期時間 滑動過期時間 兩…

自動駕駛---蘇箐對智駕產品的思考

1 前言 對于更高級別的自動駕駛,很多人都有不同的思考,方案也好,產品也罷。最近在圈內一位知名的自動駕駛專家蘇箐發表了他自己對于自動駕駛未來的思考。 蘇箐是地平線的副總裁兼首席架構師,同時也是高階智能駕駛解決方案SuperDri…

Sklearn 中的邏輯回歸

邏輯回歸的數學模型 基本模型 邏輯回歸主要用于處理二分類問題。二分類問題對于模型的輸出包含 0 和 1,是一個不連續的值。分類問題的結果一般不能由線性函數求出。這里就需要一個特別的函數來求解,這里引入一個新的函數 Sigmoid 函數,也成…

FPGA|使用quartus II通過AS下載POF固件

1、將開發板設置到AS下載擋位,或者把下載線插入到AS端口 2、打開quartus II,選擇Tools→Programmer→ Mode選擇Active Serial Programming 3、點擊左側Add file…,選擇 .pof 文件 →start 4、勾選program和verify(可選&#xff0…

.Net / C# 分析文件編碼 并將 各種編碼格式 轉為 另一個編碼格式 ( 比如: GB2312→UTF-8, UTF-8→GB2312)

相關庫 .Net 8 編碼識別: github.com/CharsetDetector/UTF-unknown <PackageReference Include"UTF.Unknown" Version"2.5.1" />代碼 using UtfUnknown;var dir_path "D:\\Desktop\\新建文件夾2\\新建文件夾"; var dir_new_path &quo…

32. C 語言 安全函數( _s 尾綴)

本章目錄 前言什么是安全函數&#xff1f;安全函數的特點主要的安全函數1. 字符串操作安全函數2. 格式化輸出安全函數3. 內存操作安全函數4. 其他常用安全函數 安全函數實例示例 1&#xff1a;strcpy_s 和 strcat_s示例 2&#xff1a;memcpy_s示例 3&#xff1a;strtok_s 總結 …

淺談網絡 | 容器網絡之Flannel

目錄 云原生網絡架構深度解構&#xff1a;Flannel的設計哲學與實現機制Flannel架構解析&#xff1a;三層核心設計原則UDP模式&#xff08;用戶態隧道&#xff09;VXLAN模式&#xff08;內核態隧道&#xff09;Host-GW模式&#xff08;直連路由&#xff09; 生產環境架構選型與調…

autosar bsw 的關鍵模塊

AUTOSAR&#xff08;AUTomotive Open System ARchitecture&#xff09;的**基礎軟件層&#xff08;BSW&#xff0c;Basic Software&#xff09;**是汽車電子系統標準化的核心&#xff0c;負責提供硬件抽象、通信、診斷、安全等基礎服務。以下是BSW的關鍵模塊及其功能分類&#…

hive:基本數據類型,關于表和列語法

基本數據類型 Hive 的數據類型分為基本數據類型和復雜數據類型 加粗的是常用數據類型 BOOLEAN出現ture和false外的其他值會變成NULL值 沒有number,decimal類似number 如果輸入的數據不符合數據類型, 映射時會變成NULL, 但是數據本身并沒有被修改 創建表 創建表的本質其實就是在…

2025創業思路和方向有哪些?

創業思路和方向是決定創業成功與否的關鍵因素。以下是一些基于找到的參考內容的創業思路和方向&#xff0c;旨在激發創業靈感&#xff1a; 一、技術創新與融合&#xff1a; 1、智能手機與云電視結合&#xff1a;開發集成智能手機功能的云電視&#xff0c;提供通訊、娛樂一體化體…

航空客戶價值的數據挖掘與分析(numpy+pandas+matplotlib+scikit-learn)

航空客戶價值的數據挖掘與分析(numpy+pandas+matplotlib+scikit-learn) K-Means聚類:https://en.wikipedia.org/wiki/K-means_clustering寫在前面 實現目的:基于K-Means聚類分析模型實現航空客戶價值大數據分析。 電腦系統:Windows 使用軟件:Anaconda(Jupyter Notebook)…