Android平台RTSP|RTMP播放器高效率如何回调YUV或RGB数据?

技术背景

我们在做Android平台RTSP、RTMP播放器的时候,经常遇到这样的技术诉求,开发者希望拿到播放器解码后的YUV或RGB数据,投递给视觉算法,做AI分析,本文以ffmpeg和大牛直播SDK的SmartPlayer为例,介绍下相关的技术实现。

FFmpeg

FFmpeg 是一个开源的跨平台多媒体处理工具库,广泛应用于音视频处理领域。

格式转换

  • 可以在众多不同的音频和视频格式之间进行转换。例如,将 MP4 格式转换为 AVI 格式,或者将 WAV 音频文件转换为 MP3 格式。
  • 支持几乎所有常见的音视频编码格式,如 H.264、H.265、AAC、MP3 等。

编码与解码

  • 能够对各种音视频编码格式进行解码,将压缩的音视频数据还原为原始的图像和音频信号。
  • 同时也可以进行编码操作,将原始的音视频数据压缩成特定的编码格式,以减小文件大小或满足特定的播放需求。
  1. 视频处理

    • 可以进行视频剪辑、拼接、裁剪等操作。比如,从一个长视频中截取特定的片段,或者将多个视频片段拼接成一个新的视频。
    • 支持视频的旋转、缩放、滤镜添加等特效处理。例如,将视频进行 90 度旋转,或者对视频应用模糊、锐化等滤镜效果。

音频处理

  • 可以进行音频的混音、提取、音量调整等操作。例如,将多个音频文件混合在一起,或者从视频中提取音频轨道。
  • 支持音频的均衡器调整、降噪等处理,以改善音频质量。

流媒体处理

  • 能够处理实时流媒体,如 RTMP、RTSP、HLS 等协议。可以进行流媒体的录制、转码、分发等操作。
  • 对于直播场景,FFmpeg 可以作为推流或拉流的工具,实现视频直播的采集、编码和传输。

集成 FFmpeg

  • 将 FFmpeg 库集成到 Android 项目中,可以通过使用 Android NDK 来编译和链接 FFmpeg 库。
  • 配置项目的build.gradle文件,添加 NDK 相关的配置,并创建一个 JNI 层的接口来调用 FFmpeg 的功能。

