9.1 图片的分割处理(c++)

        本文的图片处理分为图片分割、图像的亚像素坐标处理。亚像素处理的原理可以看论文一种基于多项式插值改进的亚像素细分算法,该论文的详解及c++的代码实现可以看博文基于多项式插值的亚像素边缘定位算法_基于多项式插值的亚像素算法-CSDN博客。下面的内容很多来自以上博文的内容,我只是对部分代码做了稍微的修改。

一. 背景

        在测量或者定位的应用中会涉及到边缘检测, 但是像 OpenCV 这样的开源库中边缘检测算法的精度在像素级别, 比如 Canny, Sobel blablabla. 要想提高精度就需要用到 亚像素 的技术, 在不改变硬件成本的前提下提高检测精度。

二、代码实现(龟速版)

1.梯度幅值

梯度检测包含幅值和方向,用8个方向模板在检测图像上进行卷积运算(模板滑过的地方的像素值和模板中对应的值相乘, 最后全部加总), 得到 8 张梯度图像, 方向模板如下. 模板的方向就是和 X 轴的夹角。

以下代码生成 8 个方向模板

#include<opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include<list>
#include<vector>
#include <typeinfo>


using namespace cv;
using namespace std;


#define KERNEL_SUM 8

Mat kernels[KERNEL_SUM];

int k = 0;
kernels[k++] = (Mat_<float>(3, 3) <<  1,  2,  1,  0,  0,  0, -1, -2, -1);	// 270°
kernels[k++] = (Mat_<float>(3, 3) <<  2,  1,  0,  1,  0, -1,  0, -1, -2);	// 315°
kernels[k++] = (Mat_<float>(3, 3) <<  1,  0, -1,  2,  0, -2,  1,  0, -1);	// 0°
kernels[k++] = (Mat_<float>(3, 3) <<  0, -1, -2,  1,  0, -1,  2,  1,  0);	// 45°

flip(kernels[0], kernels[k++], 0);											// 90°

kernels[k++] = (Mat_<float>(3, 3) << -2, -1,  0, -1,  0,  1,  0,  1,  2);	// 135°

flip(kernels[2], kernels[k++], 1);											// 180°

kernels[k++] = (Mat_<float>(3, 3) <<  0,  1,  2, -1,  0,  1, -2, -1,  0);	// 225°

        接下来利用模板卷积生成梯度图像, 假设要检测的图像长下面这个样, 边缘是模糊的

        利用 filter2D 函数计算梯度

// 梯度图像
Mat gradients[KERNEL_SUM];
// 检测图像, 路径自己更改, 注意要是单通道图像
Mat imgsrc = imread("E:\\vs\\daima\\opencvs\\OpencvPractice\\1.jpg");//IMREAD_ANYCOLOR

for (int k = 0; k < KERNEL_SUM; k++)
{
    filter2D(imgsrc, gradients[k], CV_16S, kernels[k]);// CV_64F    //

    Mat imgshowt;
    normalize(gradients[k], imgshowt, 0, 255, NORM_MINMAX);
    cv::Mat rlt;
    imgshowt.convertTo(rlt, CV_8UC1);//将imgshowt转换为无符号单通道的整型并赋值给rlt  CV_8UC1
    cvtColor(rlt, gradients[k], cv::COLOR_BGR2GRAY);//将rlt转换为灰度图并赋值给gradients[k]
    
    imshow("img", rlt);
    waitKey(0);
}

        注意,这里和前面博文的代码稍有不同,因为原图像是3通道的图像(我在前面博文截取了个圆的图片用来处理),在以上代码中用filter2D函数后将处理的图片修改为了单通道图片,并且显示了8个卷积核处理的不同图片,结果如下(截取了前博文的图片,但效果相同):

        从图中可以看出各模板可以检测出指定方向的边缘, 其中白色表示该方向最大梯度值, 黑色表示反向最大梯度值。

梯度幅值最大值处的点就是边缘的整数坐标。

2. 梯度方向

        梯度方向指向梯度幅值最大的方向, 现在已经有 8 张幅值图像, 只要比较其中最大的幅值即可得到其方向, 例如第 0 张梯度图像在 (i, j) 处的幅值比其他图像都大, 则 (i, j) 点的梯度方向是 270°. 下面我们用代码求出最大幅度值和方向。

//2. 梯度方向
  // (1. 角度列表
