海康工业相机BayerRG8转BGR8

今天出来冒个泡吧!多话不说,理论也不讲。

海康工业相机BayerRG8转BGR8

开发板找到能用的C++代码:

//海康PixelType_Gvsp_BayerRG8转PixelType_Gvsp_BGR8_Packed格式代码
int Bayer8ToRgb24(unsigned char* pbBayer, int dwWidth, int dwHeight, unsigned char* pbRgb, int iRgbBufLen)
{
    if (nullptr == pbBayer || nullptr == pbRgb)
    {
        return 0;
    }
    if (dwWidth <= 0 || dwHeight <= 0 || iRgbBufLen <= 0)
    {
        return 0;
    }
    if (iRgbBufLen != dwWidth * dwHeight * 3)
    {
        return 0;
    }

    unsigned char* m_pBay = pbBayer;
    unsigned char* m_pRGB24 = pbRgb;
    unsigned long m_ulWidth = dwWidth;
    unsigned long m_ulHeight = dwHeight;

#define R(x,y) m_pRGB24[2 + 3 * ((x) + m_ulWidth * (y))]
#define G(x,y) m_pRGB24[1 + 3 * ((x) + m_ulWidth * (y))]
#define B(x,y) m_pRGB24[0 + 3 * ((x) + m_ulWidth * (y))]
#define Bay(x,y) m_pBay[(x) + m_ulWidth * (y)]

    auto bayer_copy = [&](int x, int y)
        {
            G(x + 0, y + 0) = Bay(x + 0, y + 0);
            G(x + 1, y + 1) = Bay(x + 1, y + 1);
            G(x + 0, y + 1) = G(x + 1, y + 0) = ((unsigned long)Bay(x + 0, y + 0) + (unsigned long)Bay(x + 1, y + 1)) / 2;
            R(x + 0, y + 0) = R(x + 1, y + 0) = R(x + 1, y + 1) = R(x + 0, y + 1) = Bay(x + 0, y + 1);
            B(x + 1, y + 1) = B(x + 0, y + 0) = B(x + 0, y + 1) = B(x + 1, y + 0) = Bay(x + 1, y + 0);
        };

    auto bayer_bilinear = [&](int x, int y)
        {
            R(x + 0, y + 0) = ((unsigned long)Bay(x + 0, y + 1) + (unsigned long)Bay(x + 0, y - 1)) / 2;
            G(x + 0, y + 0) = Bay(x + 0, y + 0);
            B(x + 0, y + 0) = ((unsigned long)Bay(x - 1, y + 0) + (unsigned long)Bay(x + 1, y + 0)) / 2;
            R(x + 0, y + 1) = Bay(x + 0, y + 1);
            G(x + 0, y + 1) = ((unsigned long)Bay(x + 0, y + 0) + (unsigned long)Bay(x + 0, y + 2) + (unsigned long)Bay(x - 1, y + 1) + (unsigned long)Bay(x + 1, y + 1)) / 4;
            B(x + 0, y + 1) = ((unsigned long)Bay(x + 1, y + 0) + (unsigned long)Bay(x - 1, y + 0) + (unsigned long)Bay(x + 1, y + 2) + (unsigned long)Bay(x - 1, y + 2)) / 4;
            R(x + 1, y + 0) = ((unsigned long)Bay(x + 0, y + 1) + (unsigned long)Bay(x + 2, y + 1) + (unsigned long)Bay(x + 0, y - 1) + (unsigned long)Bay(x + 2, y - 1)) / 4;
            G(x + 1, y + 0) = ((unsigned long)Bay(x + 0, y + 0) + (unsigned long)Bay(x + 2, y + 0) + (unsigned long)Bay(x + 1, y - 1) + (unsigned long)Bay(x + 1, y + 1)) / 4;
            B(x + 1, y + 0) = Bay(x + 1, y + 0); R(x + 1, y + 1) = ((unsigned long)Bay(x + 0, y + 1) + (unsigned long)Bay(x + 2, y + 1)) / 2; G(x + 1, y + 1) = Bay(x + 1, y + 1);
            B(x + 1, y + 1) = ((unsigned long)Bay(x + 1, y + 0) + (unsigned long)Bay(x + 1, y + 2)) / 2;
        };

    int i, j;
    for (i = 0; i < m_ulWidth; i += 2)
    {
        for (j = 0; j < m_ulHeight; j += 2)
        {
            if (i == 0 || j == 0 || i == m_ulWidth - 2 || j == m_ulHeight - 2)
            {
                bayer_copy(i, j);
            }
            else
            {
                bayer_bilinear(i, j);
            }
        }
    }
    return 1;
}

