Chromium Mojo(IPC)进程通信演示 c++(3)

122版本自带的mojom通信例子channel-associated-interface 仅供学习参考:

codelabs\mojo_examples\03-channel-associated-interface-freezing

其余定义参考上一篇文章:

Chromium Mojo(IPC)进程通信演示 c++(2)-CSDN博客​​​​​​

03-mojo-browser.exe 与 03-mojo-renderer.exe进程通信完整例子。

一、目录结构:

二、03-mojo-browser.exe

codelabs\mojo_examples\03-channel-associated-interface-freezing\browser.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "base/logging.h"
#include "base/message_loop/message_pump.h"
#include "base/process/launch.h"
#include "base/run_loop.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/threading/thread.h"
#include "codelabs/mojo_examples/mojom/interface.mojom.h"
#include "codelabs/mojo_examples/process_bootstrapper.h"
#include "ipc/ipc_channel_mojo.h"
#include "ipc/ipc_channel_proxy.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/embedder/scoped_ipc_support.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"

mojo::ScopedMessagePipeHandle LaunchAndConnect() {
  // Under the hood, this is essentially always an OS pipe (domain socket pair,
  // Windows named pipe, Fuchsia channel, etc).
  mojo::PlatformChannel channel;

  mojo::OutgoingInvitation invitation;

  // Attach a message pipe to be extracted by the receiver. The other end of the
  // pipe is returned for us to use locally.
  mojo::ScopedMessagePipeHandle ipc_bootstrap_pipe =
      invitation.AttachMessagePipe("ipc_bootstrap_pipe");

  base::LaunchOptions options;
  // This is the relative path to the mock "renderer process" binary. We pass it
  // into `base::LaunchProcess` to run the binary in a new process.
  static const base::CommandLine::CharType* argv[] = {
      FILE_PATH_LITERAL("./03-mojo-renderer")};
  base::CommandLine command_line(1, argv);
  channel.PrepareToPassRemoteEndpoint(&options, &command_line);
  LOG(INFO) << "Browser: " << command_line.GetCommandLineString();
  base::Process child_process = base::LaunchProcess(command_line, options);
  channel.RemoteProcessLaunchAttempted();

  mojo::OutgoingInvitation::Send(std::move(invitation), child_process.Handle(),
                                 channel.TakeLocalEndpoint());
  return ipc_bootstrap_pipe;
}