const short angle_list[] = { 270, 315, 0, 45, 90, 135, 180, 225 };
// (2. 总幅值矩阵
Mat amplitude(imgsrc.rows, imgsrc.cols, CV_8UC1, Scalar::all(0));

// (3. 角度矩阵, 后面初始化成 -64 只是为了归一化之后能显示角度 0
Mat angle(imgsrc.rows, imgsrc.cols, CV_16SC1, Scalar::all(-64));//CV_16SC1   -64

for (int r = 0; r < imgsrc.rows; r++)
{

    short* pAng = angle.ptr<short>(r);

    for (int c = 0; c < imgsrc.cols; c++)
    {
        // 找出最大值
        for (int i = 0; i < KERNEL_SUM; i++)
        {
            if (amplitude.ptr<unsigned char>(r)[c] < gradients[i].ptr<unsigned char>(r)[c])
            {
                amplitude.ptr<unsigned char>(r)[c] = gradients[i].ptr<unsigned char>(r)[c];
                pAng[c] = angle_list[i];
            }
        }
        
    }
}


Mat imgshow;

imshow("amplitude", amplitude);
imwrite("D:\Datas\\1.jpg",amplitude);
waitKey(0);

normalize(angle, imgshow, 0, 255, NORM_MINMAX);
imgshow.convertTo(imgshow, CV_8UC1);
imshow("amplitude", imgshow);
waitKey(0);

        完成之后显示幅值和角度图像如下:

 

3. 单像素边缘

        以上的方式处理的图像边缘是比较粗的,那么我们如何将边缘的粗细提取成单像素呢,就是找最亮的像素点作为边缘。这个也比较简单, 只要在 3 × 3 的邻域中根据梯度的方向比较中心点 “左右” 的两个点幅值就可以知道了. 左右我打了引号, 有可能是在对角方向和上下方向. 如果不能理解的话, 把幅值图放大如下, 仿佛看到了马赛克

        我们发现在梯度方向幅值从小到大, 再变小, 我们只需要判断中间是否大于两边(“左右”)的幅值,如果同时大于两边,则是边缘点, 如果不是同时大于两边, 则不是边缘点, 下面用代码实现此判断。

 // 3.单像素边缘,整数坐标边缘图像
 //cout << "===============> start 单像素边缘 <================" << endl;
 Mat edge(imgsrc.rows, imgsrc.cols, CV_8UC1, Scalar::all(0));

 for (int r = 1; r < imgsrc.rows - 1; r++)
 {
     // 3 * 3 邻域, 所以用 3 个指针, 一个指针指一行
     const unsigned char* pAmp1 = amplitude.ptr<unsigned char>(r - 1);
     const unsigned char* pAmp2 = amplitude.ptr<unsigned char>(r);
     const unsigned char* pAmp3 = amplitude.ptr<unsigned char>(r + 1);

     const short* pAng = angle.ptr<short>(r);
     unsigned char* pEdge = edge.ptr<unsigned char>(r);

     for (int c = 1; c < imgsrc.cols - 1; c++)
     {
         switch (pAng[c])
         {
         case 270:
             if (pAmp2[c] > pAmp1[c] && pAmp2[c] >= pAmp3[c])
             {
                 pEdge[c] = 255;
             }
             break;
         case 90:
             if (pAmp2[c] >= pAmp1[c] && pAmp2[c] > pAmp3[c])
             {
                 pEdge[c] = 255;
             }
             break;

         case 315:
             if (pAmp2[c] > pAmp1[c - 1] && pAmp2[c] >= pAmp3[c + 1])
             {
                 pEdge[c] = 255;
             }
             break;
         case 135:
             if (pAmp2[c] >= pAmp1[c - 1] && pAmp2[c] > pAmp3[c + 1])
             {
                 pEdge[c] = 255;
             }
             break;

         case 0:
             if (pAmp2[c] > pAmp2[c - 1] && pAmp2[c] >= pAmp2[c + 1])
             {
                 pEdge[c] = 255;
             }
             break;
         case 180:
             if (pAmp2[c] >= pAmp2[c - 1] && pAmp2[c] > pAmp2[c + 1])
             {
                 pEdge[c] = 255;
             }
             break;

         case 45:
             if (pAmp2[c] >= pAmp1[c + 1] && pAmp2[c] > pAmp3[c - 1])
             {
                 pEdge[c] = 255;
             }
             break;
         case 225:
             if (pAmp2[c] > pAmp1[c + 1] && pAmp2[c] >= pAmp3[c - 1])
             {
                 pEdge[c] = 255;
             }
             break;

         default:
             break;
         }
     }
 }
 imshow("edg", edge);//总共有462个点为255(白色)
 waitKey(0);

 //cout << "===============> end 单像素边缘 <================" << endl;

        该代码得到的图片显示结果如下:

        现在的边缘是不是只有一个像素宽了, 到这里可以算是完成了 50% 工作了, 还有两个问题需要解决, 一是如何求出亚像素坐标, 二是怎样表示亚像素坐标

