海康工业相机镜头阴影矫正LSC
海康工业相机镜头阴影矫正LSC

海康工业相机镜头阴影矫正LSC

前言

  • 镜头阴影校正(Lens Shading Correction)是为了解决由于lens的光学特性,由于镜头对于光学折射不均匀导致的镜头周围出现阴影的情况。
  • 由于Lens的光学特性,Sensor影像区的边缘区域接收的光强比中心小,所造成的中心和四角亮度不一致的现象。镜头本身就是一个凸透镜,由于凸透镜原理,中心的感光必然比周边多。如图所示:
    e9b9c4b7812bfdd382e8e69e51f96c46.png
    实际成像效果如图:
    ed23432e8a20aad52a32060dc270b2d3.png
    中间区域发亮,四角发黑

海康工业相机SDK LSC算法矫正接口

SDK下载获取

海康机器视觉sdk简介,下载链接:机器视觉下载中心,下载安装MVS即可
ccef24b0069c0aa29a2445062c87d277.png
示例程序demo,参考“C:\Program Files (x86)\MVS\Development\Samples\VC\VS\SimpleSamples*LensShadingCorrection*”
044d8da3aa8871886350980ee2393cd4.png

代码流程简介

LSC矫正主要分为标定文件生成、lsc矫正使用

LSC标定文件生成

核心思想是在取流接口中,或者到一张待标定的图片,调用标定接口进行标定文件生成
如下代码,再回调函数中,调用MV_CC_LSCCalib接口,生成LSCCalib.bin标定文件

void __stdcall ImageCallBackEx(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser)
{
    printf("Get One Frame: Width[%d], Height[%d], nFrameNum[%d]\n", pFrameInfo->nWidth, pFrameInfo->nHeight, pFrameInfo->nFrameNum);
    int nRet = MV_OK;
    //判断是否需要标定
    if (true == g_IsNeedCalib)
    {
        // LSC标定
        MV_CC_LSC_CALIB_PARAM stLSCCalib = {0};
        stLSCCalib.nWidth = pFrameInfo->nWidth;
        stLSCCalib.nHeight = pFrameInfo->nHeight;
        stLSCCalib.enPixelType = pFrameInfo->enPixelType;
        stLSCCalib.pSrcBuf = pData;
        stLSCCalib.nSrcBufLen = pFrameInfo->nFrameLen;
        if (g_pCalibBuf == NULL || g_nCalibBufSize < (pFrameInfo->nWidth*pFrameInfo->nHeight*sizeof(unsigned short)))
        {
            if (g_pCalibBuf)
            {
                free(g_pCalibBuf);
                g_pCalibBuf = NULL;
                g_nCalibBufSize = 0;
            }
            g_pCalibBuf = (unsigned char *)malloc(pFrameInfo->nWidth*pFrameInfo->nHeight*sizeof(unsigned short));
            if (g_pCalibBuf == NULL)
            {
                printf("malloc pCalibBuf fail !\n");
                return;
            }
            g_nCalibBufSize = pFrameInfo->nWidth*pFrameInfo->nHeight*sizeof(unsigned short);
        }
        stLSCCalib.pCalibBuf = g_pCalibBuf;
        stLSCCalib.nCalibBufSize = g_nCalibBufSize;
        stLSCCalib.nSecNumW = 689;
        stLSCCalib.nSecNumH = 249;
        stLSCCalib.nPadCoef = 2;
        stLSCCalib.nCalibMethod = 2;
        stLSCCalib.nTargetGray = 100;
        //同一个相机,在场景、算法参数和图像参数都不变情况下,理论上只需要进行一次标定,可将标定表保存下来。
        //不同相机图片标定出来的标定表不可复用,当场景改变或算法参数改变或图像参数改变时,需要重新标定。
        nRet = MV_CC_LSCCalib(pUser, &stLSCCalib);
        if (MV_OK != nRet)
        {
            printf("LSC Calib fail! nRet [0x%x]\n", nRet);
            return;
        }

        //保存标定表到本地
        FILE* fp_out = fopen("./LSCCalib.bin", "wb");
        if (NULL == fp_out)
        {
            return ;
        }
        fwrite(stLSCCalib.pCalibBuf, 1, stLSCCalib.nCalibBufLen, fp_out);
        fclose(fp_out);
        g_IsNeedCalib = false;
    }
}