class BrowserIPCListener : public IPC::Listener {
 public:
  BrowserIPCListener(mojo::ScopedMessagePipeHandle ipc_bootstrap_pipe,
                     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
      : IPC::Listener() {
    // This program differs from `02-associated-interface-freezing`, because
    // we're using channel-associated interfaces as opposed to non-channel
    // associated interfaces. This means we need to set up an
    // `IPC::ChannelProxy` in addition to the regular mojo stuff. The sequence
    // of events will look like so:
    //   1.) Bootstrap the IPC channel. This is actually pretty easy. We just a
    //       pull a message pipe handle off of the mojo invitation we sent to
    //       the renderer process earlier, and feed that pipe handle into the
    //       IPC::ChannelProxy. From there, we can start requesting remote
    //       associated interfaces directly from the IPC channel.
    //   2.) Requesting a remote channel-associated interface from the
    //       IPC::Channel mojo connection, for interface
    //       `codelabs::mojom::ObjectA`
    //   3.) Do the same thing as (2) but for `codelabs::mojom::ObjectB`. Both
    //       of our `ObjectA` and `ObjectB` connections are channel-associated,
    //       however the remote "renderer" process will bind the backing
    //       mojo::AssociatedReceiver for each of these to different TaskQueues.
    //
    //       We first send a message to `ObjectA`, whose backing
    //       mojo::AssociatedReceiver is bound to a TaskQueue that is initially
    //       frozen
    //
    //       We then send a message to `ObjectB`, whose backing
    //       mojo::AssociatedReceiver is bound to a normal unfrozen TaskQueue.
    //
    //       From this we see two results:
    //         - The message for `ObjectA` is not delayed, despite its
    //           AssociatedReceiver being bound to a frozen TaskQueue. This is
    //           because we cannot delay channel-associated message from being
    //           delivered due to legacy IPC deadlock reasons
    //         - If you then comment out the part of the renderer code that
    //           binds the `ObjectA` interface (so that you prevent it from ever
    //           being bound to an implementation), you then observe that the
    //           `ObjectB` message is not blocked at all on the `ObjectA`
    //           message, for the same reasons above.

    // 1.) Bootstrap the IPC Channel.
    std::unique_ptr<IPC::ChannelFactory> channel_factory =
        IPC::ChannelMojo::CreateServerFactory(
            std::move(ipc_bootstrap_pipe), io_task_runner,
            base::SingleThreadTaskRunner::GetCurrentDefault());
    channel_proxy_ = IPC::ChannelProxy::Create(
        std::move(channel_factory), this, /*ipc_task_runner=*/io_task_runner,
        /*listener_task_runner=*/
        base::SingleThreadTaskRunner::GetCurrentDefault());

    // 2.) Bind and send an IPC to ObjectA.
    mojo::AssociatedRemote<codelabs::mojom::ObjectA> remote_a;
    channel_proxy_->GetRemoteAssociatedInterface(&remote_a);
    remote_a->DoA();

    // 3.) Bind and send an IPC to ObjectB.
    mojo::AssociatedRemote<codelabs::mojom::ObjectB> remote_b;
    channel_proxy_->GetRemoteAssociatedInterface(&remote_b);
    remote_b->DoB();
  }

  // IPC::Listener implementation.
  bool OnMessageReceived(const IPC::Message& msg) override {
    CHECK(false) << "The browser should not receive messages";
    return false;
  }
  void OnAssociatedInterfaceRequest(
      const std::string& interface_name,
      mojo::ScopedInterfaceEndpointHandle handle) override {
    CHECK(false)
        << "The browser should not receive associated interface requests";
  }

 private:
  std::unique_ptr<IPC::ChannelProxy> channel_proxy_;
};

int main(int argc, char** argv) {
  LOG(INFO) << "Browser process starting up";
  base::CommandLine::Init(argc, argv);

  ProcessBootstrapper bootstrapper;
  // The IO thread that the `BrowserIPCListener` ChannelProxy listens for
  // messages on *must* be different than the main thread, so in this example
  // (and in the corresponding "renderer.cc") we initialize the main thread with
  // a "DEFAULT" (i.e., non-IO-capable) main thread. This will automatically
  // give us a separate dedicated IO thread for Mojo and the IPC infrastructure
  // to use.
  bootstrapper.InitMainThread(base::MessagePumpType::DEFAULT);
  bootstrapper.InitMojo(/*as_browser_process=*/true);

  mojo::ScopedMessagePipeHandle handle = LaunchAndConnect();

  // Create a new `BrowserIPCListener` to sponsor communication coming from the
  // "browser" process. The rest of the program will execute there.
  std::unique_ptr<BrowserIPCListener> browser_ipc_listener =
      std::make_unique<BrowserIPCListener>(std::move(handle),
                                           bootstrapper.io_task_runner);

  base::RunLoop run_loop;
  // Delay shutdown of the browser process for visual effects, as well as to
  // ensure the browser process doesn't die while the IPC message is still being
  // sent to the target process asynchronously, which would prevent its
  // delivery. This delay is an arbitrary 5 seconds, which just needs to be
  // longer than the renderer's 3 seconds, which is used to show visually via
  // logging, how the ordering of IPCs can be effected by a frozen task queue
  // that gets unfrozen 3 seconds later.
  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
      FROM_HERE,
      base::BindOnce(
          [](base::OnceClosure quit_closure) {
            LOG(INFO) << "'Browser process' shutting down";
            std::move(quit_closure).Run();
          },
          run_loop.QuitClosure()),
      base::Seconds(5));
  run_loop.Run();
  return 0;
}