4. 亚像素坐标

        根据以下公式可计算亚像素坐标, 其实这个公式是二次多项式拟合(剪切前面博文的内容)

        下面代码中, sin 前面的负号非常关键, 因为图像的 y 方向和直角坐标系的 y 方向相反

// 4. 亚像素坐标
cout << "===============> start 亚像素坐标 <================" << endl;

// 根号2
const double root2 = sqrt(2.0);
// 三角函数表
double tri_list[2][KERNEL_SUM] = { 0 };

for (int i = 0; i < KERNEL_SUM; i++)
{
    tri_list[0][i] = cos(angle_list[i] * CV_PI / 180.0);
    // sin前面的负号非常关键, 因为图像的y方向和直角坐标系的y方向相反
    tri_list[1][i] = -sin(angle_list[i] * CV_PI / 180.0);
    
}

// vector 方式记录小数坐标
vector<Point3f> vPts;
// Mat 方式记录小数坐标, 注意这里是双通道
Mat coordinate(imgsrc.rows, imgsrc.cols, CV_32FC2, Scalar::all(0));

for (int r = 1; r < imgsrc.rows - 1; r++)
{
    // 3 * 3 邻域, 所以用3个指针, 一个指针指一行
    const short* pAmp1 = amplitude.ptr<short>(r - 1);
    const short* pAmp2 = amplitude.ptr<short>(r);
    const short* pAmp3 = amplitude.ptr<short>(r + 1);

    const short* pAng = angle.ptr<short>(r);
    const short* pEdge = edge.ptr<short>(r);

    float* pCoordinate = coordinate.ptr<float>(r);

    for (int c = 1; c < imgsrc.cols - 1; c++)
    {
        if (pEdge[c])
        {
            int nAngTmp = 0;
            double dTmp = 0;

            switch (pAng[c])
            {
            case 270:
                nAngTmp = 0;
                dTmp = ((double)pAmp1[c] - pAmp3[c]) / (pAmp1[c] + pAmp3[c] - 2 * pAmp2[c] + 0.000001) * 0.5;
                break;

            case 90:
                nAngTmp = 4;
                dTmp = -((double)pAmp1[c] - pAmp3[c]) / (pAmp1[c] + pAmp3[c] - 2 * pAmp2[c] + 0.000001) * 0.5;
                break;

            case 315:
                nAngTmp = 1;
                dTmp = ((double)pAmp1[c - 1] - pAmp3[c + 1]) / (pAmp1[c - 1] + pAmp3[c + 1] - 2 * pAmp2[c] + 0.000001) * root2 * 0.5;
                break;

            case 135:
                nAngTmp = 5;
                dTmp = -((double)pAmp1[c - 1] - pAmp3[c + 1]) / (pAmp1[c - 1] + pAmp3[c + 1] - 2 * pAmp2[c] + 0.000001) * root2 * 0.5;
                break;

            case 0:
                nAngTmp = 2;
                dTmp = ((double)pAmp2[c - 1] - pAmp2[c + 1]) / (pAmp2[c - 1] + pAmp2[c + 1] - 2 * pAmp2[c] + 0.000001) * 0.5;
                break;

            case 180:
                nAngTmp = 6;
                dTmp = -((double)pAmp2[c - 1] - pAmp2[c + 1]) / (pAmp2[c - 1] + pAmp2[c + 1] - 2 * pAmp2[c] + 0.000001) * 0.5;
                break;

            case 45:
                nAngTmp = 3;
                dTmp = ((double)pAmp3[c - 1] - pAmp1[c + 1]) / (pAmp1[c + 1] + pAmp3[c - 1] - 2 * pAmp2[c] + 0.000001) * root2 * 0.5;
                break;

            case 225:
                nAngTmp = 7;
                dTmp = -((double)pAmp3[c - 1] - pAmp1[c + 1]) / (pAmp1[c + 1] + pAmp3[c - 1] - 2 * pAmp2[c] + 0.000001) * root2 * 0.5;
                break;

            default:
                break;
            }

            const double x = c + dTmp * tri_list[0][nAngTmp];
            const double y = r + dTmp * tri_list[1][nAngTmp];
            const short z = angle_list[nAngTmp];
            //cout << dTmp<< "    "<< tri_list[0][nAngTmp]<<"   "<< dTmp * tri_list[0][nAngTmp] << endl;

            // vector方式
            vPts.push_back(Point3f((float)x, (float)y, (short)z));

            // Mat 方式
            pCoordinate[c << 1] = (float)x;
            pCoordinate[(c << 1) + 1] = (float)y;
        }
    }
}

