Mjpeg-streamer源码学习笔记-Main-动态库插件(三)

2023-11-30 12:18

本文主要是介绍Mjpeg-streamer源码学习笔记-Main-动态库插件(三),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目标文件:mjpg-stream/mjpg-stream.c + mjpg-stream.h + input.h + output.h

这一篇的主要难点是main()中的结构体globals引出的动态链接库,插件,条件变量,互斥锁等问题。

新手写,有不对的请大神指正,鼓励。

 

本人参考文章:

http://www.360doc.com/content/13/0913/13/13876325_314174121.shtml

http://www.cnblogs.com/ardar/articles/357321.html

一:结构体介绍

 

globals

------------------------------------------------------------------------------

// global variables that are accessed by all plugins
typedef struct _globals globals;

struct _globals {
    int stop;  // 一个全局标志位 
    
    input in[MAX_INPUT_PLUGINS];  //输入插件,一个输入插件可对应多个输出插件 宏定义为10

    int incnt;//输入插件数量
    output out[MAX_OUTPUT_PLUGINS]; //输出插件,以数组形式表示 宏定义为10
    int outcnt;//输出插件数量
 
    //int (*control)(int command, char *details);
};

------------------------------------------------------------------------------

input

------------------------------------------------------------------------------

//structure to store variables/functions for input plugin
typedef struct _input input;

struct _input {
    char *plugin;  //动态链接库的名字,或者是动态链接库的地址
    void *handle;  //动态链接库的句柄,通过该句柄可以调用动态库中的函数

    input_parameter param;  //输入插件的参数

    // input plugin parameters
    struct _control *in_parameters;// control结构
    int parametercount;//参数计数


    struct v4l2_jpegcompression jpegcomp;//关于v4l2的所有结构体在后面详述

   
    pthread_mutex_t db;        //互斥锁,数据锁
    pthread_cond_t  db_update;      //条件变量,数据更新的标志  

   
    unsigned char *buf;       //全局JPG帧的缓冲区的指针
    int size;     //缓冲区的大小

     
    struct timeval timestamp;//定时结构

    input_format *in_formats;//输入格式
    int formatCount;//格式计数
    int currentFormat; // holds the current format number 保存当前格式数量

    int (*init)(input_parameter *, int id);  //四个函数指针
    int (*stop)(int);
    int (*run)(int);
    int (*cmd)(int plugin, unsigned int control_id, unsigned int group, int value);
 
};

------------------------------------------------------------------------------

output

------------------------------------------------------------------------------

//structure to store variables/functions for output plugin
typedef struct _output output;
struct _output {
    char *plugin; //动态链接库的名字,或者是动态链接库的地址
    void *handle; //动态链接库的句柄,通过该句柄可以调用动态库中的函数
    output_parameter param;//输出插件的参数

    // input plugin parameters
    struct _control *out_parameters; //control结构
    int parametercount; //参数计数

    int (*init)(output_parameter *param, int id);  //四个函数指针
    int (*stop)(int);
    int (*run)(int);
    int (*cmd)(int plugin, unsigned int control_id, unsigned int group, int value);
};

------------------------------------------------------------------------------

input_parameter

------------------------------------------------------------------------------

//parameters for input plugin
typedef struct _input_parameter input_parameter;
struct _input_parameter {
    int id;  //用于标记是哪一个输入插件的参数
    char *parameters; //输入参数指针
    int argc;
    char *argv[MAX_PLUGIN_ARGUMENTS];//插件参数数组
    struct _globals *global;//全局结构指针
};

------------------------------------------------------------------------------

output_parameter

------------------------------------------------------------------------------

//parameters for output plugin
typedef struct _output_parameter output_parameter;
struct _output_parameter {
    int id;  //用于标记是哪一个输出插件的参数
    char *parameters;//输出参数指针
    int argc;
    char *argv[MAX_PLUGIN_ARGUMENTS];//插件参数数组
    struct _globals *global;//全局结构指针
};

------------------------------------------------------------------------------

input_format

------------------------------------------------------------------------------

typedef struct _input_format input_format;
struct _input_format {
    struct v4l2_fmtdesc format;//V4L2 ioctl 中的重要结构体
    input_resolution *supportedResolutions; //分辨率指针
    int resolutionCount;//分辨率计数
    char currentResolution;//当前分辨率
};