三、03-mojo-renderer.exe

codelabs\mojo_examples\03-channel-associated-interface-freezing\renderer.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>

#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/task/sequence_manager/task_queue.h"
#include "codelabs/mojo_examples/mojo_impls.h"
#include "codelabs/mojo_examples/mojom/interface.mojom.h"
#include "codelabs/mojo_examples/process_bootstrapper.h"
#include "ipc/ipc_channel_mojo.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_sync_channel.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"

static ObjectAImpl g_object_a;
static ObjectBImpl g_object_b;

class CustomTaskQueue : public base::RefCounted<CustomTaskQueue> {
 public:
  CustomTaskQueue(base::sequence_manager::SequenceManager& sequence_manager,
                  const base::sequence_manager::TaskQueue::Spec& spec)
      : task_queue_(sequence_manager.CreateTaskQueue(spec)),
        voter_(task_queue_->CreateQueueEnabledVoter()) {}
  void FreezeTaskQueue() { voter_->SetVoteToEnable(false); }

  void UnfreezeTaskQueue() {
    LOG(INFO) << "Unfreezing the task queue that `ObjectAImpl` is bound to.";
    voter_->SetVoteToEnable(true);
  }

  const scoped_refptr<base::SingleThreadTaskRunner>& task_runner() const {
    return task_queue_->task_runner();
  }

 private:
  ~CustomTaskQueue() = default;
  friend class base::RefCounted<CustomTaskQueue>;

  base::sequence_manager::TaskQueue::Handle task_queue_;
  // Used to enable/disable the underlying `TaskQueueImpl`.
  std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter> voter_;
};

class RendererIPCListener : public IPC::Listener {
 public:
  RendererIPCListener(
      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
      scoped_refptr<base::SingleThreadTaskRunner> initially_frozen_task_runner)
      : initially_frozen_task_runner_(initially_frozen_task_runner) {
    // The sequence of events we'll need to perform are the following:
    //   1.) Create the ChannelProxy (specifically a SyncChannel) for the
    //       receiving end of the IPC communication.
    //   2.) Accept the incoming mojo invitation. From the invitation, we
    //       extract a message pipe that we will feed directly into the
    //       `IPC::ChannelProxy` to initialize it. This bootstraps the
    //       bidirectional IPC channel between browser <=> renderer.

    // 1.) Create a new IPC::ChannelProxy.
    channel_proxy_ = IPC::SyncChannel::Create(
        this, io_task_runner, base::SingleThreadTaskRunner::GetCurrentDefault(),
        &shutdown_event_);

    // 2.) Accept the mojo invitation.
    mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
        mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
            *base::CommandLine::ForCurrentProcess()));
    mojo::ScopedMessagePipeHandle ipc_bootstrap_pipe =
        invitation.ExtractMessagePipe("ipc_bootstrap_pipe");

    // Get ready to receive the invitation from the browser process, which bears
    // a message pipe represented by `ipc_bootstrap_pipe`.
    channel_proxy_->Init(
        IPC::ChannelMojo::CreateClientFactory(
            std::move(ipc_bootstrap_pipe), /*ipc_task_runner=*/io_task_runner,
            /*proxy_task_runner=*/
            base::SingleThreadTaskRunner::GetCurrentDefault()),
        /*create_pipe_now=*/true);
  }

 private:
  // IPC::Listener implementation.
  bool OnMessageReceived(const IPC::Message& msg) override {
    LOG(WARNING) << "The renderer received a message";
    return true;
  }
  void OnAssociatedInterfaceRequest(
      const std::string& interface_name,
      mojo::ScopedInterfaceEndpointHandle handle) override {
    std::string tmp_name = interface_name;
    LOG(WARNING) << "The renderer received an associated interface request for "
                 << tmp_name.c_str();
    if (interface_name == "codelabs.mojom.ObjectA") {
      // Amazingly enough, if you comment out all of this code, which causes the
      // `ObjectA` interface to not get bound and therefore the `DoA()` message
      // to never be delivered, the `DoB()` message still gets delivered and
      // invoked on `ObjectB`. This is because channel-associated interface
      // messages are dispatched very differently than non-channel-associated
      // ones, because we can't block at all.
      mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectA> pending_a(
          std::move(handle));
      g_object_a.BindToFrozenTaskRunner(
          std::move(pending_a), std::move(initially_frozen_task_runner_));
    } else if (interface_name == "codelabs.mojom.ObjectB") {
      mojo::PendingAssociatedReceiver<codelabs::mojom::ObjectB> pending_b(
          std::move(handle));
      g_object_b.Bind(std::move(pending_b));
    }
  }

  std::unique_ptr<IPC::SyncChannel> channel_proxy_;
  scoped_refptr<base::SingleThreadTaskRunner> initially_frozen_tq_;
  base::WaitableEvent shutdown_event_{
      base::WaitableEvent::ResetPolicy::MANUAL,
      base::WaitableEvent::InitialState::NOT_SIGNALED};

  scoped_refptr<base::SingleThreadTaskRunner> initially_frozen_task_runner_;
};