//cout << "" << vPts.size() << endl;//总共有462个点为255(白色)
//for (size_t i = 0; i < vPts.size(); i++)
//{
//    cout << vPts[i].z << ":    " << vPts[i].x << ",    " << vPts[i].y <<endl;
//}

cout << "===============> end 亚像素坐标 <================" << endl;
return 0;

        注意,以上代码大家要了解为什么不同角度下加减不同,了解这个很重要。

三、龟速测试

至此, 龟速版本的代码已经完成了, 找一张其他图像试试? 找 lena 来试试。

        看到边缘图像有的同学可能要伤心了, 女神怎么变成这样了, 那么多边缘被检测出来了, 我们不需要那么多边缘啊. 同学别急, 检测出来那么多边缘是因为我们没有对梯度幅度进行筛选, 你想一下, 我们在计算单像素边缘的时候只要满足中间大于两边就算边缘, 女神图像中有一些中间只比两边大了一点点, 所以这种边缘可以去除,  我们想要的是比较强烈的边缘,解决办法就是设定一个阈值, 当梯度值大于阈值是才算真正的边缘。

将单像素边缘检测修改如下:

// 3.单像素边缘,整数坐标边缘图像
//cout << "===============> start 单像素边缘 <================" << endl;

// 阈值
double thres = 128;	// 此处为增加

Mat edge(imgsrc.rows, imgsrc.cols, CV_8UC1, Scalar::all(0));

for (int r = 1; r < imgsrc.rows - 1; r++)
{
    // 3 * 3 邻域, 所以用 3 个指针, 一个指针指一行
    const unsigned char* pAmp1 = amplitude.ptr<unsigned char>(r - 1);
    const unsigned char* pAmp2 = amplitude.ptr<unsigned char>(r);
    const unsigned char* pAmp3 = amplitude.ptr<unsigned char>(r + 1);

    const short* pAng = angle.ptr<short>(r);
    unsigned char* pEdge = edge.ptr<unsigned char>(r);

    for (int c = 1; c < imgsrc.cols - 1; c++)
    {
        
        // 以下判断为增加部分
		if (pAmp2[c] < thres)
		{
			continue;
		}
		//

        switch (pAng[c])
        {
        case 270:
            if (pAmp2[c] > pAmp1[c] && pAmp2[c] >= pAmp3[c])
            {
                pEdge[c] = 255;
            }
            break;
        case 90:
            if (pAmp2[c] >= pAmp1[c] && pAmp2[c] > pAmp3[c])
            {
                pEdge[c] = 255;
            }
            break;

        case 315:
            if (pAmp2[c] > pAmp1[c - 1] && pAmp2[c] >= pAmp3[c + 1])
            {
                pEdge[c] = 255;
            }
            break;
        case 135:
            if (pAmp2[c] >= pAmp1[c - 1] && pAmp2[c] > pAmp3[c + 1])
            {
                pEdge[c] = 255;
            }
            break;

        case 0:
            if (pAmp2[c] > pAmp2[c - 1] && pAmp2[c] >= pAmp2[c + 1])
            {
                pEdge[c] = 255;
            }
            break;
        case 180:
            if (pAmp2[c] >= pAmp2[c - 1] && pAmp2[c] > pAmp2[c + 1])
            {
                pEdge[c] = 255;
            }
            break;

        case 45:
            if (pAmp2[c] >= pAmp1[c + 1] && pAmp2[c] > pAmp3[c - 1])
            {
                pEdge[c] = 255;
            }
            break;
        case 225:
            if (pAmp2[c] > pAmp1[c + 1] && pAmp2[c] >= pAmp3[c - 1])
            {
                pEdge[c] = 255;
            }
            break;

        default:
            break;
        }
    }
}
imshow("edg", edge);//总共有462个点为255(白色)
imwrite("D:\Datas\\2.jpg", edge);
waitKey(0);