------------------------------------------------------------------------------

input_resolution

------------------------------------------------------------------------------

typedef struct _input_resolution input_resolution;
struct _input_resolution {
    unsigned int width;//分辨率宽度
    unsigned int height;//分辨率高度
};

------------------------------------------------------------------------------

control

------------------------------------------------------------------------------

typedef struct _control control;
struct _control {
    struct v4l2_queryctrl ctrl;//v4l2 结构
    int value; 
    struct v4l2_querymenu *menuitems;// v4l2结构   
    int class_id; 
    int group;
};

------------------------------------------------------------------------------

 

二:源码分析

------------------------------------------------------------------------------

铺垫代码

------------------------------------------------------------------------------
    if(global.outcnt == 0) {
      
        global.outcnt = 1;
    }

这段代码确保了至少有一个输出插件选中

------------------------------------------------------------------------------

 打开输入插件代码

------------------------------------------------------------------------------
    for(i = 0; i < global.incnt; i++) { 
        //同步全局图像缓冲区:
        if(pthread_mutex_init(&global.in[i].db, NULL) != 0) {
            LOG("could not initialize mutex variable\n");
            closelog();
            exit(EXIT_FAILURE);
        }
        if(pthread_cond_init(&global.in[i].db_update, NULL) != 0) {
            LOG("could not initialize condition variable\n");
            closelog();
            exit(EXIT_FAILURE);
        }

        //查找字符串s中首次出现字符‘ ’的位置

        //保存输入插件字节数

        tmp = (size_t)(strchr(input[i], ' ') - input[i]);  

 

        //初始化global全局变量
        global.in[i].stop      = 0;
        global.in[i].buf       = NULL;
        global.in[i].size      = 0;

 

        //在命令中获得动态库
        global.in[i].plugin = (tmp > 0) ? strndup(input[i], tmp) : strdup(input[i]);

        //打开动态链接库
        global.in[i].handle = dlopen(global.in[i].plugin, RTLD_LAZY);
        if(!global.in[i].handle) {
            LOG("ERROR: could not find input plugin\n");
            LOG("       Perhaps you want to adjust the search path with:\n");
            LOG("       # export LD_LIBRARY_PATH=/path/to/plugin/folder\n");
            LOG("       dlopen: %s\n", dlerror());
            closelog();
            exit(EXIT_FAILURE);
        }

        //获得动态库内的input_init()函数
        global.in[i].init = dlsym(global.in[i].handle, "input_init");
        if(global.in[i].init == NULL) {
            LOG("%s\n", dlerror());
            exit(EXIT_FAILURE);
        }

        //获得动态库内的input_stop()函数
        global.in[i].stop = dlsym(global.in[i].handle, "input_stop");
        if(global.in[i].stop == NULL) {
            LOG("%s\n", dlerror());
            exit(EXIT_FAILURE);
        }

        //获得动态库内的input_run()函数
        global.in[i].run = dlsym(global.in[i].handle, "input_run");
        if(global.in[i].run == NULL) {
            LOG("%s\n", dlerror());
            exit(EXIT_FAILURE);
        }
        //试图找到可选的命令         

        //获得动态库内的input_cmd()函数  
        global.in[i].cmd = dlsym(global.in[i].handle, "input_cmd");

        //将命令参数的起始地址赋给para.parameter

        global.in[i].param.parameters = strchr(input[i], ' ');
        split_parameters(global.in[i].param.parameters, &global.in[i].param.argc, global.in[i].param.argv);

        //将global结构体的地址赋给param.global
        global.in[i].param.global = &global;
        global.in[i].param.id = i;

        //传递global.in.param给init,进行初始化

        if(global.in[i].init(&global.in[i].param, i)) {
            LOG("input_init() return value signals to exit\n");
            closelog();
            exit(0);
        }
    }

------------------------------------------------------------------------------
打开输出插件代码