用海康SDK搞:

//海康api PixelType_Gvsp_BayerRG8转PixelType_Gvsp_BGR8_Packed格式代码
bool Convert2Mat(void* handle, stImageNode& pstImage, uint8_t** pBgrData)
{
    if (NULL == pstImage.pData)
    {
        return false; //return MV_E_PARAMETER;
    }
    unsigned int nDataSizeForBGR = pstImage.nWidth * pstImage.nHeight * 3;
    *pBgrData = (unsigned char*)malloc(nDataSizeForBGR);
    if (NULL == *pBgrData)
    {
        return false; // return MV_E_BUFOVER;
    }

    // ch:像素格式转换 | en:Convert pixel format 
    MV_CC_PIXEL_CONVERT_PARAM stConvertParam = { 0 };
    memset(&stConvertParam, 0, sizeof(MV_CC_PIXEL_CONVERT_PARAM));

    stConvertParam.nWidth = pstImage.nWidth;                        //ch:图像宽 | en:image width
    stConvertParam.nHeight = pstImage.nHeight;                      //ch:图像高 | en:image height
    stConvertParam.pSrcData = pstImage.pData;                       //ch:输入数据缓存 | en:input data buffer
    stConvertParam.nSrcDataLen = pstImage.nFrameLen;                //ch:输入数据大小 | en:input data size
    stConvertParam.enSrcPixelType = pstImage.enPixelType;           //ch:输入像素格式 | en:input pixel format
    stConvertParam.enDstPixelType = PixelType_Gvsp_BGR8_Packed;     //ch:输出像素格式 | en:output pixel format
    stConvertParam.pDstBuffer = *pBgrData;                          //ch:输出数据缓存 | en:output data buffer
    stConvertParam.nDstBufferSize = nDataSizeForBGR;                //ch:输出缓存大小 | en:output buffer size
    int nRet = MV_CC_ConvertPixelType(handle, &stConvertParam);
    if (MV_OK != nRet)
    {
        free(*pBgrData);
        printf("++++++++++++++++++++++error nFrameNum [%d]  \r\n", pstImage.nFrameNum);
        return false; //return MV_E_UNKNOW;
    }
    return true;
    //注意在外面要free pBgrData
}

完整的全帧率相机抓拍取图程序:


#include <stdio.h>
#ifdef _WIN32
#include <Windows.h>
#include <conio.h>
#include <process.h>
#endif
#include <iostream>
#include <fstream>
#include <sstream>
#include <random>
#include <queue>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <opencv2/opencv.hpp>
#include "MvCameraControl.h"


//缓存队列
template<class T>
class DataQueue
{
public:
    DataQueue(void)
    {
    };
    virtual ~DataQueue(void)
    {
        clear();
    };
public:
    bool push(T& data)
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_queue.push(std::move(data));
        return true;
    };
    bool pop(T& data)
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        if (m_queue.empty())
            return false;
        data = m_queue.front();
        m_queue.pop();
        return true;
    };
    void clear()
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        while (!m_queue.empty())
        {
            T& data = m_queue.front();
            m_queue.pop();
        }
    };
    int size()
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        return m_queue.size();
    };
protected:
    std::mutex m_mutex;
    std::queue<T> m_queue;
};

//信号量工具
class Semaphore
{
    std::mutex mx;
    std::condition_variable cond;
    long count;
public:
    Semaphore(long count = 0) : count(count) {};
    ~Semaphore(void) { post(); };
    void wait(void)
    {
        std::unique_lock<std::mutex>lock(mx);
        cond.wait(lock, [&]() {return count > 0; });
        --count;
    };
    bool wait(int stime)
    {
        std::unique_lock<std::mutex>lock(mx);
        bool bRet = cond.wait_for(lock, std::chrono::seconds(stime), [&]() {return count > 0; });
        --count;
        return bRet;
    };
    void post(void)
    {
        std::unique_lock<std::mutex>lock(mx);
        ++count;
        cond.notify_one();
    };
};

