本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本项目展示了如何利用Microsoft Foundation Classes (MFC) 和 OpenCV库在Windows平台实现图像显示应用。学习本项目,你可以深入了解MFC在Windows应用程序开发中的使用,掌握OpenCV进行图像处理的核心技术,以及如何在C++环境下将两者结合。项目包括CImage与Mat数据结构转换的实践,环境配置的详细步骤,以及图像在MFC应用程序中如何被显示。通过这个项目,开发者将获得在Windows平台上开发计算机视觉应用的实际经验。
MFCretry1.rar

1. MFC基础与应用

1.1 MFC概述与历史地位

MFC(Microsoft Foundation Classes)是微软推出的一套C++类库,它为开发者提供了快速访问Windows API的途径,简化了基于Windows平台的桌面应用程序开发。自1992年首次推出以来,MFC一直是Windows软件开发的重要工具之一,尤其在早期的Windows应用程序开发中扮演了核心角色。MFC封装了大量常用的界面元素和功能模块,使得开发者不必直接操作底层API,从而提高了开发效率和程序的可维护性。

1.2 MFC程序的基本框架

一个典型的MFC程序包括以下几个基本组成部分:

  • 应用程序类(CWinApp派生类):负责管理应用程序的启动和终止。
  • 窗口类(CFrameWnd或CDialog派生类):定义应用程序的用户界面,包括主窗口和对话框。
  • 文档类(CDocument派生类):管理与应用程序相关联的数据。
  • 视图类(CView派生类):提供用户数据的可视化展示。

了解这些基本框架元素是深入MFC编程的第一步。下面通过一个简单的MFC程序示例,展示如何构建一个基本的应用程序框架。

2. OpenCV基础与图像处理

2.1 OpenCV的基本概念与安装

2.1.1 OpenCV库的引入与基本框架

OpenCV,即开源计算机视觉库,是一个由英特尔公司支持的开源计算机视觉和机器学习软件库。它是目前图像处理、计算机视觉领域最流行的开源库之一。由于其高度的模块化、效率、以及对多种编程语言的支持,包括C++、Python、Java等,使得OpenCV成为教育和科研中不可或缺的工具。

安装OpenCV时,用户可以按照以下步骤进行:

  1. 首先需要确保你的系统已经安装了CMake和相应的编译器,如GCC或MSVC。
  2. 下载对应操作系统的OpenCV版本的压缩包,并解压。
  3. 打开命令行工具,进入到解压后的OpenCV目录。
  4. 创建一个用于编译的目录,例如,可以在命令行中输入 mkdir build
  5. 进入到新建的build目录,运行CMake工具指定源代码位置和编译目标目录,例如 cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..
  6. 使用命令行编译源代码,例如,在Linux环境下使用 make -jX (X为你CPU的核心数),在Windows环境下使用生成的Visual Studio解决方案文件。
  7. 最后,使用命令 make install (Linux)或安装Visual Studio解决方案(Windows)来完成安装。
2.1.2 图像处理中的基本操作

OpenCV中的图像通常使用Mat类来表示,它是一个二维矩阵,包含行、列以及每个像素点的数据。图像的基本操作包括加载、显示、保存、以及像素级的访问和修改等。例如,以下是一些简单的操作代码示例:

#include <opencv2/opencv.hpp>

int main() {
    // 加载图像
    cv::Mat image = cv::imread("path_to_image.jpg", cv::IMREAD_COLOR);
    // 检查图像是否正确加载
    if (image.empty()) {
        std::cerr << "Error loading image file" << std::endl;
        return -1;
    }
    // 显示图像
    cv::imshow("Image", image);
    // 等待用户按键继续
    cv::waitKey(0);
    // 保存图像
    cv::imwrite("path_to_save.jpg", image);

    return 0;
}

在此代码块中,首先包含了opencv库,并在 main() 函数中完成了图像的加载、显示和保存。 cv::imread 函数用于加载图像, cv::imshow 用于显示图像,而 cv::imwrite 则用于保存图像。需要注意的是,在保存图像时,路径应确保正确,并且图像格式应被支持。此外, cv::waitKey 函数常用于等待用户按键来控制图像显示的时间。

