开发者

如何使用OpenCV进行视频读取与处理的完整指南

开发者 https://www.devze.com 2024-08-15 09:16 出处:网络 作者: 一键难忘
目录使用OpenCV进行视频读取与处理的完整指南一、视频读取的基础知识二、视频处理的常见操作1. 灰度处理2. 边缘检测3. 背景减除三、视频处理中的性能优化1. 合理选择视频分辨率和帧率2. 利用多线程处理3. 使用GPU加速
目录
  • 使用OpenCV进行视频读取与处理的完整指南
  • 一、视频读取的基础知识
  • 二、视频处理的常见操作
    • 1. 灰度处理
    • 2. 边缘检测
    • 3. 背景减除
  • 三、视频处理中的性能优化
    • 1. 合理选择视频分辨率和帧率
    • 2. 利用多线程处理
    • 3. 使用GPU加速
  • 四、视频的保存与导出
    • 1. 视频的保存
    • 2. 调整视频格式和压缩率www.devze.com
  • 五、高级视频处理技术
    • 1. 视频的对象检测与跟踪
    • 2. 多目标跟踪
    • 3. 视频稳定化
  • 六、视频处理中的挑战与解决方案
    • 1. 实时处理性能优化
    • 2. 数据存储与管理
    • 3. 处理多种视频格式
  • 七、视频处理中的常见问题及调试技巧
    • 1. 视频无法读取或读取失败
    • 2. 处理过程中的卡顿或延迟
    • 3. 输出视频质量不佳
    • 4. 内存泄漏与资源管理
  • android、实际应用案例分析
    • 1. 安防监控中的对象检测与报警系统
    • 2. 自动驾驶中的车道检测与跟踪
    • 3. 智能家居中的人脸识别与身份验证
  • 九、前沿技术展望

    从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。

    每一个案例都附带关键代码,详细讲解供大家学习,希望可以帮到大家。正在不断更新中~

    使用OpenCV进行视频读取与处理的完整指南

    OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库,广泛应用于图像和视频的处理。视频处理在诸多领域,如监控系统、运动检测、自动驾驶等,扮演着至关重要的角色。本篇文章将详细解析如何使用OpenCV读取和处理视频,并结合实际的代码示例来展示操作的全过程,同时探讨一些性能优化的策略。

    一、视频读取的基础知识

    在进行视频处理前,首先需要了解如何使用OpenCV读取视频。OpenCV提供了VideoCapture类,用于从视频文件、摄像头或其他视频流中捕获视频。

    import cv2
    # 创建VideoCapture对象
    cap = cv2.VideoCapture('video.mp4')  # 传入视频文件路径
    # 检查视频是否成功打开
    if not cap.isOpened():
        print("Error: 无法打开视频文件")
        exit()
    # 循环读取视频帧
    while True:
        ret, frame = cap.read()
        # 如果视频结束或出现错误,退出循环
        if not ret:
            break
        # 显示每一帧
        cv2.imshow('Video Frame', frame)
        # 按下'q'键退出播放
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break
    # 释放资源
    cap.release()
    cv2.destroyAllWindows()

    以上代码展示了如何从一个本地视频文件中读取帧,并逐帧显示。这是视频处理的第一步:确保视频源能够成功读取。

    二、视频处理的常见操作

    视频处理可以涵盖从基本的帧间操作到复杂的图像增强与分析。下面我们将介绍一些常见的视频处理操作:

    如何使用OpenCV进行视频读取与处理的完整指南

    1. 灰度处理

    将视频转换为灰度图像是视频处理中常见的预处理步骤,尤其在需要简化计算或处理算法时尤为重要。

    while True:
        ret, frame = cap.read()
        if not ret:
            break
        # 转换为灰度图像
        gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        # 显示灰度帧
        cv2.imshow('Gray Video Frame', gray_frame)
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break

    2. 边缘检测

    边缘检测可以帮助识别图像中的物体轮廓,是许多计算机视觉任务的基础。OpenCV的Canny边缘检测器是一个常用的工具。

    while True:
        ret, frame = cap.read()
        if not ret:
            break
        # 转换为灰度图像
        gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        # 应用Canny边缘检测
        edges = cv2.Canny(gray_frame, 100, 200)
        # 显示边缘检测结果
        cv2.imshow('Edges', edges)
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break

    3. 背景减除

    背景减除是视频的监控中的常见任务,用于检测前景对象。OpenCV提供了多种背景减除算法,例如MOG2

    # 创建背景减除器
    fgbg = cv2.createBackgroundSubtractorMOG2()
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        # 应用背景减除
        fgmask = fgbg.apply(frame)
        # 显示背景减除结果
        cv2.imshow('Foreground Mask', fgmask)
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break

    三、视频处理中的性能优化

    视频处理通常需要处理大量数据,因此优化性能至关重要。以下是几种优化策略:

    1. 合理选择视频分辨率和帧率

    视频分辨率和帧率对处理速度有直接影响。根据需求选择适当的分辨率和帧率可以显著提高处理性能。

    # 降低视频分辨率
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

    如何使用OpenCV进行视频读取与处理的完整指南

    2. 利用多线程处理

    在现代多核处理器中,多线程可以提高处理速度。python的threading模块或concurrent.futures库都可以帮助实现多线程视频处理。

    3. 使用GPU加速

    OpenCV支持CUDA等GPU加速技术。在图像处理和视频分析中使用GPU可以大幅提升性能。

    import cv2.cuda as cuda
    # 使用CUDA加速某些操作
    gpu_frame = cuda_GpuMat()
    gpu_frame.upload(frame)
    gpu_gray = cuda.cvtColor(gpu_frame, cv2.COLOR_BGR2GRAY)

    四、视频的保存与导出

    在处理完视频帧后,通常需要将处理后的视频保存下来,以便进一步分析或应用。OpenCV 提供了VideoWriter类来进行视频的保存操作。通过指定视频的编码格式、帧率和输出文件路径,可以将处理后的每一帧写入一个新的视频文件中。

    1. 视频的保存

    下面是如何使用VideoWriter保存处理后的视频的代码示例:

    import cv2
    # 创建VideoCapture对象
    cap = cv2.VideoCapture('input_video.mp4')
    # 获取视频的宽度、高度和帧率
    frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = int(cap.get(cv2.CAP_PROP_FPS))
    # 创建VideoWriter对象
    output_filename = 'output_video.avi'
    fourcc = cv2.VideoWriter_fourcc(*'XVID')  # 使用XVID编码
    out = cv2.VideoWriter(output_filename, fourcc, fps, (frame_width, frame_height))
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        # 在此处添加处理代码,例如灰度转换、边缘检测等
        gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        colored_frame = cv2.cvtColor(gray_frame, cv2.COLOR_GRAY2BGR)  # 转换回三通道以便保存
        # 将处理后的帧写入输出视频
        out.write(colored_frame)
        cv2.imshow('Processed Video', colored_frame)
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break
    # 释放资源
    cap.releasaQOkqPe()
    out.release()
    cv2.destroyAllWindows()

    在上述代码中,VideoWriter_fourcc函数用于指定视频编码格式,这里选择了常用的XVID格式。保存的视频帧必须与原始帧具有相同的宽度和高度。此外,确保处理后的图像仍为三通道(即RGB),因为大多数视频格式要求输出的帧具有这种结构。

    2. 调整视频格式和压缩率

    视频格式和压缩率对文件大小和质量有直接影响。根据不同的应用场景,选择合适的编码器可以大幅影响视频的输出效果。

    # 使用H264编码
    fourcc = cv2.VideoWriter_fourcc(*'H264')
    out = cv2.VideoWriter('output_h264.mp4', fourcc, fps, (frame_width, frame_height))

    H.264编码在视频压缩和质量平衡上表现优异,适合用于流媒体和视频存储。

    五、高级视频处理技术

    如何使用OpenCV进行视频读取与处理的完整指南

    在完成基本的视频读取、处理与保存后,我们可以进一步探索更高级的视频处理技术。这些技术常用于视频分析与计算机视觉领域的复杂任务中。

    1. 视频的对象检测与跟踪

    对象检测与跟踪是计算机视觉中的核心问题。通过使用预训练的深度学习模型,例如YOLO或SSD,OpenCV能够在视频中实时检测和跟踪对象。

    import cv2
    import numpy as np
    # 加载预训练的YOLO模型
    net = cv2.dnn.readNet('yolov3.weights', 'yolov3.cfg')
    layer_names = net.getLayerNames()
    output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        # 预处理帧以适应YOLO模型
        blob = cv2.dnn.blobFromImage(frame, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
        net.setInput(blob)
        # 前向传播以获得检测结果
        outs = net.forward(output_layers)
        # 分析检测结果并绘制边界框
        for out in outs:
            for detection in out:
                scores = detection[5:]
                class_id = np.argmax(scores)
                confidence = scores[class_id]
                if confidence > 0.5:
                    center_x = int(detection[0] * frame_width)
                    center_y = int(detection[1] * frame_height)
                    w = int(detection[2] * frame_width)
                    h = int(detection[3] * frame_height)
                    x = int(center_x - w / 2)
                    y = int(center_y - h / 2)
                    # 绘制检测到的对象的边界框
                    cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
                    text = f'{class_id}: {int(confidence * 100)}%'
                    cv2.putText(frame, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
        cv2.imshow('Object Detection', frame)
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break

    2. 多目标跟踪

    如何使用OpenCV进行视频读取与处理的完整指南

    在多目标跟踪中,我们需要同时跟踪视频中多个对象的运动轨迹。这通常结合了对象检测与跟踪算法,例如Kalman滤波器、MeanShift或KCF跟踪器。

    # 创建KCF跟踪器
    tracker = cv2.TrackerKCF_create()
    # 初始化跟踪器
    ret, frame = cap.read()
    bbox = cv2.selectROI('Frame', frame, False)
    tracker.init(frame, bbox)
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        # 更新跟踪器
        ret, bbox = tracker.update(frame)
        if ret:
            x, y, w, h = [int(v) for v in bbox]
            cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)
        else:
            cv2.putText(frame, 'Tracking Failure', (100, 80), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 0, 255), 2)
        cv2.imshow('Multi-Object Tracking', frame)
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break

    3. 视频稳定化

    视频稳定化是指通过消除抖动或不规则运动来平滑视频。OpenCV通过分析帧间运动矢量来实现这一点,从而减少由于摄像机抖动而引起的画面不稳定。

    import cv2
    import numpy as np
    # 读取视频
    cap = cv2.VideoCapture('shaky_video.mp4')
    # 用于保存处理后的视频
    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    out = cv2.VideoWriter('stabilized_video.avi', fourcc, fps, (frame_width, frame_height))
    # 获取第一帧
    ret, prev_frame = cap.read()
    prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
    # 初始化转换矩阵
    transforms = []
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        # 转换为灰度图
        curr_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        # 计算光流
        prev_pts = cv2.goodFeaturesToTrack(prev_gray, maxCorners=200, qualityLevel=0.01, minDistance=30, blockSize=3)
        curr_pts, status, err = cv2.calcOpticalFlowpyrLK(prev_gray, curr_gray, prev_pts, None)
        # 过滤有效点
        valid_prev_pts = prev_pts[status == 1]
        valid_curr_pts = curr_pts[status == 1]
        # 计算变换矩阵
        matrix = cv2.estimateAffinePartial2D(valid_prev_pts, valid_curr_pts)[0]
        transforms.append(matrix)
        # 将当前帧应用到转换矩阵上
        stabilized_frame = cv2.warpAffine(frame, matrix, (frame_width, frame_height))
        out.write(stabilized_frame)
        cv2.imshow('Stabilized Video', stabilized_frame)
        prev_gray = curr_gray
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break
    # 释放资源
    cap.release()
    out.release()
    cv2.destroyAllWindows()

    六、视频处理中的挑战与解决方案

    尽管OpenCV提供了强大的工具集用于视频处理,但在实际应用中,我们仍然会面临各种挑战,如处理速度、算法复杂度、硬件资源限制等。以下是一些常见的挑战及相应的解决方案:

    1. 实时处理性能优化

    如何使用OpenCV进行视频读取与处理的完整指南

    实时视频处理对系统性能提出了较高要求。为提高处理效率,可以考虑以下优化策略:

    • 降低视频分辨率和帧率:减少每帧的像素数和帧数,直接降低处理负荷。
    • 使用更高效的算法

    :例如在对象检测中使用轻量级的模型,如MobileNet或Tiny-YOLO。

    • 利用硬件加速:在支持的设备上启用GPU加速,如CUDA或OpenCL,加速图像处理和深度学习推理。

    2. 数据存储与管理

    在处理大规模视频数据时,存储和管理也是一大挑战。考虑以下建议:

    • 视频分段处理:将长视频分段处理,减少内存占用并提高灵活性。
    • 高效的视频压缩格式:使用如H.265/HEVC等高效的视频压缩格式,减少存储需求。
    • 使用数据库管理元数据:将视频文件及其对应的处理信息存储在数据库中,以便检索和管理。

    3. 处理多种视频格式

    处理多种视频格式(如MP4、AVI、MOV等)时,可能会遇到兼容性问题。为此,建议使用FFmpeg工具进行预处理,将视频转换为统一格式(如H.264编码的MP4),从而确保处理流程的一致性。

    七、视频处理中的常见问题及调试技巧

    在进行视频处理时,我们经常会遇到一些常见的问题,例如视频无法读取、处理过程中出现卡顿、或者视频输出质量不佳。针对这些问题,掌握一些调试技巧和解决方案将大大提高开发效率。

    1. 视频无法读取或读取失败

    在处理视频时,最常见的问题之一是视频无法读取或在读取过程中出现错误。这可能由于文件路径错误、不支持的视频格式或编解码器问题导致。

    解决方案

    • 检查文件路径:确保提供的文件路径正确且文件存在。
    • 检查视频格式和编解码器:如果视频格式不受OpenCV支持,可以尝试使用FFmpeg进行转换,例如:
    ffmpeg -i input_video.mov -vcodec libx264 output_video.mp4
    • 尝试不同的视频源:例如通过摄像头捕获视频时,如果出现问题,可以尝试更换摄像头或使用其他视频源测试。

    2. 处理过程中的卡顿或延迟

    在处理高分辨率或高帧率视频时,可能会遇到处理过程中的卡顿或延迟。这通常是由于算法复杂度过高或硬件资源不足造成的。

    解决方案

    • 优化代码:确保代码中没有冗余的计算和操作,例如重复的图像转换或复杂的嵌套循环。
    • 调整分辨率和帧率:通过降低视频分辨率和帧率来减少处理负荷。
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
    • 启用硬件加速:在支持GPU的设备上,可以启用CUDA或OpenCL进行加速,特别是在深度学习模型推理时:
    net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
    net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)

    3. 输出视频质量不佳

    在保存处理后的视频时,可能会遇到视频质量下降、压缩失真等问题。这可能由于编码器选择不当或压缩率设置过高导致。

    解决方案

    • 选择合适的编码器和压缩率:根据实际需求选择适当的视频编码器和压缩比。例如,对于高质量需求的视频,可以选择H.264编码并设置较低的压缩率:
    fourcc = cv2.VideoWriter_fourcc(*'H264')
    out = cv2.VideoWriter('high_quality_output.mp4', fourcc, 20, (frame_width, frame_height))
    • 调整输出参数:通过调整视频的比特率、帧率等参数来优化输出质量。例如,增加比特率可以提高视频的清晰度,但也会增加文件大小。
    ffmpeg -i input_video.mp4 -b:v 2M output_video.mp4

    4. 内存泄漏与资源管理

    在长时间处理视频时,可能会遇到内存泄漏或资源占用过高的问题。这通常是由于资源(如VideoCapture和Vid编程eoWriter对象)未及时释放造成的。

    解决方案

    • 确保资源释放:在代码中使用cap.release()out.release()等方法释放资源,并在处理结束时调用cv2.destroyAllWindows()关闭所有窗口。
    cap.release()
    out.release()
    cv2.destroyAllWindows()
    • 使用内存管理工具:在长时间运行的视频处理应用中,可以使用Python的内存分析工具(如memory_profiler)监控内存使用情况,并优化代码以减少内存占用。

    八、实际应用案例分析

    为了更好地理解视频处理在实际中的应用场景,下面我们通过几个典型的案例来分析如何将上述技术应用到真实项目中。

    如何使用OpenCV进行视频读取与处理的完整指南

    1. 安防监控中的对象检测与报警系统

    在安防监控系统中,实时检测视频中的可疑行为并发出报警是一个典型应用。通过结合OpenCV与深度学习模型(如YOLO),我们可以实时分析监控视频,检测特定对象(如人、车辆等),并根据设定规则发出报警。

    案例代码示例

    import cv2
    def detect_objects_and_alert(frame, net, output_layers, confidence_threshold=0.5):
        blob = cv2.dnn.blobFromImage(frame, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
        net.setInput(blob)
        outs = net.forward(output_layers)
        for out in outs:
            for detection in out:
                scores = detection[5:]
                class_id = np.argmax(scores)
                confidence = scores[class_id]
                if confidence > confidence_threshold:
                    center_x = int(detection[0] * frame_width)
                    center_y = int(detection[1] * frame_height)
                    w = int(detection[2] * frame_width)
                    h = int(detection[3] * frame_height)
                    x = int(center_x - w / 2)
         aQOkqP           y = int(center_y - h / 2)
                    # 绘制边界框
                    cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
                    # 发出报警(可以是声音、发送信息等)
                    print("Object detected: Alert!")
        return frame
    # 主循环
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        processed_frame = detect_objects_and_alert(frame, net, output_layers)
        cv2.imshow('Security Monitoring', processed_frame)
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break

    2. 自动驾驶中的车道检测与跟踪

    在自动驾驶系统中,车道检测是关键任务之一。通过处理车载摄像头的视频流,检测道路车道线,并实时进行跟踪和导航调整。

    案例代码示例

    import cv2
    import numpy as np
    def detect_lane(frame):
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        blur = cv2.GaussianBlur(gray, (5, 5), 0)
        edges = cv2.Canny(blur, 50, 150)
        # 定义感兴趣区域
        height, width = edges.shape
        mask = np.zeros_like(edges)
        triangle = np.array([[(0, height), (width, height), (width // 2, height // 2)]])
        cv2.fillPoly(mask, triangle, 255)
        masked_edges = cv2.bitwise_and(edges, mask)
        # Hough变换检测车道线
        lines = cv2.HoughLinesP(masked_edges, 1, np.pi/180, threshold=100, minLineLength=40, maxLineGap=5)
        if lines is not None:
            for line in lines:
                x1, y1, x2, y2 = line[0]
                cv2.line(frame, (x1, y1), (x2, y2), (255, 0, 0), 5)
        return frame
    # 主循环
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        lane_frame = detect_lane(frame)
        cv2.imshow('Lane Detection', lane_frame)
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break

    3. 智能家居中的人脸识别与身份验证

    智能家居系统中的人脸识别功能可以用于自动门禁、个性化设置等场景。通过在视频流中实时检测并识别家庭成员的脸部特征,可以实现身份验证和设备自动化控制。

    案例代码示例

    import cv2
    # 加载人脸检测器和人脸识别模型
    face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.XML')
    recognizer = cv2.face.LBPHFaceRecognizer_create()
    recognizer.read('face_trainer.yml')
    def recognize_face(frame, face_cascade, recognizer):
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
        for (x, y, w, h) in faces:
            roi_gray = gray[y:y+h, x:x+w]
            id_, confidence = recognizer.predict(roi_gray)
            if confidence >= 45:  # 根据实际情况调整阈值
                name = 'User'  # 从数据库获取名字
                cv2.putText(frame, name, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)
                cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 
    0, 0), 2)
                # 执行身份验证通过后的操作
                print("Access Granted!")
            else:
                cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 2)
                print("Access Denied!")
        return frame
    # 主循环
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        recognized_frame = recognize_face(frame, face_cascade, recognizer)
        cv2.imshow('Face Recognition', recognized_frame)
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break

    如何使用OpenCV进行视频读取与处理的完整指南

    九、前沿技术展望

    随着人工智能与计算机视觉技术的快速发展,视频处理领域也在不断创新。未来,我们将看到更多基于深度学习的实时视频处理应用,例如:

    • 实时对象检测与分割:通过更高效的深度学习模型,实现视频中对象的实时检测与分割,并应用于自动驾驶、安防监控等领域。
    • 视频增强与修复:利用生成对抗网络(GANs)等技术,实时增强视频质量,修复低清晰度视频或旧视频。
    • 多模态视频分析:结合音频、文本与视频内容,实现多模态分析应用,如视频内容摘要、情感分析等。

    通过不断学习与实践这些新技术,我们可以在视频处理领域获得更大的成就,并为各行各业提供更智能、更高效的解决方案。

    到此这篇关于使用OpenCV进行视频读取与处理的完整指南的文章就介绍到这了,更多相关OpenCV视频读取与处理内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号