MV_CC_LSCCalib接口中,需要传入算法参数,来看一下这个结构体MV_CC_LSC_CALIB_PARAM的构成
除了常规的图像宽、高、像素格式之外的参数,部分算法参数如下解释:

参数 解释 建议值
nSecNumW 宽度分快数,nSecNumW越大,矫正后一致性越好,但此值越大,会导致部分缺陷类检测缺陷失真;nSecNumW<widthmax 图像最大宽度除以4
nSecNumH 高度分快数,nSecNumH越大,矫正后一致性越好,但此值越大,会导致部分缺陷类检测缺陷失真;nSecNumH<hightmax 图像最大高度除以4
nPadCoef 边缘填充系数,范围1-5 2
nCalibMethod 标定方式[0-2],提供三种标定方法1:中心为亮度基准,取图像中心区域灰度值为基准进行矫正;2:最亮区域;3:目标亮度,像数格式8bit时,nTargetGray值范围可设置为[0-255];像数格式10bit时,nTargetGray值范围可设置为[0-1023];像数格式12bit时,nTargetGray值范围可设置为[0-4095] 0
nTargetGray nCalibMethod取值为2时有效 nCalibMethod为2时,图像中心区域平均亮度
typedef struct _MV_CC_LSC_CALIB_PARAM_T_
{
    unsigned int            nWidth;                                 ///< [IN]  \~chinese 图像宽度[16,65535]     \~english Image Width
    unsigned int            nHeight;                                ///< [IN]  \~chinese 图像高度[16-65535]     \~english Image Height
    enum MvGvspPixelType    enPixelType;                            ///< [IN]  \~chinese 像素格式               \~english Pixel format
    unsigned char*          pSrcBuf;                                ///< [IN]  \~chinese 输入数据缓存           \~english Input data buffer
    unsigned int            nSrcBufLen;                             ///< [IN]  \~chinese 输入数据长度           \~english Input data length
    unsigned char*          pCalibBuf;                              ///< [OUT] \~chinese 输出标定表缓存         \~english Output calib buffer
    unsigned int            nCalibBufSize;                          ///< [IN]  \~chinese 提供的标定表缓冲大小(nWidth*nHeight*sizeof(unsigned short))    \~english Provided output buffer size
    unsigned int            nCalibBufLen;                           ///< [OUT] \~chinese 输出标定表缓存长度     \~english Output calib buffer length
    unsigned int            nSecNumW;                               ///< [IN]  \~chinese 宽度分块数             \~english Width Sec num
    unsigned int            nSecNumH;                               ///< [IN]  \~chinese 高度分块数             \~english Height Sec num
    unsigned int            nPadCoef;                               ///< [IN]  \~chinese 边缘填充系数[1,5]      \~english Pad Coef[1,5]
    unsigned int            nCalibMethod;                           ///< [IN]  \~chinese 标定方式(0-中心为基准;1-最亮区域为基准;2-目标亮度为基准) \~english Calib method
    unsigned int            nTargetGray;                            ///< [IN]  \~chinese 目标亮度(标定方式为2时有效)    \~english Target Gray
                                                                    ///< \~chinese 8位,范围:[0,255]            \~english 8bit,range:[0,255]
                                                                    ///< \~chinese 10位,范围:[0,1023]          \~english 10bit,range:[0,1023]
                                                                    ///< \~chinese 12位,范围:[0,4095]          \~english 12bit,range:[0,4095]
    unsigned int            nRes[8];                                ///<       \~chinese 预留                   \~english Reserved
}MV_CC_LSC_CALIB_PARAM;