2.2 OpenCV中的图像处理技术

2.2.1 常用图像处理算法

OpenCV提供了许多图像处理算法来执行从基础到复杂的图像操作。以下是一些常用的图像处理算法及其应用:

  • 灰度转换:将彩色图像转换为灰度图,这是一种常见的预处理步骤。
  • 边缘检测:通过算法如Canny边缘检测器,可以找出图像中的边缘。
  • 图像滤波:用于去除图像噪声,如均值滤波、高斯滤波等。
  • 图像形态学操作:改变图像结构,包括腐蚀、膨胀、开运算、闭运算等。

以上处理算法可以组合使用来满足不同场景下的图像处理需求。例如,一个典型的图像处理流程可能会包括读取图像、灰度转换、滤波、边缘检测等步骤,最终达到增强图像质量或提取特定特征的目的。

2.2.2 特征提取与识别

特征提取与识别是计算机视觉中的重要分支,涉及到从图像中提取有用信息作为识别或分类的依据。常见的特征提取方法包括:

  • SIFT(尺度不变特征变换):用于检测并描述图像中的局部特征点。
  • SURF(加速稳健特征):在SIFT的基础上进行了优化,以提高速度。
  • ORB(Oriented FAST and Rotated BRIEF):是一种更快速的特征描述子。

特征提取之后,通常会结合特征匹配或使用机器学习的方法来识别图像中的对象。例如,在人脸识别中,可能会提取人脸图像的特征,并使用支持向量机(SVM)、深度学习等算法进行分类。

2.3 OpenCV在实际项目中的应用案例

2.3.1 实际案例介绍

在实际的项目应用中,OpenCV能够解决一系列的图像处理和计算机视觉问题。例如,可以用OpenCV开发人脸识别系统,包括人脸检测、特征点提取、以及人脸比对等多个步骤。另一个常见的应用场景是物体检测与跟踪,在视频监控系统中,物体检测可以帮助系统识别并跟踪视频中的移动目标,从而实现自动报警或数据记录等高级功能。

2.3.2 OpenCV应用的拓展与优化

尽管OpenCV提供了大量的功能模块和算法,但在实际应用中,我们仍需要对这些基础功能进行拓展和优化以满足特定的需求。如在性能优化方面,可以考虑图像预处理减少数据量、使用高效的特征提取算法、并行计算等策略。在功能拓展方面,可以结合深度学习框架,如TensorFlow或PyTorch,利用深度神经网络实现更复杂的图像识别和处理任务。

在本节内容中,我们介绍了OpenCV的安装、基础框架、图像处理操作、常用算法、以及在实际应用项目中的案例。OpenCV库的引入与基本框架为初学者提供了一个快速上手的途径,而图像处理中的基本操作则为进行更高级的图像处理任务奠定了基础。在介绍常用图像处理技术时,我们了解到了一系列实用的算法,这些算法是构建复杂图像处理系统的重要工具。特征提取与识别方面,我们认识到了OpenCV在计算机视觉领域的应用潜力,以及如何将这些功能拓展和优化以更好地适应现实世界的需求。

3. C++编程在MFC和OpenCV项目中的应用

随着软件开发技术的不断演进,C++作为一种高效稳定的编程语言,在MFC(Microsoft Foundation Classes)和OpenCV(Open Source Computer Vision Library)项目中的应用日益广泛。本章旨在通过回顾C++的基础语法,展示如何将其运用于MFC和OpenCV项目,并详细介绍在项目开发中的优化与调试技巧。

3.1 C++基础语法回顾与项目应用

3.1.1 面向对象编程的基本概念

面向对象编程(Object-Oriented Programming, OOP)是C++的核心特性之一。其基本概念包括类(Class)和对象(Object)、继承(Inheritance)、多态(Polymorphism)以及封装(Encapsulation)。C++提供了丰富的语法支持这些OOP特性,使得代码更加模块化、易于维护和扩展。

以一个简单的例子来说明这些概念的应用:

// 定义一个基类 Animal
class Animal {
public:
    void makeSound() const { std::cout << "Some sound" << std::endl; }
};