//中间缓存图像格式
typedef struct _stImageNode_
{
    unsigned char* pData;           //图像数据
    uint64_t  nFrameLen;

    unsigned int nWidth;
    unsigned int nHeight;
    unsigned int nFrameNum;
    enum MvGvspPixelType enPixelType;                //enum MvGvspPixelType
    _stImageNode_()
    {
        pData = 0;
        nFrameLen = nWidth = nHeight = nFrameNum = 0;
        enPixelType = PixelType_Gvsp_BayerGB8;
    }
    ~_stImageNode_()
    {
        if (pData != NULL)
        {
            free(pData);
        }
    }
} stImageNode;

//海康PixelType_Gvsp_BayerRG8转PixelType_Gvsp_BGR8_Packed格式代码
int Bayer8ToRgb24(unsigned char* pbBayer, int dwWidth, int dwHeight, unsigned char* pbRgb, int iRgbBufLen)
{
    if (nullptr == pbBayer || nullptr == pbRgb)
    {
        return 0;
    }
    if (dwWidth <= 0 || dwHeight <= 0 || iRgbBufLen <= 0)
    {
        return 0;
    }
    if (iRgbBufLen != dwWidth * dwHeight * 3)
    {
        return 0;
    }

    unsigned char* m_pBay = pbBayer;
    unsigned char* m_pRGB24 = pbRgb;
    unsigned long m_ulWidth = dwWidth;
    unsigned long m_ulHeight = dwHeight;

#define R(x,y) m_pRGB24[2 + 3 * ((x) + m_ulWidth * (y))]
#define G(x,y) m_pRGB24[1 + 3 * ((x) + m_ulWidth * (y))]
#define B(x,y) m_pRGB24[0 + 3 * ((x) + m_ulWidth * (y))]
#define Bay(x,y) m_pBay[(x) + m_ulWidth * (y)]

    auto bayer_copy = [&](int x, int y)
        {
            G(x + 0, y + 0) = Bay(x + 0, y + 0);
            G(x + 1, y + 1) = Bay(x + 1, y + 1);
            G(x + 0, y + 1) = G(x + 1, y + 0) = ((unsigned long)Bay(x + 0, y + 0) + (unsigned long)Bay(x + 1, y + 1)) / 2;
            R(x + 0, y + 0) = R(x + 1, y + 0) = R(x + 1, y + 1) = R(x + 0, y + 1) = Bay(x + 0, y + 1);
            B(x + 1, y + 1) = B(x + 0, y + 0) = B(x + 0, y + 1) = B(x + 1, y + 0) = Bay(x + 1, y + 0);
        };

    auto bayer_bilinear = [&](int x, int y)
        {
            R(x + 0, y + 0) = ((unsigned long)Bay(x + 0, y + 1) + (unsigned long)Bay(x + 0, y - 1)) / 2;
            G(x + 0, y + 0) = Bay(x + 0, y + 0);
            B(x + 0, y + 0) = ((unsigned long)Bay(x - 1, y + 0) + (unsigned long)Bay(x + 1, y + 0)) / 2;
            R(x + 0, y + 1) = Bay(x + 0, y + 1);
            G(x + 0, y + 1) = ((unsigned long)Bay(x + 0, y + 0) + (unsigned long)Bay(x + 0, y + 2) + (unsigned long)Bay(x - 1, y + 1) + (unsigned long)Bay(x + 1, y + 1)) / 4;
            B(x + 0, y + 1) = ((unsigned long)Bay(x + 1, y + 0) + (unsigned long)Bay(x - 1, y + 0) + (unsigned long)Bay(x + 1, y + 2) + (unsigned long)Bay(x - 1, y + 2)) / 4;
            R(x + 1, y + 0) = ((unsigned long)Bay(x + 0, y + 1) + (unsigned long)Bay(x + 2, y + 1) + (unsigned long)Bay(x + 0, y - 1) + (unsigned long)Bay(x + 2, y - 1)) / 4;
            G(x + 1, y + 0) = ((unsigned long)Bay(x + 0, y + 0) + (unsigned long)Bay(x + 2, y + 0) + (unsigned long)Bay(x + 1, y - 1) + (unsigned long)Bay(x + 1, y + 1)) / 4;
            B(x + 1, y + 0) = Bay(x + 1, y + 0); R(x + 1, y + 1) = ((unsigned long)Bay(x + 0, y + 1) + (unsigned long)Bay(x + 2, y + 1)) / 2; G(x + 1, y + 1) = Bay(x + 1, y + 1);
            B(x + 1, y + 1) = ((unsigned long)Bay(x + 1, y + 0) + (unsigned long)Bay(x + 1, y + 2)) / 2;
        };

    int i, j;
    for (i = 0; i < m_ulWidth; i += 2)
    {
        for (j = 0; j < m_ulHeight; j += 2)
        {
            if (i == 0 || j == 0 || i == m_ulWidth - 2 || j == m_ulHeight - 2)
            {
                bayer_copy(i, j);
            }
            else
            {
                bayer_bilinear(i, j);
            }
        }
    }
    return 1;
}