利用 FFmpeg 解码视频并获取 YUV 数据

  • 在 JNI 层的代码中,使用 FFmpeg 的解码功能来解码 RTSP/RTMP 视频流。FFmpeg 提供了丰富的 API 来处理各种多媒体格式。
  • 在解码过程中,可以获取解码后的视频帧,并将其转换为 YUV 格式的数据。然后通过 JNI 回调将 YUV 数据传递到 Java 层。
  • 以下是一个简单的 JNI 方法示例,用于解码视频并回调 YUV 数据:
 #include <jni.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <android/log.h>#include <libavcodec/avcodec.h>#include <libavformat/avformat.h>#include <libswscale/swscale.h>#define LOG_TAG "FFmpegNative"#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)AVFormatContext *pFormatCtx = NULL;AVCodecContext *pCodecCtx = NULL;AVCodec *pCodec = NULL;AVFrame *pFrame = NULL;AVPacket packet;struct SwsContext *img_convert_ctx = NULL;jobject yuvCallbackObj;jmethodID yuvCallbackMethod;void Java_com_example_myapp_MyNativeLib_init(JNIEnv *env, jobject obj, jstring url, jobject callbackObj, jmethodID callbackMethod) {const char *input_filename = (*env)->GetStringUTFChars(env, url, NULL);yuvCallbackObj = (*env)->NewGlobalRef(env, callbackObj);yuvCallbackMethod = callbackMethod;av_register_all();avformat_network_init();if (avformat_open_input(&pFormatCtx, input_filename, NULL, NULL)!= 0) {LOGE("Could not open input file.");return;}if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {LOGE("Could not find stream information.");return;}int videoStream = -1;for (int i = 0; i < pFormatCtx->nb_streams; i++) {if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {videoStream = i;break;}}if (videoStream == -1) {LOGE("Could not find video stream.");return;}pCodecCtx = avcodec_alloc_context3(NULL);if (!pCodecCtx) {LOGE("Could not allocate video codec context.");return;}avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar);pCodec = avcodec_find_decoder(pCodecCtx->codec_id);if (!pCodec) {LOGE("Could not find codec.");return;}if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {LOGE("Could not open codec.");return;}pFrame = av_frame_alloc();if (!pFrame) {LOGE("Could not allocate video frame.");return;}}void Java_com_example_myapp_MyNativeLib_decodeAndCallback(JNIEnv *env, jobject obj) {while (av_read_frame(pFormatCtx, &packet) >= 0) {if (packet.stream_index == videoStream) {int ret = avcodec_send_packet(pCodecCtx, &packet);if (ret < 0) {LOGE("Error sending a packet for decoding.");av_packet_unref(&packet);continue;}while (ret >= 0) {ret = avcodec_receive_frame(pCodecCtx, pFrame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;} else if (ret < 0) {LOGE("Error during decoding.");break;}// 将解码后的帧转换为 YUV 格式if (img_convert_ctx == NULL) {img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);if (!img_convert_ctx) {LOGE("Could not initialize the conversion context.");return;}}AVFrame *yuvFrame = av_frame_alloc();if (!yuvFrame) {LOGE("Could not allocate YUV frame.");return;}yuvFrame->format = AV_PIX_FMT_YUV420P;yuvFrame->width = pCodecCtx->width;yuvFrame->height = pCodecCtx->height;int bufferSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);uint8_t *buffer = (uint8_t *)av_malloc(bufferSize);av_image_fill_arrays(yuvFrame->data, yuvFrame->linesize, buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);sws_scale(img_convert_ctx, (const uint8_t *const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, yuvFrame->data, yuvFrame->linesize);// 回调 YUV 数据到 Java 层(*env)->CallVoidMethod(env, yuvCallbackObj, yuvCallbackMethod, buffer, bufferSize);av_free(buffer);av_frame_free(&yuvFrame);}}av_packet_unref(&packet);}}void Java_com_example_myapp_MyNativeLib_release(JNIEnv *env, jobject obj) {if (pFormatCtx) {avformat_close_input(&pFormatCtx);}if (pCodecCtx) {avcodec_close(pCodecCtx);avcodec_free_context(&pCodecCtx);}if (pFrame) {av_frame_free(&pFrame);}if (img_convert_ctx) {sws_freeContext(img_convert_ctx);}(*env)->DeleteGlobalRef(env, yuvCallbackObj);}

SmartPlayer

SmartPlayer是大牛直播SDK旗下全自研内核,行业内一致认可的跨平台RTSP、RTMP直播播放器SDK,功能齐全、高稳定、超低延迟,超低资源占用,适用于安防、教育、单兵指挥等行业。