int main(int argc, char** argv) {
  base::AtExitManager exit_manager;
  base::CommandLine::Init(argc, argv);
  LOG(INFO) << "Renderer: "
            << base::CommandLine::ForCurrentProcess()->GetCommandLineString();

  ProcessBootstrapper bootstrapper;
  // See the documentation above the corresponding "browser.cc".
  bootstrapper.InitMainThread(base::MessagePumpType::DEFAULT);
  bootstrapper.InitMojo(/*as_browser_process=*/false);

  // This is the task queue that `ObjectAImpl`'s receiver will be bound to. We
  // freeze it to demonstrate that channel-associated interfaces bound to frozen
  // queues *still* have their messages delivered.
  scoped_refptr<CustomTaskQueue> initially_frozen_tq =
      base::MakeRefCounted<CustomTaskQueue>(
          *bootstrapper.sequence_manager.get(),
          base::sequence_manager::TaskQueue::Spec(
              base::sequence_manager::QueueName::TEST_TQ));
  initially_frozen_tq->FreezeTaskQueue();

  // The rest of the magic happens in this object.
  std::unique_ptr<RendererIPCListener> renderer_ipc_listener =
      std::make_unique<RendererIPCListener>(
          /*io_task_runner=*/bootstrapper.io_task_runner,
          initially_frozen_tq->task_runner());

  // Post a task for 3 seconds from now that will unfreeze the TaskRunner that
  // the `codelabs::mojom::ObjectA` implementation is bound to. This would
  // normally block all messages from going to their corresponding
  // implementations (i.e., messages bound for ObjectA would be blocked, and
  // necessarily subsequent messages bound for ObjectB would *also* be blocked),
  // however since the associated interfaces here are specifically
  // *channel*-associated, we do not support blocking messages, so they're all
  // delivered immediately.
  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
      FROM_HERE,
      base::BindOnce(
          [](scoped_refptr<CustomTaskQueue> initially_frozen_tq) {
            LOG(INFO) << "Unfreezing frozen TaskRunner";
            initially_frozen_tq->UnfreezeTaskQueue();
          },
          initially_frozen_tq),
      base::Seconds(3));

  // This task is posted first, but will not run until the task runner is
  // unfrozen in ~3 seconds.
  initially_frozen_tq->task_runner()->PostTask(
      FROM_HERE, base::BindOnce([]() {
        LOG(WARNING) << "Renderer: This is the first task posted to the frozen "
                        "TaskRunner. It shouldn't run within the first 2 "
                        "seconds of the program";
      }));

  base::RunLoop run_loop;
  run_loop.Run();

  return 0;
}

四、编译

  1、gn gen out/debug

  2、 ninja -C out/debug 03-mojo-browser

      生成03-mojo-browser.exe

 3、ninja -C out/debug 03-mojo-renderer

       生成03-mojo-renderer.exe

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

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