------------------------------------------------------------------------------
    for(i = 0; i < global.outcnt; i++) { //因为是一个输入对应多个输出,所以输出采用了for循环
        tmp = (size_t)(strchr(output[i], ' ') - output[i]);

        //在命令中获得动态库
        global.out[i].plugin = (tmp > 0) ? strndup(output[i], tmp) : strdup(output[i]);

        //打开动态链接库
        global.out[i].handle = dlopen(global.out[i].plugin, RTLD_LAZY);
        if(!global.out[i].handle) {
            LOG("ERROR: could not find output plugin %s\n", global.out[i].plugin);
            LOG("       Perhaps you want to adjust the search path with:\n");
            LOG("       # export LD_LIBRARY_PATH=/path/to/plugin/folder\n");
            LOG("       dlopen: %s\n", dlerror());
            closelog();
            exit(EXIT_FAILURE);
        }

        //获得动态库内的output_init()函数
        global.out[i].init = dlsym(global.out[i].handle, "output_init");
        if(global.out[i].init == NULL) {
            LOG("%s\n", dlerror());
            exit(EXIT_FAILURE);
        }

        //获得动态库内的output_stop()函数
        global.out[i].stop = dlsym(global.out[i].handle, "output_stop");
        if(global.out[i].stop == NULL) {
            LOG("%s\n", dlerror());
            exit(EXIT_FAILURE);
        }

        //获得动态库内的output_run()函数
        global.out[i].run = dlsym(global.out[i].handle, "output_run");
        if(global.out[i].run == NULL) {
            LOG("%s\n", dlerror());
            exit(EXIT_FAILURE);
        }

        //获得动态库内的output_cmd()函数
        global.out[i].cmd = dlsym(global.out[i].handle, "output_cmd");

        将命令参数的起始地址赋给para.parameter

        global.out[i].param.parameters = strchr(output[i], ' ');       
        split_parameters(global.out[i].param.parameters, &global.out[i].param.argc, global.out[i].param.argv);

        //将global结构体的地址赋给param.global

        global.out[i].param.global = &global;
        global.out[i].param.id = i;

        //传递global.out.param给init,进行初始化
        if(global.out[i].init(&global.out[i].param, i)) {
            LOG("output_init() return value signals to exit\n");
            closelog();
            exit(0);
        }
    }

------------------------------------------------------------------------------

运行插件代码直到Main()结束

------------------------------------------------------------------------------

//start to read the input, push pictures into global buffer 

//开始读取输入,把照片放到全局缓冲区
    DBG("starting %d input plugin\n", global.incnt);
    for(i = 0; i < global.incnt; i++) {
        syslog(LOG_INFO, "starting input plugin %s", global.in[i].plugin);

        //开始运行输入插件的run()函数
        if(global.in[i].run(i)) {
            LOG("can not run input plugin %d: %s\n", i, global.in[i].plugin);
            closelog();
            return 1;
        }
    }

    DBG("starting %d output plugin(s)\n", global.outcnt);

    //开始运行输出插件的run()函数
    for(i = 0; i < global.outcnt; i++) {
        syslog(LOG_INFO, "starting output plugin: %s (ID: d)", global.out[i].plugin, global.out[i].param.id);
        global.out[i].run(global.out[i].param.id);
    }

    //wait for signals

    //运行完以上函数,该进程进入休眠状态,等待用户按下<CTRL>+C结束所有的进程
    pause();

 

    return 0;

------------------------------------------------------------------------------

 

至此,main函数的代码已经贴完。但具体还有很多细节没分析,下面逐个分析。

 

三:动态链接库

在上面加粗的很多函数中,见到了dlopen,dlerror等函数。下面就动态链接库内容分析

 

3.1 动态链接库的操作函数

 #include<dlfcn.h>

~1 void *dlopen(const char *filename, int flag);

flag表示在什么时候解决未定义的符号(调用)。取值有两个:

1) RTLD_LAZY:表明在动态链接库的函数代码执行时解决。

2) RTLD_NOW : 表明在dlopen返回前就解决所有未定义的符号,一旦未解决,dlopen将返回错误。

dlopen调用失败时,将返回NULL值,否则返回的是操作句柄。


~2 const char *dlerror(void);   

当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

 

~3 void *dlsym(void *handle, const char *symbol);

 

~4 int dlclose(void *handle);  

dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。

 

注意:Link with -ldl.   

 