//海康api PixelType_Gvsp_BayerRG8转PixelType_Gvsp_BGR8_Packed格式代码
bool Convert2Mat(void* handle, stImageNode& pstImage, uint8_t** pBgrData)
{
    if (NULL == pstImage.pData)
    {
        return false; //return MV_E_PARAMETER;
    }
    unsigned int nDataSizeForBGR = pstImage.nWidth * pstImage.nHeight * 3;
    *pBgrData = (unsigned char*)malloc(nDataSizeForBGR);
    if (NULL == *pBgrData)
    {
        return false; // return MV_E_BUFOVER;
    }

    // ch:像素格式转换 | en:Convert pixel format 
    MV_CC_PIXEL_CONVERT_PARAM stConvertParam = { 0 };
    memset(&stConvertParam, 0, sizeof(MV_CC_PIXEL_CONVERT_PARAM));

    stConvertParam.nWidth = pstImage.nWidth;                        //ch:图像宽 | en:image width
    stConvertParam.nHeight = pstImage.nHeight;                      //ch:图像高 | en:image height
    stConvertParam.pSrcData = pstImage.pData;                       //ch:输入数据缓存 | en:input data buffer
    stConvertParam.nSrcDataLen = pstImage.nFrameLen;                //ch:输入数据大小 | en:input data size
    stConvertParam.enSrcPixelType = pstImage.enPixelType;           //ch:输入像素格式 | en:input pixel format
    stConvertParam.enDstPixelType = PixelType_Gvsp_BGR8_Packed;     //ch:输出像素格式 | en:output pixel format
    stConvertParam.pDstBuffer = *pBgrData;                          //ch:输出数据缓存 | en:output data buffer
    stConvertParam.nDstBufferSize = nDataSizeForBGR;                //ch:输出缓存大小 | en:output buffer size
    int nRet = MV_CC_ConvertPixelType(handle, &stConvertParam);
    if (MV_OK != nRet)
    {
        free(*pBgrData);
        printf("++++++++++++++++++++++error nFrameNum [%d]  \r\n", pstImage.nFrameNum);
        return false; //return MV_E_UNKNOW;
    }
    return true;
    //注意在外面要free pBgrData
}

//全局变量
DataQueue<stImageNode*> m_list;
Semaphore m_sem;