// Dog 类继承自 Animal 类
class Dog : public Animal {
public:
    void makeSound() const override { std::cout << "Bark" << std::endl; }
};

// Cat 类也继承自 Animal 类
class Cat : public Animal {
public:
    void makeSound() const override { std::cout << "Meow" << std::endl; }
};

int main() {
    Dog dog;
    Cat cat;

    dog.makeSound(); // 输出:Bark
    cat.makeSound(); // 输出:Meow
    return 0;
}

3.1.2 C++在MFC项目中的应用

在MFC项目中,C++的OOP特性可以极大地简化和优化UI组件的管理。开发者可以定义各种窗口类,继承自MFC库中的CWnd类,并重写相关方法以实现特定的UI行为。

// 自定义窗口类
class MyWindow : public CWnd {
public:
    // 重写消息处理函数
    afx_msg void OnPaint() {
        CPaintDC dc(this); // 设备上下文用于绘制
        // 在此处添加消息处理程序代码
        // ...
    }
};

// 在 MFC 应用程序中注册窗口类
BEGIN_MESSAGE_MAP(MyWindow, CWnd)
    ON_WM_PAINT()
END_MESSAGE_MAP()

在此例中, MyWindow 类重写了 OnPaint 方法,这个方法会在窗口需要重绘的时候被调用。 BEGIN_MESSAGE_MAP END_MESSAGE_MAP 宏定义了消息映射,使得窗口能够响应系统消息。

3.2 C++与OpenCV结合编程技巧

3.2.1 C++在OpenCV项目中的高级应用

在OpenCV项目中,C++不仅可以处理数据结构的构建与操作,还能实现高效的算法设计。使用C++模板和STL(Standard Template Library)可以编写出更加通用和高效的代码。

例如,下面的代码展示了如何使用C++模板函数和STL容器结合OpenCV进行图像的迭代处理:

#include <vector>
#include <opencv2/opencv.hpp>

// 模板函数,遍历图像矩阵中的每一个像素
template<typename T>
void processImage(cv::Mat& image) {
    for(int y = 0; y < image.rows; y++) {
        for(int x = 0; x < image.cols; x++) {
            cv::Vec<T, 3>& pixel = image.at<cv::Vec<T, 3>>(y, x);
            // 对像素值进行处理
            pixel[0] = 255 - pixel[0]; // 示例:反转颜色通道
        }
    }
}

int main() {
    cv::Mat image = cv::imread("path_to_image.jpg", cv::IMREAD_COLOR);
    if (image.empty()) {
        std::cerr << "Error: Could not open the image file!" << std::endl;
        return -1;
    }
    // 根据图像的类型调用不同的处理函数
    if (image.type() == CV_8UC3) {
        processImage<uchar>(image);
    } else if (image.type() == CV_32FC3) {
        processImage<float>(image);
    }
    // 显示处理后的图像
    cv::imshow("Processed Image", image);
    cv::waitKey(0);
    return 0;
}

在这个模板函数中,我们迭代了图像的每一个像素,并对其进行了处理。这种抽象的方式不仅增加了代码的可复用性,而且也使代码更加易于维护。

3.2.2 性能优化与调试技巧

在实际项目中,性能优化和调试是保证代码质量和运行效率的重要环节。C++提供了多种手段,比如STL的 std::vector reserve 方法预分配内存,避免在运行时频繁的内存分配;使用 std::chrono 进行时间测量,帮助识别性能瓶颈;以及利用Visual Studio提供的调试工具来跟踪程序执行和内存使用情况。

在性能优化方面,对于算法优化,可以考虑使用OpenCV的IPP(Intel Performance Primitives)库,或者编写SIMD(Single Instruction, Multiple Data)指令集优化代码以提高处理速度。对于大规模数据处理,合理使用内存池和缓存策略也是关键。

#include <chrono>
#include <vector>

int main() {
    // 测试代码执行时间
    auto start = std::chrono::high_resolution_clock::now();
    std::vector<int> numbers(1000000);
    // 执行一些计算任务...
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> diff = end - start;
    std::cout << "Time taken: " << diff.count() << "s" << std::endl;
    return 0;
}

以上代码通过记录时间点,来测量执行代码段的时间,从而分析性能。