3.2 Linux动态链接库简介

 

大家都知道,在WINDOWS系统中有很多的动态链接库(以.DLL为后缀的文件,DLL即Dynamic Link Library)。这种动态链接库,和静态函数库不同,它里面的函数并不是执行程序本身的一部分,而是根据执行程序需要按需装入,同时其执行代码可在多个执行程序间共享,节省了空间,提高了效率,具备很高的灵活性,得到越来越多程序员和用户的青睐

 

在linux下动态链接库,在/lib目录下,就有许多以.so作后缀的文件,,这就是LINUX系统应用的动态链接库,只不过与WINDOWS叫法不同,它叫so,即Shared Object,共享对象。

在LINUX下,静态函数库是以.a作后缀的。

 

3.3动态链接库程序示例


#include"stdio.h"
#include"dlfcn.h"
#define SOFILE "./my.so"

#define SHARED
#include"datetime.h"

main()
{
DATETYPE d;
TIMETYPE t;
void *dp;
char *error;

puts("动态链接库应用示范");
dp=dlopen(SOFILE,RTLD_LAZY);
if (dp==NULL)
{
fputs(dlerror(),stderr);
exit(1);
}
getdate=dlsym(dp,"getdate");
error=dlerror();
if (error)
{
 fputs(error,stderr);
 exit(1);
}
 getdate(&d);
 printf("当前日期: d-d-d\n",d.year,d.mon,d.day);
 gettime=dlsym(dp,"gettime");

 error=dlerror();
 if (error)
{
 fputs(error,stderr);
 exit(1);

}

 gettime(&t);

 printf("当前时间: d:d:d\n",t.hour,t.min,t.sec);

 dlclose(dp);

 exit(0);

}


3.4 小结

LINUX创建与使用动态链接库并不是一件难事。

 

编译函数源程序时选用-shared选项即可创建动态链接库,注意应以.so后缀命名,最好放到公用库目录(如/lib,/usr/lib等)下面,并要写好用户接口文件,以便其它用户共享。

 

使用动态链接库,源程序中要包含dlfcn.h头文件,写程序时注意dlopen等函数的正确调用,编译时要采用-rdynamic选项与-ldl选项,以产生可调用动态链接库的执行代码。

 

四:字符串操作

在上面加粗的很多函数中,见到了strchr,strdup,strndup等函数。下面就字符串操作内容进行简单介绍

 

----------------------------------------------------------------------------------------------

#include<string.h>

char *strchr(const char *s, int c); 

char *strrchr(const char *s, int c); 

char *strdup(const char *s);  

char *strndup(const char *s, size_t n); 

char *strdupa(const char *s);  

char *strndupa(const char *s, size_t n);

----------------------------------------------------------------------------------------------

 

五:条件变量和互斥锁

在上面加粗的很多函数中,见到了pthread_mutex_init,pthread_cond_init等函数。

这里涉及到很重要的线程问题。

将在下一篇(四)详细讲述线程和线程控制,并举出大量实例来分析。

所以这里一笔带过

 

六:源码中的两个函数

 

6.1 signal_handler

--------------------------------------------------------

void signal_handler(int sig)
{
    int i;

    // signal "stop" to threads
    LOG("setting signal to stop\n");
    global.stop = 1;
    usleep(1000 * 1000);

    // clean up threads
    LOG("force cancellation of threads and cleanup resources\n");
    for(i = 0; i < global.incnt; i++) {

    //运行输入插件的stop函数
        global.in[i].stop(i);
    }

    for(i = 0; i < global.outcnt; i++) {

    //运行输出插件的stop函数
        global.out[i].stop(global.out[i].param.id);

    //销毁条件变量

        pthread_cond_destroy(&global.in[i].db_update);

    //销毁互斥锁
        pthread_mutex_destroy(&global.in[i].db);
    }
    usleep(1000 * 1000);

    // close handles of input plugins
    for(i = 0; i < global.incnt; i++) {
        dlclose(global.in[i].handle);
    }

    for(i = 0; i < global.outcnt; i++) {     
        dlclose(global.out[i].handle);
    }
    DBG("all plugin handles closed\n");

    LOG("done\n");

    closelog();
    exit(0);
    return;
}