//cout << "===============> end 单像素边缘 <================" << endl;

内容没完,快速版的后续继续更新!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/710211.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

简单Mesh多线程合并,使用什么库性能更高

1&#xff09;简单Mesh多线程合并&#xff0c;使用什么库性能更高 2&#xff09;Unity Semaphore.WaitForSignal耗时高 3&#xff09;VS编辑的C#代码注释的中文部分乱码 4&#xff09;变量IntPtr m_cachePtr切换线程后变空 这是第389篇UWA技术知识分享的推送&#xff0c;精选了…

Stability AI最新的SD3模型存在严重问题 为规避裸体结果导致躯体部分错乱

人工智能 Stability AI 最新的 SD3 Medium 模型存在严重问题&#xff0c;只要生成人物就会出现躯体错乱&#xff0c;这似乎是该公司刻意规避生成裸体图片的结果。目前猜测他们可能在训练过程中就剔除了 NSFW 内容&#xff0c;同时在训练时规避裸体内容进而导致模型也会刻意将人…

03 Tricks

一&#xff1a;Auto-ML的一般形式 还可以支持这个CV啦lp啦&#xff0c;还有多模态啦&#xff0c;都还有很多很多任务啊&#xff0c;都可以支持啊 Auto-Sklearn Auto-Pytorch 结构搜所&#xff1a;神经网络搜所算法&#xff1a; AutoGluon 02 >自动特征工程 Tsfresh Boru…

ATF是如何完成双系统切换的?

ATF&#xff08;Arm Trusted Firmware&#xff09;是一个用于ARM架构处理器的可信固件&#xff0c;它最初提供的最主要的功能就是&#xff1a;双系统切换和电源管理。 那么如何进行双系统切换呢&#xff0c;在双系统切换的示例中&#xff0c;除了CPU的跳转&#xff0c;例如CPU…

用Rust手把手编写一个Proxy(代理), 开始动工

https://shop.kongfz.com/795263/ 代理端和代理服务端之间可用自有格式来实现多路复用以减少连接的建立断开的开销,目前暂未实现代理服务端。 类结构 proxy.rs 负责代理结构的存储,监听类型,监听地址,是否有父级地址,认证账号密码等。 flag.rs 监听类型的二进制结构,…

2024 年最新 Python 基于百度智能云实现短语音识别、语音合成详细教程

百度智能云语音识别 采用国际领先的流式端到端语音语言一体化建模算法&#xff0c;将语音快速准确识别为文字&#xff0c;支持手机应用语音交互、语音内容分析、机器人对话等场景。百度短语音识别可以将 60 秒以下的音频识别为文字。适用于语音对话、语音控制、语音输入等场景…

金融行业的等保测评要求

在金融行业中&#xff0c;网络安全问题非常普遍&#xff0c;如恶意攻击、病毒感染、数据泄露等。这些问题可能会导致金融机构的信息系统瘫痪&#xff0c;造成巨大的经济损失&#xff0c;甚至影响国家金融稳定。因此&#xff0c;金融机构应该高度重视网络安全问题&#xff0c;采…

微软的反面:错过了AI时代最大机遇的亚马逊

内容提要 一度被亚马逊给予厚望的Alexa为何被后来者ChatGPT吊打&#xff0c;错失争霸AI世界的机会&#xff1f;这里面不仅有技术的锅。 文章正文 在AI浪潮席卷全球之际&#xff0c;科技巨头们无不争先恐后&#xff0c;力图抢占先机。然而&#xff0c;就在微软借助ChatGPT一举…

彻底理解 C 语言的数组在内存中到底是怎么存放的!

在C语言中&#xff0c;数组是经常被用到的重要数据类型&#xff0c;但在实际使用时&#xff0c;往往有很多工程师会出现各种各样的问题&#xff0c;如内存越界、错误的访问、初始化不当等。这其中有很大一个原因是没有彻底理解数组的存储机制&#xff0c;出现了一些非法地址或者…