相关文章

鸢尾博客项目开源

1.博客介绍 鸢尾博客是一个基于Spring BootVue3 TypeScript ViteJavaFx的客户端和服务器端的博客系统。项目采用前端与后端分离&#xff0c;支持移动端自适应&#xff0c;配有完备的前台和后台管理功能。后端使用Sa-Token进行权限管理,支持动态菜单权限&#xff0c;服务健康…

【模型学习之路】手写+分析bert

手写分析bert 目录 前言 架构 embeddings Bertmodel 预训练任务 MLM NSP Bert 后话 netron可视化 code2flow可视化 fine tuning 前言 Attention is all you need! 读本文前&#xff0c;建议至少看懂【模型学习之路】手写分析Transformer-CSDN博客。 毕竟Bert是tr…

word及Excel常见功能使用

最近一直在整理需规文档及表格&#xff0c;Word及Excel需要熟练使用。 Word文档 清除复制过来的样式 当复制文字时&#xff0c;一般会带着字体样式&#xff0c;此时可选中该文字 并使用 ctrlshiftN 快捷键进行清除。 批注 插入->批注&#xff0c;选中文本 点击“批注”…

【C++篇】数据之林:解读二叉搜索树的优雅结构与运算哲学

文章目录 二叉搜索树详解&#xff1a;基础与基本操作前言第一章&#xff1a;二叉搜索树的概念1.1 二叉搜索树的定义1.1.1 为什么使用二叉搜索树&#xff1f; 第二章&#xff1a;二叉搜索树的性能分析2.1 最佳与最差情况2.1.1 最佳情况2.1.2 最差情况 2.2 平衡树的优势 第三章&a…

【Mac】安装 VMware Fusion Pro

VMware Fusion Pro 软件已经正式免费提供给个人用户使用&#xff01; 1、下载 【官网】 下拉找到 VMware Fusion Pro Download 登陆账号 如果没有账号&#xff0c;点击右上角 LOGIN &#xff0c;选择 REGISTER 注册信息除了邮箱外可随意填写 登陆时&#xff0c;Username为…

文心一言 VS 讯飞星火 VS chatgpt (383)-- 算法导论24.5 3题

三、对引理 24.10 的证明进行改善&#xff0c;使其可以处理最短路径权重为 ∞ ∞ ∞ 和 − ∞ -∞ −∞ 的情况。引理 24.10(三角不等式)的内容是&#xff1a;设 G ( V , E ) G(V,E) G(V,E) 为一个带权重的有向图&#xff0c;其权重函数由 w : E → R w:E→R w:E→R 给出&…

Linux 服务器使用指南:从入门到登录

&#x1f31f;快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 &#x1f31f; &#x1f6a9;博主致力于用通俗易懂且不失专业性的文字&#xff0c;讲解计算机领域那些看似枯燥的知识点&#x1f6a9; 目录 一…

【Maven】——基础入门,插件安装、配置和简单使用,Maven如何设置国内源

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 引入&#xff1a; 一&#xff1a;Maven插件的安装 1&#xff1a;环境准备 2&#xff1a;创建项目 二…

数据库基础(2) . 安装MySQL

0.增加右键菜单选项 添加 管理员cmd 到鼠标右键 运行 reg文件 在注册表中添加信息 这样在右键菜单中就有以管理员身份打开命令行的选项了 1.获取安装程序 网址: https://dev.mysql.com/downloads/mysql/ 到官网下载MySQL8 的zip包, 然后解压 下载后的包为: mysql-8.0.16-…

cocos开发QA

目录 TS相关foreach循环中使用return循环延迟动态获取类属性 Cocos相关属性检查器添加Enum属性使用Enum报错 枚举“XXX”用于其声明前实现不规则点击区域使用cc.RevoluteJoint的enable激活组件无效本地存储以及相关问题JSON.stringify(map)返回{}数据加密客户端复制文本使用客户…