--------------------------------------------------------

6.1.1 usleep函数

--------------------------------------------------------

usleep功能把进程挂起一段时间, 单位是微秒(千分之一毫秒);
头文件: <unistd.h>
语法: void usleep(int micro_seconds);
返回值: 无
内容说明:本函数可暂时使程序停止执行。参数 micro_seconds 为要暂停的微秒数(us)。

--------------------------------------------------------

6.1.2 线程函数pthread_cond_destroy,pthread_mutex_destroy

--------------------------------------------------------

和线程有关的,一笔带过,后面详细讲。

--------------------------------------------------------

6.1.3 函数功能总结

--------------------------------------------------------

我们来看看这个函数到底干了什么

最重要的几步:

~1 设置global.stop=1

~2 挂起进程 usleep

~3 逐个分别运行输入插件和输出插件的函数stop

~4 逐个分别销毁互斥锁和条件变量pthread_cond_destroy,pthread_mutex_destroy

~5 挂起进程 usleep

~6 逐个分别关闭输入插件和输出插件的动态链接库

~7 关闭系统日志 closelog

--------------------------------------------------------

 

6.2 split_parameters

代码中:

split_parameters(global.in[i].param.parameters, &global.in[i].param.argc, global.in[i].param.argv);

split_parameters(global.out[i].param.parameters, &global.out[i].param.argc, global.out[i].param.argv);

--------------------------------------------------------

int split_parameters(char *parameter_string, int *argc, char **argv)
{
    int count = 1;
    argv[0] = NULL; // the plugin may set it to 'INPUT_PLUGIN_NAME'

    //参数名非0,参数长度非0
    if(parameter_string != NULL && strlen(parameter_string) != 0) {

    //新建3个变量
        char *arg = NULL, *saveptr = NULL, *token = NULL;

    //arg用来存储传入的地址

        arg = strdup(parameter_string);

        if(strchr(arg, ' ') != NULL) {

            //分割空格 并存入指针saveptr中,再传给token
            token = strtok_r(arg, " ", &saveptr);
            if(token != NULL) {
                argv[count] = strdup(token);
                count++;
                while((token = strtok_r(NULL, " ", &saveptr)) != NULL) {
                    argv[count] = strdup(token);
                    count++;
                    if(count >= MAX_PLUGIN_ARGUMENTS) { //最大为32
                        IPRINT("ERROR: too many arguments to input plugin\n");
                        return 0;
                    }
                }
            }
        }
    }
    *argc = count;
    return 1;
}

--------------------------------------------------------

6.2.1 strtok函数

--------------------------------------------------------
char *strtok(char s[], const char *delim);

分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。

从s开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。
所有delim中包含的字符都会被滤掉,并将被滤掉的地方设为一处分割的节点。

strtok是一个线程不安全的函数,因为它使用了静态分配的空间来存储被分割的字符串位置

线程安全的函数叫strtok_r

例如:strtok("abc,def,ghi",","),最后可以分割成为abc def ghi.尤其在点分十进制的IP中提取应用较多。

--------------------------------------------------------

 

6.2.2 函数小结

名字就是分割参数,利用strtok函数做到这一点。

到最后,是通过while,把所有的传入参数:

1,先给parameter_string

2,通过strdup复制,传给arg

3,通过strtok分割,传给argv[x]数组

4,直到count到32,代表所有的输入输出插件的参数全部分割存储完毕。

 

6.3 pause函数

--------------------------------------------------------

功能:让进程暂停直到信号出现

相关函数:kill,signal,sleep

表头文件: #include<unistd.h>
定义函数: int pause(void);
函数说明: pause()会令目前的进程暂停(进入睡眠状态),直到被信号(signal)所中断。
返回值: 只返回-1。

--------------------------------------------------------

 

至此,mjpg-streamer.c + mjpg-streamer.h + input.h + output.h + utils.c + utils.h 全部内容已经分析完了。

下一篇,我们将会抛开源码,着重讲线程,线程控制,多线程编程的应用以解释源码中互斥锁和条件变量的运用。

转自:http://blog.csdn.net/s419101357/article/details/11827809

这篇关于Mjpeg-streamer源码学习笔记-Main-动态库插件(三)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/436997

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学