功能设计如下:

  •  [支持播放协议]高稳定、超低延迟、业内首屈一指的RTSP直播播放器SDK;
  •  [多实例播放]支持多实例播放;
  •  [事件回调]支持网络状态、buffer状态等回调;
  •  [视频格式]支持H.265、H.264,此外,还支持RTSP MJPEG播放;
  •  [音频格式]支持AAC/PCMA/PCMU;
  •  [H.264/H.265软解码]支持H.264/H.265软解;
  •  [H.264硬解码]Windows/Android/iOS支持特定机型H.264硬解;
  •  [H.265硬解]Windows/Android/iOS支持特定机型H.265硬解;
  •  [H.264/H.265硬解码]Android支持设置Surface模式硬解和普通模式硬解码;
  •  [RTSP模式设置]支持RTSP TCP/UDP模式设置;
  •  [RTSP TCP/UDP自动切换]支持RTSP TCP、UDP模式自动切换;
  •  [RTSP超时设置]支持RTSP超时时间设置,单位:秒;
  •  [RTSP 401认证处理]支持上报RTSP 401事件,如URL携带鉴权信息,会自动处理;
  •  [缓冲时间设置]支持buffer time设置;
  •  [首屏秒开]支持首屏秒开模式;
  •  [复杂网络处理]支持断网重连等各种网络环境自动适配;
  •  [快速切换URL]支持播放过程中,快速切换其他URL,内容切换更快;
  •  [音视频多种render机制]Android平台,视频:surfaceview/OpenGL ES,音频:AudioTrack/OpenSL ES;
  •  [实时静音]支持播放过程中,实时静音/取消静音;
  •  [实时音量调节]支持播放过程中实时调节音量;
  •  [实时快照]支持播放过程中截取当前播放画面;
  •  [只播关键帧]Windows平台支持实时设置是否只播放关键帧;
  •  [渲染角度]支持0°,90°,180°和270°四个视频画面渲染角度设置;
  •  [渲染镜像]支持水平反转、垂直反转模式设置;
  •  [等比例缩放]支持图像等比例缩放绘制(Android设置surface模式硬解模式不支持);
  •  [实时下载速度更新]支持当前下载速度实时回调(支持设置回调时间间隔);
  •  [解码前视频数据回调]支持H.264/H.265数据回调;
  •  [解码后视频数据回调]支持解码后YUV/RGB数据回调;
  •  [解码前音频数据回调]支持AAC/PCMA/PCMU数据回调;
  •  [音视频自适应]支持播放过程中,音视频信息改变后自适应;
  •  [扩展录像功能]完美支持和录像SDK组合使用。

播放之前,设置YUV数据回调:

/** SmartPlayer.java* Copyright © 2014~2024 daniusdk.com All rights reserved.*/
private boolean StartPlay()
{if(isPlaying)return false;if(!isPulling){if (!OpenPullHandle())return false;}// 如果第二个参数设置为null,则播放纯音频libPlayer.SmartPlayerSetSurface(player_handle_, sSurfaceView);//libPlayer.SmartPlayerSetSurface(player_handle_, null);libPlayer.SmartPlayerSetRenderScaleMode(player_handle_, 1);if(video_opt_ == 3){libPlayer.SmartPlayerSetExternalRender(player_handle_, new I420ExternalRender(publisher_array_));}//libPlayer.SmartPlayerSetExternalAudioOutput(player_handle_, new PlayerExternalPCMOutput(stream_publisher_));libPlayer.SmartPlayerSetFastStartup(player_handle_, isFastStartup ? 1 : 0);libPlayer.SmartPlayerSetAudioOutputType(player_handle_, 1);if (isMute) {libPlayer.SmartPlayerSetMute(player_handle_, isMute ? 1	: 0);}if (isHardwareDecoder){int isSupportH264HwDecoder = libPlayer.SetSmartPlayerVideoHWDecoder(player_handle_, 1);int isSupportHevcHwDecoder = libPlayer.SetSmartPlayerVideoHevcHWDecoder(player_handle_, 1);Log.i(TAG, "isSupportH264HwDecoder: " + isSupportH264HwDecoder + ", isSupportHevcHwDecoder: " + isSupportHevcHwDecoder);}libPlayer.SmartPlayerSetLowLatencyMode(player_handle_, isLowLatency ? 1	: 0);libPlayer.SmartPlayerSetRotation(player_handle_, rotate_degrees);int iPlaybackRet = libPlayer.SmartPlayerStartPlay(player_handle_);if (iPlaybackRet != 0 && !isPulling) {Log.e(TAG, "StartPlay failed!");releasePlayerHandle();return false;}isPlaying = true;return true;
}

YUV数据回调上层实现:

private static class I420ExternalRender implements NTExternalRender {// public static final int NT_FRAME_FORMAT_RGBA = 1;// public static final int NT_FRAME_FORMAT_ABGR = 2;// public static final int NT_FRAME_FORMAT_I420 = 3;private WeakReference<LibPublisherWrapper> publisher_;private ArrayList<WeakReference<LibPublisherWrapper> > publisher_list_;private int width_;private int height_;private int y_row_bytes_;private int u_row_bytes_;private int v_row_bytes_;private ByteBuffer y_buffer_;private ByteBuffer u_buffer_;private ByteBuffer v_buffer_;public I420ExternalRender(LibPublisherWrapper publisher) {if (publisher != null)publisher_ = new WeakReference<>(publisher);}public I420ExternalRender(LibPublisherWrapper[] publisher_list) {if (publisher_list != null && publisher_list.length > 0) {for (LibPublisherWrapper i : publisher_list) {if (i != null) {if (null == publisher_list_)publisher_list_ = new ArrayList<>();publisher_list_.add(new WeakReference<>(i));}}}}private final List<LibPublisherWrapper> get_publisher_list() {if (null == publisher_list_ || publisher_list_.isEmpty())return null;ArrayList<LibPublisherWrapper> list = new ArrayList<>(publisher_list_.size());for (WeakReference<LibPublisherWrapper> i : publisher_list_) {if (i != null) {LibPublisherWrapper o = i.get();if (o != null && !o.empty())list.add(o);}}return list;}@Overridepublic int getNTFrameFormat() {Log.i(TAG, "I420ExternalRender::getNTFrameFormat return "+ NT_FRAME_FORMAT_I420);return NT_FRAME_FORMAT_I420;}private static int align(int d, int a) { return (d + (a - 1)) & ~(a - 1); }@Overridepublic void onNTFrameSizeChanged(int width, int height) {width_ = width;height_ = height;y_row_bytes_ = width;u_row_bytes_ = (width+1)/2;v_row_bytes_ = (width+1)/2;y_buffer_ = ByteBuffer.allocateDirect(y_row_bytes_*height_);u_buffer_ = ByteBuffer.allocateDirect(u_row_bytes_*((height_ + 1) / 2));v_buffer_ = ByteBuffer.allocateDirect(v_row_bytes_*((height_ + 1) / 2));}@Overridepublic ByteBuffer getNTPlaneByteBuffer(int index) {switch (index) {case 0:return y_buffer_;case 1:return u_buffer_;case 2:return v_buffer_;default:Log.e(TAG, "I420ExternalRender::getNTPlaneByteBuffer index error:" + index);return null;}}@Overridepublic int getNTPlanePerRowBytes(int index) {switch (index) {case 0:return y_row_bytes_;case 1:return u_row_bytes_;case 2:return v_row_bytes_;default:Log.e(TAG, "I420ExternalRender::getNTPlanePerRowBytes index error:" + index);return 0;}}public void onNTRenderFrame(int width, int height, long timestamp){if (null == y_buffer_ || null == u_buffer_ || null == v_buffer_)return;Log.i(TAG, "I420ExternalRender::onNTRenderFrame " + width + "*" + height + ", t:" + timestamp);y_buffer_.rewind();u_buffer_.rewind();v_buffer_.rewind();List<LibPublisherWrapper> publisher_list = get_publisher_list();if (null == publisher_list || publisher_list.isEmpty())return;for (LibPublisherWrapper i : publisher_list) {i.PostLayerImageI420ByteBuffer(0, 0, 0,y_buffer_, 0, y_row_bytes_,u_buffer_, 0, u_row_bytes_,v_buffer_, 0, v_row_bytes_,width_, height_, 0, 0,0,0, 0,0);}}
}

总结

Android平台RTSP、RTMP播放器回调yuv数据,意义非常重大,既保证了低延迟传输解码,又可以通过回调解码后数据,高效率的投递给AI算法,实现视觉处理。ffmpeg实现还是SmartPlayer,各有利弊,感兴趣的开发者,可以单独跟我探讨。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络-MSTP概述