对于调试技巧,建议编写清晰、结构化的代码,并且在关键路径添加日志输出,以方便跟踪程序运行状态。当遇到问题时,可以使用断点、单步执行、监视表达式等调试功能来定位问题。熟练使用这些调试技巧,有助于高效地找到并解决问题。

以上内容为第三章:C++编程在MFC和OpenCV项目中的应用。在这一章中,我们回顾了C++的基础语法,并展示了如何将这些语法应用于MFC和OpenCV项目。同时,我们也探讨了一些高级编程技巧、性能优化方法和调试技巧,帮助开发者在实际项目中更高效地使用C++。

4. CImage与Mat数据结构的转换方法

在现代的图像处理和计算机视觉应用中,OpenCV库因其强大的功能和高效的性能而广泛使用。MFC(Microsoft Foundation Classes)是一个C++库,用于创建Windows应用程序。有时,开发者需要在MFC应用程序中使用OpenCV处理图像,这就涉及到CImage(MFC中的图像类)与Mat(OpenCV中的矩阵类)之间的转换。

4.1 CImage与Mat的基本概念

4.1.1 CImage的定义与操作

CImage是MFC库中用于图像处理的一个类,它提供了许多处理图像的功能,如创建、加载、保存图像以及对图像进行基本的像素操作。CImage是MFC中用于操作位图的类,它封装了位图句柄(HBITMAP),提供了更为方便的操作接口。

4.1.2 Mat的定义与操作

OpenCV中的Mat类是用于存储图像和矩阵数据的容器。它比传统的C/C++数组更加灵活,包含了图像的尺寸、类型、和一个指向图像数据的指针。Mat还包含了数据的引用计数,可以用来进行高效的内存管理。

4.2 CImage与Mat数据结构的转换实践

4.2.1 转换方法与应用场景

在开发中,CImage和Mat之间的转换是非常常见的需求。比如,当用户从MFC界面中加载了一张图片,如果想要用OpenCV进行进一步处理,那么就需要将CImage转换为Mat。同样,如果处理完的图像需要在MFC界面中显示,那么就需要从Mat转换回CImage。

转换的大致步骤是:

  1. 从CImage对象中提取出HBITMAP句柄。
  2. 创建一个临时的CBitmap对象,并将HBITMAP句柄与之关联。
  3. 使用CBitmap的GetBitmapBits()函数将位图数据复制到一个字节数组中。
  4. 根据CImage的宽度、高度和颜色通道数,将字节数组数据填充到一个uchar指针指向的内存区域中。
  5. 使用填充好的uchar指针,构建一个OpenCV的Mat对象。

4.2.2 转换过程中的常见问题及解决策略

在实际转换过程中,可能会遇到以下问题:

  • 图像颜色通道不匹配:CImage默认为RGB格式,而OpenCV为BGR格式。在转换时需要调整通道顺序。
  • 位图颜色深度不匹配:在转换时要注意32位、24位、16位等不同颜色深度的处理。
  • 转换效率:每次转换都涉及到数据的复制,这可能会消耗较多的计算资源。可以通过重用内存区域、批量处理等方法来优化转换过程。

下面是一个CImage与Mat转换的代码示例,及其解析:

#include <opencv2/opencv.hpp>
#include <atlimage.h>

// 将MFC CImage转换为OpenCV Mat对象
cv::Mat CImageToMat(const CImage& srcImage) {
    // 预分配一个Mat对象,初始化为3通道的8位无符号整型数组
    cv::Mat dstMat(srcImage.GetHeight(), srcImage.GetWidth(), CV_8UC3);
    // 将CImage中的数据复制到Mat对象中
    for (int y = 0; y < srcImage.GetHeight(); ++y) {
        for (int x = 0; x < srcImage.GetWidth(); ++x) {
            COLORREF color = srcImage.GetPixel(x, y);
            dstMat.at<cv::Vec3b>(y, x)[2] = (uchar)GetBValue(color); // 蓝色通道
            dstMat.at<cv::Vec3b>(y, x)[1] = (uchar)GetGValue(color); // 绿色通道
            dstMat.at<cv::Vec3b>(y, x)[0] = (uchar)GetRValue(color); // 红色通道
        }
    }
    return dstMat;
}