Pytorch解决 多元回归 问题的算法

Pytorch解决 多元回归 问题的算法 回归是一种基本的统计建模技术&#xff0c;用于建立因变量与一个或多个自变量之间的关系。 我们将使用 PyTorch&#xff08;一种流行的深度学习框架&#xff09;来开发和训练线性回归模型。 二元回归的简单示例 训练数据集&#xff08;可获取&…

微信群发机器人.使用指南.

0.简介 1.介绍 微信群发机器人是用来群发微信消息的工具,通过控制电脑的键盘和鼠标操作微信app来实现群发.支持的消息类型有:文字,图片,视频,文件,小程序,位置等. 群发机器人也可以将微信联系人中的信息保存到电脑csv表格中,以供分析. 因其是通过模拟用户操作鼠标键盘来实现群…

如何在 Vue 3 中使用 vue3-print-nb 实现灵活的前端打印

你好&#xff0c;我是小白Coding日志&#xff0c;一个热爱技术的程序员。在这里&#xff0c;我分享自己在编程和技术世界中的学习心得和体会。希望我的文章能够给你带来一些灵感和帮助。欢迎来到我的博客&#xff0c;一起在技术的世界里探索前行吧&#xff01; 前言 在前端开…

go interface

package mainimport "fmt"// 接口 interface func main() {c : Chinese{} //创建一个中国人实例u : American{} //创建一个美国人实例greet(c) //中国人打招呼greet(u) //美国人打招呼 }// 接收具备SayHello接口能力的变量 func greet(s SayHello) {…

KOL营销策略:危机公关中的品牌修复与形象重塑

在当今数字化时代&#xff0c;品牌声誉的管理和维护愈发重要。危机公关作为品牌管理的重要一环&#xff0c;对于企业的长期生存和发展具有至关重要的影响。而KOL作为具有强大影响力和号召力的个体&#xff0c;在危机公关中扮演着不可或缺的角色。本文Nox聚星将和大家探讨KOL在危…

yolo案例项目学习记录

box-ocr: 监控摄像头视频流实时计数传送带的货物&#xff0c;并提取货物上面文字或二维码 1.本地环境&#xff1a; 1.1torch、torchvison、torchaudio版本对应关系 PyTorch中torch、torchvision、torchaudio、torchtext版本对应关系_torch2.0.1对应的torchvision-CSDN博客 1…

Leetcode419. 甲板上的战舰

Every day a Leetcode 题目来源&#xff1a;419. 甲板上的战舰 解法1&#xff1a;一次遍历 战舰的个数&#xff0c;等于战舰「头部」的个数。 具体来说&#xff0c;如果位于 (i, j) 的格子是战舰的头部&#xff0c;那么左边和上边的相邻格子不能是 X。 代码&#xff1a; …

php收银系统源码推荐

智慧新零售系统是一套线下线上一体化的收银系统。致力于给零售门店提供『多样化线下收银』、『ERP进销存』、『o2o小程序商城』、『精细化会员管理』、『丰富营销插件』等一体化行业解决方案&#xff01; 一、多样化线下收银 1.聚合收款码 ①适用商户&#xff1a;小微门店&am…

Vue40-vueComponent构造函数

一、组件的本质&#xff1a;VueComponent构造函数 组件的本质是&#xff1a;构造函数 二、每一次调用vue.extend&#xff0c;返回的事一个全新的 VueComponent VueComponent的源码如下&#xff1a; 三、组件中的this 组件中的this是VueComponent实例对象&#xff0c;结构和vm…

想学编程,什么语言最好上手?

Python是许多初学者的首选&#xff0c;因为它的语法简洁易懂&#xff0c;而且有丰富的资源和社区支持。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频 讲解&#xff0c;项目实战。如果你渴望学习编程&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;…

「C系列」C 结构体

文章目录 一、C 结构体1. 定义结构体2. 声明结构体变量3. 初始化结构体变量4. 访问结构体成员5. 结构体数组6. 结构体指针7. 结构体嵌套 二、C 如何使用结构体1. 定义结构体类型2. 声明结构体变量3. 初始化结构体变量4. 访问结构体成员5. 结构体指针6. 在函数中使用结构体7. 注…