标定过程,需要特定场景的,按照如下步骤进行
1、相机对准均匀场景(均匀光源、白纸、白板等),通过调节曝光、增益等,使得图像灰度在120-160之间(镜头光圈保持与实际使用场景一致)
1a331c9558bc8dbe6830f27de18bf923.png
2、运行标定程序,生成标定文件;可直接运行LensShadingCorrection示例demo,exe路径下会有当前标定文件生成
3、拍摄实际物体,运行矫正程序,观察效果;可直接运行LensShadingCorrection示例demo观察
如果标定矫正效果不佳,那么就需要重新调整标定场景,重新生成标定文件
同理,如遇到多种光源切换场景,可以生成不同的标定文件,进行动态切换矫正

LSC矫正

拿到标定文件后,矫正过程相对简单,获取一张图像后,再调用MV_CC_LSCCorrect接口即可完成(每一张图片都需要调用,一张一张矫正)

void __stdcall ImageCallBackEx(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser)
{
    printf("Get One Frame: Width[%d], Height[%d], nFrameNum[%d]\n", pFrameInfo->nWidth, pFrameInfo->nHeight, pFrameInfo->nFrameNum);
    int nRet = MV_OK;
    // LSC校正
    if (g_pDstData == NULL || g_nDstDataSize < pFrameInfo->nFrameLen)
    {
        if (g_pDstData)
        {
            free(g_pDstData);
            g_pDstData = NULL;
            g_nDstDataSize = 0;
        }

        g_pDstData = (unsigned char *)malloc(pFrameInfo->nFrameLen);
        if (g_pDstData == NULL)
        {
            printf("malloc pDstData fail !\n");
            return;
        }
        g_nDstDataSize = pFrameInfo->nFrameLen;
    }
    MV_CC_LSC_CORRECT_PARAM stLSCCorrectParam = {0};
    stLSCCorrectParam.nWidth = pFrameInfo->nWidth;
    stLSCCorrectParam.nHeight = pFrameInfo->nHeight;
    stLSCCorrectParam.enPixelType = pFrameInfo->enPixelType;
    stLSCCorrectParam.pSrcBuf = pData;
    stLSCCorrectParam.nSrcBufLen = pFrameInfo->nFrameLen;
    stLSCCorrectParam.pDstBuf = g_pDstData;
    stLSCCorrectParam.nDstBufSize = g_nDstDataSize;
    stLSCCorrectParam.pCalibBuf = g_pCalibBuf;
    stLSCCorrectParam.nCalibBufLen = g_nCalibBufSize;
    nRet = MV_CC_LSCCorrect(pUser, &stLSCCorrectParam);
    if (MV_OK != nRet)
    {
        printf("LSC Correct fail! nRet [0x%x]\n", nRet);
        return;
    }
    //矫正完成,其他图像处理
}

矫正耗时测试

测试1:不同分辨率图像,不同版本算法,耗时情况

分辨率 x64 x32
2448*2048 4.3ms 5.6ms
3840*2748 8.6ms 11.89ms
5472*3648 16.45ms 22.14ms
  • 算法耗时取决于图像分辨率大小,分辨率越大,耗时越大
    测试2:不同算法参数,矫正模块耗时情况
分辨率 nCalibMethod 耗时 分块数 耗时 边缘填充系数 耗时
2448*2048 0:中心基准 4.25ms 16*16 4.28ms 1 4.26ms
2448*2048 1:最亮基准 4.25ms 600*400 4.30ms 3 4.20ms
2448*2048 2:目标基准 4.28ms 2448*2048 4.25ms 5 4.26ms

图像效果对比

测试参数:

参数 Value
分辨率 2448*2048
nSecNumW 2448
nSecNumH 2048
nPadCoef 2
nCalibMethod 0

0ecab3130265a25744ce5a6d2e599518.png

LensShadingCorrection完整示例程序