// 将OpenCV Mat对象转换为MFC CImage对象
void MatToCImage(const cv::Mat& srcMat, CImage& dstImage) {
    // 创建一个临时的CBitmap对象
    CBitmap bitmap;
    BITMAPINFOHEADER bmi = { sizeof(BITMAPINFOHEADER) };
    bmi.biWidth = srcMat.cols;
    bmi.biHeight = -srcMat.rows;
    bmi.biPlanes = 1;
    bmi.biBitCount = 24;
    bmi.biCompression = BI_RGB;
    bitmap.CreateBitmapIndirect(&bmi);
    bitmap.MoveToDevCenterPoint();
    // 将Mat对象的像素数据复制到CBitmap中
    for (int y = 0; y < srcMat.rows; ++y) {
        for (int x = 0; x < srcMat.cols; ++x) {
            cv::Vec3b pixel = srcMat.at<cv::Vec3b>(y, x);
            COLORREF color = RGB(pixel[2], pixel[1], pixel[0]);
            bitmap.SetPixel(x, y, color);
        }
    }
    // 将位图数据从CBitmap复制到CImage中
    dstImage.Attach(bitmap.Detach());
}

在上述代码中,我们定义了两个函数 CImageToMat MatToCImage ,用于实现两种方向的转换。 CImageToMat 函数在转换过程中,考虑到了CImage和OpenCV中颜色通道的差异,并进行了正确的转换。 MatToCImage 函数则通过创建一个临时的CBitmap对象来完成转换,并利用MFC的Bitmap类方法来将像素数据填充到CImage中。

以上代码段展示了基本的转换过程,但在实际应用中,可能需要根据图像的具体属性(如颜色深度、像素格式等)进行相应的调整和优化。此外,在处理大型图像或需要高性能的场景中,还可以考虑使用更高效的内存操作技术和算法来优化转换过程。

5. Visual Studio 2017环境配置与MFC项目开发

5.1 Visual Studio 2017环境的配置

5.1.1 开发环境的安装与配置

在开始MFC项目开发之前,安装和配置合适的开发环境是至关重要的一步。Visual Studio 2017是微软推出的集成开发环境,它为开发者提供了丰富的开发工具和功能。为了在Visual Studio 2017中顺利进行MFC项目开发,以下是安装和配置环境的详细步骤:

  1. 访问Visual Studio官方网站下载Visual Studio 2017安装程序。
  2. 运行安装程序后,选择“Custom”安装模式以便进行自定义配置。
  3. 在安装向导中,选择“Desktop development with C++”工作负载。
  4. 除了默认包含的组件外,还需要添加MFC桌面特性的支持。这可以在“Individual components”选项中找到“C++ MFC for latest v141 build tools (x86 & x64)”进行勾选安装。
  5. 完成以上步骤后,点击安装继续。

5.1.2 MFC与OpenCV插件的配置

配置MFC开发环境后,为了让Visual Studio支持OpenCV库,还需要进行相应的插件配置:

  1. 确保你已下载并安装了OpenCV库。这通常包括下载OpenCV的预编译二进制文件并将其解压到指定目录。
  2. 在Visual Studio安装向导的“Individual components”部分中,确保勾选了“C++ OpenCV”组件。
  3. 打开Visual Studio,进入“Tools” -> “Options” -> “Projects and Solutions” -> “VC++ Directories”。在“Include Directories”中添加OpenCV的包含路径(例如, C:\opencv\build\include ),在“Library Directories”中添加OpenCV的库路径(例如, C:\opencv\build\x64\vc14\lib )。
  4. 同时,在项目属性中,需要设置链接器输入为OpenCV相关库。在“Linker” -> “Input” -> “Additional Dependencies”中添加OpenCV的 .lib 文件名(不包括 .lib 后缀)。

这些配置完成后,你的Visual Studio 2017环境就准备好开发基于MFC和OpenCV的项目了。

5.2 MFC项目创建与调试的步骤

5.2.1 创建MFC项目