int main()
{
#ifdef _WIN32
    system("rd /s /q img");
    system("md img");
#else
    system("rm -rf img");
    system("mkdir img");
#endif

    printf("app start        *********************\n");
    std::atomic_bool m_stated;
    std::thread m_thread;
    MV_CC_DEVICE_INFO_LIST stDeviceList;
    memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));
    int nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE | MV_GENTL_CAMERALINK_DEVICE | MV_GENTL_CXP_DEVICE | MV_GENTL_XOF_DEVICE, &stDeviceList);
    if (MV_OK != nRet || stDeviceList.nDeviceNum < 1)
    {
        return -1;
    }

    int nIndex = 0;
    void* handle = NULL;
    MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]);//nIndex=0
    MV_CC_OpenDevice(handle);
    MV_CC_SetIntValueEx(handle, "Width", 1920);
    MV_CC_SetIntValueEx(handle, "Height", 1080);
    MV_CC_SetIntValueEx(handle, "OffsetX", 64);
    MV_CC_SetIntValueEx(handle, "OffsetY", 484);
    MV_CC_SetEnumValueByString(handle, "PixelFormat", "BayerGB8");      //可以实现90fps取图
    //MV_CC_SetEnumValueByString(handle, "PixelFormat", "RGB8Packed");  //只有20多fps 

    MVCC_INTVALUE_EX stIntEx = { 0 };
    MV_CC_GetIntValueEx(handle, "Width", &stIntEx);
    int nWidth = stIntEx.nCurValue;
    memset(&stIntEx, 0, sizeof(MVCC_INTVALUE_EX));
    MV_CC_GetIntValueEx(handle, "Height", &stIntEx);
    int nHeight = stIntEx.nCurValue;
    MV_CC_GetIntValueEx(handle, "PayloadSize", &stIntEx);
    uint64_t m_nImageSize = stIntEx.nCurValue;

    MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_OFF);             //设置触发模式为off
    MV_CC_SetBoolValue(handle, "CCMEnable", false);                             //CCM关闭
    MV_CC_SetBoolValue(handle, "ColorTransformationEnable", false);             //颜色变化关闭
    MV_CC_SetBoolValue(handle, "HueEnable", false);                             //hue关闭
    MV_CC_SetBoolValue(handle, "SaturationEnable", false);                      //饱和度关闭
    MV_CC_SetBoolValue(handle, "BlackLevelEnable", false);                      //关闭黑白电平
    MV_CC_SetIntValueEx(handle, "Brightness", 90);
    MV_CC_SetIntValueEx(handle, "AutoExposureTimeLowerLimit", 30);
    MV_CC_SetIntValueEx(handle, "AutoExposureTimeUpperLimit", 11000);
    MV_CC_SetEnumValueByString(handle, "ExposureAuto", "Continuous");           //Off-手动  Continuous-自动
    MV_CC_SetFloatValue(handle, "ExposureTime", 11111.0000);                    //曝光设置 1000/90约11ms曝光
    MV_CC_SetEnumValueByString(handle, "GainAuto", "Off");                      //Off-手动  Continuous-自动
    MV_CC_SetFloatValue(handle, "Gain", 0.0000);                                //增益0-12 
    //注册抓图回调
    MV_CC_RegisterImageCallBackEx(handle, [](unsigned char* pData, MV_FRAME_OUT_INFO_EX * pFrameInfo, void* pUser) {
        if (NULL == pFrameInfo || NULL == pData)
        {
            printf("ImageCallBackEx Input Param invalid.\n");
            return;
        }
        //相机内存数据存队列
        stImageNode* sImage = new stImageNode();
        sImage->enPixelType = pFrameInfo->enPixelType;
        sImage->nFrameLen = pFrameInfo->nFrameLenEx;
        sImage->nWidth = pFrameInfo->nWidth;
        sImage->nHeight = pFrameInfo->nHeight;
        sImage->nFrameNum = pFrameInfo->nFrameNum + 1;
        sImage->pData = (unsigned char*)malloc(sImage->nHeight * sImage->nWidth * 3);
        memcpy(sImage->pData, pData, sImage->nFrameLen);
        //通知存图
        m_list.push(sImage);
        m_sem.post();
    }, NULL);
    printf("app init ok      *********************\n");

    auto cutstart = [&] {
        //先关线程
        m_stated.store(false);
        if (m_thread.joinable())
            m_thread.join();
        //开始取流API
        MV_CC_StartGrabbing(handle);
        m_stated.store(true);
        //下面建议起多个比如6个线程写会快很多
        m_thread = std::move(std::thread([&]()
            {
                printf("WorkThread Begin . \r\n");
                while (m_stated)
                {
                    m_sem.wait();
                    stImageNode* sImage = NULL;
                    if (!m_list.pop(sImage))
                    {
                        printf("Poll failed, maybe no data. \r\n");
                        std::this_thread::sleep_for(std::chrono::seconds(2));
                        continue;
                    }
                    else
                    {
                        //根据实际场景需求,对图像进行 处理   
#if 1
                        cv::Mat cvImage(sImage->nHeight, sImage->nWidth, CV_8UC3, cv::Scalar(0, 0, 0));
                        //BayerGB8转RGB格式 不过建议用下面方法 SDK的MV_CC_ConvertPixelType做可以节约CPU
                        Bayer8ToRgb24((unsigned char*)(sImage->pData), sImage->nWidth, sImage->nHeight, cvImage.data, cvImage.step * sImage->nHeight);
                        //下面写jpg存图
                        char szFileName[256] = { 0 };
                        sprintf(szFileName, "img/Image_%d_%d_%d.jpg", sImage->nWidth, sImage->nHeight, sImage->nFrameNum);
                        printf("write >>> %s\n", szFileName);
                        cv::imwrite(szFileName, cvImage);   //cv::imshow("cvImage", cvImage);  //cv::waitKey();
#endif
#if 0
                        //SDK库BayerGB8转RGB格式
                        uint8_t* pBgrData = NULL;
                        cv::Mat cvImage;
                        Convert2Mat(handle, *sImage, &pBgrData);
                        cvImage = cv::Mat(sImage->nHeight, sImage->nWidth, CV_8UC3, pBgrData);
                        //下面写jpg存图
                        char szFileName[256] = { 0 };
                        sprintf(szFileName, "img/Image_%d_%d_%d.jpg", Image.nWidth, Image.nHeight, Image.nFrameNum);
                        printf("write >>> %s\n", szFileName);
                        cv::imwrite(szFileName, cvImage);
                        //删除 Convert2Mat里面拷贝的数据
                        if (pBgrData != NULL)
                        {
                            free(pBgrData);
                            pBgrData = NULL;
                        }
#endif
#if 0
                        //原始raw格式
                        FILE* fp = NULL;
                        char szFileName[256] = { 0 };
                        sprintf(szFileName, "img/Image_%d_%d_%d.raw", sImage->nWidth, sImage->nHeight, sImage->nFrameNum);
                        printf("write %s\n", szFileName);
                        fp = fopen(szFileName, "wb+");
                        if (fp == NULL)
                        {
                            //return MV_E_RESOURCE;
                        }
                        fwrite(sImage->pData, 1, sImage->nFrameLen, fp);
                        fclose(fp);
                        fp = NULL;
#endif
                        cvImage.release();
                        //删除释放数据
                        if (sImage->pData)
                        {
                            free(sImage->pData);
                            sImage->pData = NULL;
                        }
                        delete sImage;
                    }
                }
                printf("WorkThread exit . \r\n");
            }));
        };

    auto cutend = [&] {
        MV_CC_StopGrabbing(handle);                         //停止取流
        MV_CC_RegisterImageCallBackEx(handle, NULL, NULL);  //注销抓图回调
        printf("**************************************\n");
        printf("cut end*******************************\n");
        printf("**************************************\n");
        printf("\n\n\n");
        };

    cutstart();
    std::this_thread::sleep_for(std::chrono::seconds(5));
    cutend();

    while (1)
    {
        char c = getchar();             //按键会阻塞
        if ('q' == c)                   //退出程序
            break;
        else if ('b' == c)
        {
            cutstart();
        }
        else if ('e' == c)
        {
            cutend();
        }
        else if ('c' == c)
        {
            m_sem.post();
            m_stated.store(false);
            if (m_thread.joinable())
                m_thread.join();
            MV_CC_CloseDevice(handle);                          //关闭设备
            MV_CC_DestroyHandle(handle);                        //销毁句柄
            MV_CC_Finalize();                                   //反初始化SDK 
        }
        else
        {
            //printf("**************************************\n");
        }
    }
    printf("app end ok       *********************\n");
    //scanf("\n");
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值