一、RSTP/STP的缺陷与不足 前面我们学习了RSTP对于STP的一些优化与快速收敛机制。但在划分VLAN的网络中运行RSTP/STP&#xff0c;局域网内所有的VLAN共享一棵生成树&#xff0c;被阻塞后的链路将不承载任何流量&#xff0c;无法在VLAN间实现数据流量的负载均衡&#xff0c;导致…

ios 快捷指令扩展(Intents Extension)简单使用 swift语言

本文介绍使用Xcode15 建立快捷指令的Extension&#xff0c;并描述如何修改快捷指令的IntentHandler&#xff0c;带参数跳转主应用&#xff1b;以及展示多个选项的快捷指令弹框(配置intentdefinition文件)&#xff0c;点击选项带参数跳到主应用的方法 创建快捷指令 快捷指令是…

智能财务 | 数据与融合,激发企业财务数智化转型思考

数据与融合&#xff0c;激发企业财务数智化转型思考 用友持续深耕企业财务领域&#xff0c;见证中国企业走过了财务电算化、信息化时代&#xff0c;当下共同经历数智化时代。2023 年度&#xff0c;通过走访标杆企业&#xff0c;与高校教授、权威机构学者共同探讨等形式&#xf…

openpnp - 解决“底部相机高级校正成功后, 开机归零时,吸嘴自动校验失败的问题“

文章目录 openpnp - 解决"底部相机高级校正成功后, 开机归零时&#xff0c;吸嘴自动校验失败的问题"概述笔记问题现象1问题现象2原因分析现在底部相机和吸嘴的位置偏差记录修正底部相机位置现在再看看NT1在底部相机中的位置开机归零&#xff0c;看看是否能通过所有校…

DreamClear:中科院与字节联合推出!隐私安全优先的高性能图像修复技术

❤️ 如果你也关注大模型与 AI 的发展现状&#xff0c;且对大模型应用开发非常感兴趣&#xff0c;我会快速跟你分享最新的感兴趣的 AI 应用和热点信息&#xff0c;也会不定期分享自己的想法和开源实例&#xff0c;欢迎关注我哦&#xff01; &#x1f966; 微信公众号&#xff…

SpringBoot驱动的毕业生招聘信息平台

1 系统概述 1.1 概述  随着社会的快速发展&#xff0c;计算机的影响是全面且深入的。人们的生活水平不断提高&#xff0c;日常生活中毕业生对招聘平台方面的要求也在不断提高&#xff0c;需要招聘平台的人数更是不断增加&#xff0c;使得毕业生信息招聘平台的开发成为必需而且…

Mac程序坞窗口预览的方法来了

当你同一程序内打开的窗口过多的时候&#xff0c;在Mac上想要切换就只能打开程序然后在内部进行切换&#xff0c;没办法直达你想要打开的窗口&#xff0c;多了一步的操作&#xff0c;那么如何才能一步到位呢 如果程序坞有应用程序的缩略图&#xff0c;是不是就可以一步到位了&…

【C/C++】结构体的定义

零.导言 在上一篇博客中&#xff0c;我讲解了qsort函数&#xff0c;并在其中提到了结构体数组的排序&#xff0c;那么结构体是什么呢? 接下来我将详细讲解结构体的定义。 一&#xff0c;结构体是什么&#xff1f; 结构体是自定义的数据类型&#xff0c;可以存放自定义的数据。…

JavaScript 中如何识别异步函数?

我们如何判断一个函数是否是异步函数&#xff08;async function&#xff09;呢&#xff1f; 遇到问题的思考过程是什么呢&#xff0c;首先需要找到二者的区别&#xff0c;那就打印看一下&#xff0c;然后在思考如何做。 由此可以看出二者的差异。 1、使用 typeof 检查函数类…

springboot学生请假管理系统-计算机毕业设计源码12712

摘 要 从20年代开始&#xff0c;计算机在人们的生活和工作中广泛应用&#xff0c;成为了人们生活、工作的得力助手。计算机深入到每个家庭和每个工作场所&#xff0c;网络办公和网络教学取代了传统的手工记录和管理方式。使用计算机办公可以不受时间和地点限制&#xff0c;通过…