#include <stdio.h> #include <Windows.h> #include <conio.h> #include <io.h> #include "MvCameraControl.h" // ch:等待按键输入 | en:Wait for key press void WaitForKeyPress(void) { while(!_kbhit()) { Sleep(10); } _getch(); } bool PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo) { if (NULL == pstMVDevInfo) { printf("The Pointer of pstMVDevInfo is NULL!\n"); return false; } if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE) { int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24); int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16); int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8); int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff); // ch:打印当前相机ip和用户自定义名字 | en:print current ip and user defined name printf("CurrentIp: %d.%d.%d.%d\n" , nIp1, nIp2, nIp3, nIp4); printf("UserDefinedName: %s\n\n" , pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName); } else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE) { printf("UserDefinedName: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName); printf("Serial Number: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chSerialNumber); printf("Device Number: %d\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.nDeviceNumber); } else { printf("Not support.\n"); } return true; } unsigned char * g_pDstData = NULL; unsigned int g_nDstDataSize = 0; unsigned char * g_pCalibBuf = NULL; unsigned int g_nCalibBufSize = 0; bool g_IsNeedCalib = true; void __stdcall ImageCallBackEx(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser) { printf("Get One Frame: Width[%d], Height[%d], nFrameNum[%d]\n", pFrameInfo->nWidth, pFrameInfo->nHeight, pFrameInfo->nFrameNum); int nRet = MV_OK; //判断是否需要标定 if (true == g_IsNeedCalib) { // LSC标定 MV_CC_LSC_CALIB_PARAM stLSCCalib = {0}; stLSCCalib.nWidth = pFrameInfo->nWidth; stLSCCalib.nHeight = pFrameInfo->nHeight; stLSCCalib.enPixelType = pFrameInfo->enPixelType; stLSCCalib.pSrcBuf = pData; stLSCCalib.nSrcBufLen = pFrameInfo->nFrameLen; if (g_pCalibBuf == NULL || g_nCalibBufSize < (pFrameInfo->nWidth*pFrameInfo->nHeight*sizeof(unsigned short))) { if (g_pCalibBuf) { free(g_pCalibBuf); g_pCalibBuf = NULL; g_nCalibBufSize = 0; } g_pCalibBuf = (unsigned char *)malloc(pFrameInfo->nWidth*pFrameInfo->nHeight*sizeof(unsigned short)); if (g_pCalibBuf == NULL) { printf("malloc pCalibBuf fail !\n"); return; } g_nCalibBufSize = pFrameInfo->nWidth*pFrameInfo->nHeight*sizeof(unsigned short); } stLSCCalib.pCalibBuf = g_pCalibBuf; stLSCCalib.nCalibBufSize = g_nCalibBufSize; stLSCCalib.nSecNumW = 689; stLSCCalib.nSecNumH = 249; stLSCCalib.nPadCoef = 2; stLSCCalib.nCalibMethod = 2; stLSCCalib.nTargetGray = 100; //同一个相机,在场景、算法参数和图像参数都不变情况下,理论上只需要进行一次标定,可将标定表保存下来。 //不同相机图片标定出来的标定表不可复用,当场景改变或算法参数改变或图像参数改变时,需要重新标定。 nRet = MV_CC_LSCCalib(pUser, &stLSCCalib); if (MV_OK != nRet) { printf("LSC Calib fail! nRet [0x%x]\n", nRet); return; } //保存标定表到本地 FILE* fp_out = fopen("./LSCCalib.bin", "wb"); if (NULL == fp_out) { return ; } fwrite(stLSCCalib.pCalibBuf, 1, stLSCCalib.nCalibBufLen, fp_out); fclose(fp_out); g_IsNeedCalib = false; } // LSC校正 if (g_pDstData == NULL || g_nDstDataSize < pFrameInfo->nFrameLen) { if (g_pDstData) { free(g_pDstData); g_pDstData = NULL; g_nDstDataSize = 0; } g_pDstData = (unsigned char *)malloc(pFrameInfo->nFrameLen); if (g_pDstData == NULL) { printf("malloc pDstData fail !\n"); return; } g_nDstDataSize = pFrameInfo->nFrameLen; } MV_CC_LSC_CORRECT_PARAM stLSCCorrectParam = {0}; stLSCCorrectParam.nWidth = pFrameInfo->nWidth; stLSCCorrectParam.nHeight = pFrameInfo->nHeight; stLSCCorrectParam.enPixelType = pFrameInfo->enPixelType; stLSCCorrectParam.pSrcBuf = pData; stLSCCorrectParam.nSrcBufLen = pFrameInfo->nFrameLen; stLSCCorrectParam.pDstBuf = g_pDstData; stLSCCorrectParam.nDstBufSize = g_nDstDataSize; stLSCCorrectParam.pCalibBuf = g_pCalibBuf; stLSCCorrectParam.nCalibBufLen = g_nCalibBufSize; nRet = MV_CC_LSCCorrect(pUser, &stLSCCorrectParam); if (MV_OK != nRet) { printf("LSC Correct fail! nRet [0x%x]\n", nRet); return; } if (pFrameInfo->nFrameNum < 10) { //保存图像到文件 MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam; memset(&stSaveFileParam, 0, sizeof(MV_SAVE_IMG_TO_FILE_PARAM)); stSaveFileParam.enImageType = MV_Image_Bmp; stSaveFileParam.enPixelType = pFrameInfo->enPixelType; stSaveFileParam.nWidth = pFrameInfo->nWidth; stSaveFileParam.nHeight = pFrameInfo->nHeight; stSaveFileParam.nDataLen = pFrameInfo->nFrameLen; stSaveFileParam.pData = pData; sprintf_s(stSaveFileParam.pImagePath, 256, "BeforeImage_w%d_h%d_fn%03d.bmp", stSaveFileParam.nWidth, stSaveFileParam.nHeight, pFrameInfo->nFrameNum); nRet = MV_CC_SaveImageToFile(pUser, &stSaveFileParam); if (MV_OK != nRet) { printf("SaveImageToFile failed[%x]!\n", nRet); return; } stSaveFileParam.pData = g_pDstData; sprintf_s(stSaveFileParam.pImagePath, 256, "AfterImage_w%d_h%d_fn%03d.bmp", stSaveFileParam.nWidth, stSaveFileParam.nHeight, pFrameInfo->nFrameNum); nRet = MV_CC_SaveImageToFile(pUser, &stSaveFileParam); if (MV_OK != nRet) { printf("SaveImageToFile failed[%x]!\n", nRet); return; } } } int main() { int nRet = MV_OK; void* handle = NULL; do { // ch:枚举设备 | en:Enum device MV_CC_DEVICE_INFO_LIST stDeviceList; memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST)); nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList); if (MV_OK != nRet) { printf("Enum Devices fail! nRet [0x%x]\n", nRet); break; } if (stDeviceList.nDeviceNum > 0) { for (unsigned int i = 0; i < stDeviceList.nDeviceNum; i++) { printf("[device %d]:\n", i); MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i]; if (NULL == pDeviceInfo) { break; } PrintDeviceInfo(pDeviceInfo); } } else { printf("Find No Devices!\n"); break; } printf("Please Input camera index(0-%d):", stDeviceList.nDeviceNum-1); unsigned int nIndex = 0; scanf_s("%d", &nIndex); if (nIndex >= stDeviceList.nDeviceNum) { printf("Input error!\n"); break; } // ch:选择设备并创建句柄 | en:Select device and create handle nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]); if (MV_OK != nRet) { printf("Create Handle fail! nRet [0x%x]\n", nRet); break; } // ch:打开设备 | en:Open device nRet = MV_CC_OpenDevice(handle); if (MV_OK != nRet) { printf("Open Device fail! nRet [0x%x]\n", nRet); break; } // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera) if (stDeviceList.pDeviceInfo[nIndex]->nTLayerType == MV_GIGE_DEVICE) { int nPacketSize = MV_CC_GetOptimalPacketSize(handle); if (nPacketSize > 0) { nRet = MV_CC_SetIntValue(handle,"GevSCPSPacketSize",nPacketSize); if(nRet != MV_OK) { printf("Warning: Set Packet Size fail nRet [0x%x]!", nRet); } } else { printf("Warning: Get Packet Size fail nRet [0x%x]!", nPacketSize); } } // ch:设置触发模式为off | eb:Set trigger mode as off nRet = MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_OFF); if (MV_OK != nRet) { printf("Set Trigger Mode fail! nRet [0x%x]\n", nRet); break; } //判断是否可以本地导入 FILE* fp = fopen("./LSCCalib.bin", "rb"); if (fp) { int nFileLen = filelength(fileno(fp)); if (g_pCalibBuf == NULL || g_nCalibBufSize < nFileLen) { if (g_pCalibBuf) { free(g_pCalibBuf); g_pCalibBuf = NULL; g_nCalibBufSize = 0; } g_pCalibBuf = (unsigned char *)malloc(nFileLen); if (g_pCalibBuf == NULL) { printf("malloc pCalibBuf fail !\n"); break; } g_nCalibBufSize = nFileLen; } fread(g_pCalibBuf, 1, g_nCalibBufSize, fp); fclose(fp); g_IsNeedCalib = false; } // ch:注册抓图回调 | en:Register image callback nRet = MV_CC_RegisterImageCallBackEx(handle, ImageCallBackEx, handle); if (MV_OK != nRet) { printf("Register Image CallBack fail! nRet [0x%x]\n", nRet); break; } // ch:开始取流 | en:Start grab image nRet = MV_CC_StartGrabbing(handle); if (MV_OK != nRet) { printf("Start Grabbing fail! nRet [0x%x]\n", nRet); break; } printf("Press a key to stop grabbing.\n"); WaitForKeyPress(); Sleep(1000); // ch:停止取流 | en:Stop grab image nRet = MV_CC_StopGrabbing(handle); if (MV_OK != nRet) { printf("Stop Grabbing fail! nRet [0x%x]\n", nRet); break; } // ch:关闭设备 | en:Close device nRet = MV_CC_CloseDevice(handle); if (MV_OK != nRet) { printf("Close Device fail! nRet [0x%x]\n", nRet); break; } // ch:销毁句柄 | en:Destroy handle nRet = MV_CC_DestroyHandle(handle); if (MV_OK != nRet) { printf("Destroy Handle fail! nRet [0x%x]\n", nRet); break; } } while (0); if (g_pCalibBuf) { free(g_pCalibBuf); g_pCalibBuf = NULL; g_nCalibBufSize = 0; } if (g_pDstData) { free(g_pDstData); g_pDstData = NULL; g_nDstDataSize = 0; } if (nRet != MV_OK) { if (handle != NULL) { MV_CC_DestroyHandle(handle); handle = NULL; } } printf("Press a key to exit.\n"); WaitForKeyPress(); return 0; }
版权声明:本文为V社区用户原创内容,转载时必须标注文章的来源(V社区),文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:v-club@hikrobotics.com 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
上一篇

海康工业网口相机组播功能

下一篇

彩色工业相机RGB8转RGBPlanar格式方法

评论请先登录 登录
全部评论 0
Lv.0
0
关注
0
粉丝
0
创作
0
获赞
相关阅读
  • 移动机器人技术分享-25年10月
    2025-11-03 浏览 0
  • RobotPilotV2.2.0震撼发布:文档全面升级,助您轻松解锁新功能!
    2025-10-29 浏览 0
  • 关于海康RCS4.X单机部署bond0与bond1的理解
    2025-11-03 浏览 0
  • VM模块-GROUP(组合模块),无代码循环使用方法分享
    2025-10-27 浏览 0
  • CeMAT物流展圆满落幕,智慧物流引领行业新未来
    2025-11-04 浏览 0

请升级浏览器版本

您正在使用的浏览器版本过低,请升级最新版本以获得更好的体验。

推荐使用以下浏览器