创建MFC项目的过程相对直观,可以按照以下步骤进行:

  1. 打开Visual Studio 2017。
  2. 选择“File” -> “New” -> “Project…”。
  3. 在“New Project”对话框中,选择“Visual C++”类别下的“MFC Application”。
  4. 输入项目名称,并指定保存位置。
  5. 在创建向导中,选择MFC应用程序的类型。可以是基于对话框的、单文档或多文档界面。
  6. 点击“Finish”创建项目。

5.2.2 项目调试与问题排查

创建项目后,调试是确保程序按预期运行的重要环节。以下是进行MFC项目调试的基本步骤:

  1. 打开项目后,在代码编辑器中设置断点。可以通过点击行号左侧区域或按F9键。
  2. 启动调试会话。可以通过点击工具栏的“Debug”按钮或按F5键。
  3. Visual Studio将编译项目并在断点处暂停执行。此时可以在“Locals”窗口中查看和修改变量值,或使用“Immediate”窗口执行即时命令。
  4. 使用“Step Into”(F11)、“Step Over”(F10)和“Step Out”(Shift+F11)等调试命令逐步执行代码。
  5. 在调试过程中,如果遇到异常,可以检查“Output”窗口中“Debug”选项卡下的输出信息来定位问题。

5.3 图像在MFC应用程序中的显示技术

5.3.1 GDI+与Direct2D技术介绍

MFC应用程序中图像显示可以通过多种技术实现,其中最为常见的包括GDI+和Direct2D:

  1. GDI+ :GDI+是Windows操作系统提供的图形设备接口,是基于GDI(图形设备接口)的改进版本。它提供了一组丰富的API,可以用于2D矢量图形、文本、以及图像的处理。在MFC项目中,可以使用GDI+库进行图像的绘制和显示。
  2. Direct2D :Direct2D是微软提供的一个高性能的2D图形API。相比GDI+,Direct2D提供了更好的图形渲染性能和更丰富的图形特性。Direct2D特别适合用于需要高质量图形渲染的应用程序。

5.3.2 图像显示的高级应用与优化

在MFC项目中实现图像的显示并进行优化,可以按照以下步骤:

  1. 初始化GDI+ :在MFC应用程序中,需要首先初始化GDI+。这可以通过在项目的入口函数中调用 GdiplusStartup 来完成。
  2. 加载和显示图像 :使用 CImage 类加载图像文件,然后将图像绘制到MFC窗口上。例如:
    cpp CImage img; img.Load(_T("path_to_image.jpg")); // 加载图像文件 CDC* pDC = GetDC(); // 获取设备上下文 img.Draw(pDC->m_hDC, 0, 0); // 在窗口中绘制图像 ReleaseDC(pDC); // 释放设备上下文
  3. 使用Direct2D显示图像 :要在MFC中使用Direct2D进行图像显示,需要创建一个与MFC窗口关联的 ID2D1HwndRenderTarget 。代码示例:
    ```cpp
    // 创建Direct2D设备和渲染目标
    ID2D1HwndRenderTarget* pRenderTarget = NULL;
    D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory);
    m_pD2DFactory->CreateHwndRenderTarget(
    D2D1::RenderTargetProperties(),
    D2D1::HwndRenderTargetProperties(m_hWnd, D2D1::SizeU(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN))),
    &pRenderTarget);

// 使用渲染目标绘制图像
pRenderTarget->DrawBitmap(pBitmap, D2D1::RectF(0, 0, pBitmap->GetPixelSize().width, pBitmap->GetPixelSize().height));
```

通过上述步骤,可以在MFC应用程序中实现高质量的图像显示,并且利用Direct2D带来的性能优势,优化渲染效率。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本项目展示了如何利用Microsoft Foundation Classes (MFC) 和 OpenCV库在Windows平台实现图像显示应用。学习本项目,你可以深入了解MFC在Windows应用程序开发中的使用,掌握OpenCV进行图像处理的核心技术,以及如何在C++环境下将两者结合。项目包括CImage与Mat数据结构转换的实践,环境配置的详细步骤,以及图像在MFC应用程序中如何被显示。通过这个项目,开发者将获得在Windows平台上开发计算机视觉应用的实际经验。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