频率限制:WAF保护网站免受恶意攻击的关键功能

频率限制&#xff08;Rate Limiting&#xff09;是一项有效的安全措施&#xff0c;用于控制每个 IP 地址的访问速率&#xff0c;以防止恶意用户利用大量请求对网站进行攻击&#xff0c;例如防止 CC 攻击等。频率限制不仅能保护网站资源&#xff0c;还能提升服务的稳定性。 下面…

ClickHouse 神助攻:纽约城市公共交通管理(MTA)数据应用挑战赛

本文字数&#xff1a;13198&#xff1b;估计阅读时间&#xff1a;33 分钟 作者&#xff1a;The PME Team 本文在公众号【ClickHouseInc】首发 我们一向对开放数据挑战充满热情&#xff0c;所以当发现 MTA&#xff08;城市交通管理局&#xff09;在其官网发起了这样的挑战时&…

什么是数据中心?

数据中心是一个专门用于容纳大量联网计算机设备的设施&#xff0c;这些设备共同协作&#xff0c;以处理、存储和传输数据。现代社会中&#xff0c;大部分高科技公司都依赖数据中心来提供在线服务&#xff0c;例如网站、应用程序和云服务等。可以说&#xff0c;数据中心是互联网…

【论文精读】ID-like Prompt Learning for Few-Shot Out-of-Distribution Detection

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;论文精读_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 注&#xff1a;下文…

【文心智能体 | AI大师工坊】如何使用智能体插件,完成一款旅游类智能体的开发,来体验一下我的智能体『​​​​​​​背包客』

&#x1f680;『背包客』点击前往体验&#xff1a;https://mbd.baidu.com/ma/s/d7RHMlWh 最近参加了百度文心智能体平台AI大师工坊&#x1f389;活动&#xff0c;在这个活动中&#xff0c;我利用文心平台提供的各种插件、大模型等工具&#xff0c;打造了一个工具类的智能体应用…

理解ADC:为什么量化噪声也会产生谐波?附带介绍 Dither(抖动)

前言 今天继续从经典的 ADI 《MT-001》说起&#xff0c;通常情况下量化噪声是白噪声&#xff0c;但如果量化噪声与输入信号之间存在相关性&#xff0c;就不能被当做白噪声对待。 文中举了一个有意思的例子&#xff1a;理想 ADC 的采样频率为 80 MSPS &#xff0c;一种情况输入…

从0到1构建 UniApp + Vue3 + TypeScript 移动端跨平台开源脚手架

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f343; vue-uniapp-template &#x1f33a; 仓库主页&#xff1a; GitCode&#x1f4ab; Gitee &#x1f…

Docker部署教程:打造流畅的斗地主网页小游戏

Docker部署教程:打造流畅的斗地主网页小游戏 一、项目介绍项目简介项目预览二、系统要求环境要求环境检查Docker版本检查检查操作系统版本三、部署斗地主网页小游戏下载镜像创建容器检查容器状态查看容器日志安全设置四、访问斗地主网页小游戏五、总结一、项目介绍 项目简介 …

计算机视觉常用数据集Cityscapes的介绍、下载、转为YOLO格式进行训练

我在寻找Cityscapes数据集的时候花了一番功夫&#xff0c;因为官网下载需要用公司或学校邮箱邮箱注册账号&#xff0c;等待审核通过后才能进行下载数据集。并且一开始我也并不了解Cityscapes的格式和内容是什么样的&#xff0c;现在我弄明白后写下这篇文章&#xff0c;用于记录…

【机器学习】Lesson3 - 逻辑回归(LR)二分类

目录 背景 一、适用数据集 1. 数据集选择 1.1 领域 1.2 数据集维度 1.3 记录行&#xff08;样本数量&#xff09; 2. 本文数据集介绍 3. 数据集下载 注意 二、逻辑回归的基本原理 1. 目的 2. Sigmoid 函数 3. 类别划分 4. 召回率 三、代码 1. 导入所需包&数…