flutter区别于vue的写法

View.dart 页面渲染&#xff1a; 类似于vue里面使用 <template> <div> <span> <textarea>等标签绘制页面, flutter 里面则是使用不同的控件来绘制页面 样式 与传统vue不同的是 flutter里面没有css/scss样式表&#xff0c; Flutter的理念是万物皆…

DICOM标准:DICOM标准中的公用模块、核心模块详解(一)——病人、研究、序列、参考帧和设备模块属性详解

目录 概述 1 公用病人IE模块 1.1 病人模块 2 公用的研究IE模块 2.1 常规研究模块 2.2 病人研究模块 3 公用序列IE模块 3.1 常规序列模块 3.1.1 常规序列属性描述 4 公用参考帧信息实体模块 4.1 参考帧模块 4.1.1 参考帧属性描述 5 公用设备IE模块 5.1 常规设备模…

轻松搞定项目管理!用对在线项目管理工具助你生产力翻倍!

一、引言 在线项目管理是指借助互联网平台和相关软件工具&#xff0c;对项目从启动到结束的全过程进行规划、组织、协调、控制和监督的一种管理方式。它打破了传统项目管理在时间和空间上的限制&#xff0c;使得项目团队成员无论身处何地&#xff0c;都能实时同步项目信息、协…

ERP研究 | 颜值美和道德美,哪个更重要?

摘要 道德美和颜值美都会影响我们的评价。在这里&#xff0c;本研究采用事件相关电位(ERPs)技术探讨了道德美和颜值美如何交互影响社会判断和情感反应。参与者(均为女性)将积极、中性或消极的言语信息与高吸引力或低吸引力面孔进行关联&#xff0c;并对这些面孔进行评分&#…

【Linux】从零开始使用多路转接IO --- epoll

当你偶尔发现语言变得无力时&#xff0c; 不妨安静下来&#xff0c; 让沉默替你发声。 --- 里则林 --- 从零开始认识多路转接 1 epoll的作用和定位2 epoll 的接口3 epoll工作原理4 实现epollserverV1 1 epoll的作用和定位 之前提过的多路转接方案select和poll 都有致命缺点…

利用 Feather 格式加速数据科学工作流:Pandas 中的最佳实践

利用 Feather 格式加速数据科学工作流&#xff1a;Pandas 中的最佳实践 在数据科学中&#xff0c;高效的数据存储和传输对于保持分析流程的流畅性至关重要。传统的 CSV 格式虽然通用&#xff0c;但在处理大规模数据集时速度较慢&#xff0c;特别是在反复读取和写入时。幸运的是…

[极客大挑战 2019]BabySQL 1

[极客大挑战 2019]BabySQL 1 审题 还是SQL注入和之前的是一个系列的。 知识点 联合注入&#xff0c;双写绕过 解题 输入万能密码 发现回显中没有or&#xff0c;猜测是使用正则过滤了or。 尝试双写绕过 登录成功 使用联合查询&#xff0c;本题中过滤了from&#xff0c;w…

全面解析:大数据技术及其应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 全面解析&#xff1a;大数据技术及其应用 全面解析&#xff1a;大数据技术及其应用 全面解析&#xff1a;大数据技术及其应用 大…

七次课掌握 Photoshop:基础与入门

Photoshop 是 Adobe 公司开发的功能强大的图像处理软件&#xff0c;被广泛应用于平面设计、网页设计、摄影后期处理、UI 设计等多个领域。 ◆ ◆ ◆ Photoshop 中的核心概念 一、像素 像素&#xff08;Pixel&#xff09;是组成数字图像的基本单位&#xff0c;如同组成人体的细…

G2 基于生成对抗网络(GAN)人脸图像生成

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 基于生成对抗网络&#xff08;GAN&#xff09;人脸图像生成 这周将构建并训练一个生成对抗网络&#xff08;GAN&#xff09;来生成人脸图像。 GAN 原理概述 …