C#图像处理OpenCV开发指南(CVStar,04)——图片像素访问与多种局部放大效果的实现代码

news2024/10/6 12:33:34

​​​​​​​

使用本文代码需要预先设置一点开发环境,请阅读另外一篇博文:

C#图像处理OpenCV开发指南(CVStar,03)——基于.NET 6的图像处理桌面程序开发实践第一步icon-default.png?t=N7T8https://blog.csdn.net/beijinghorn/article/details/134684471?spm=1001.2014.3001.5502

1 功能需求

(1)读取图片;并显示于窗口;可缩放;

(2)可访问图片指定位置像素信息;

(3)提取局部图片(ROI)

(4)有 拾取框;

(5)局部放大(连续型放大);

(6)支持 大像素 型得局部放大;

(7)支持 圆形 像素的局部放大;

2 代码分段讲解

前面一个博文中出现过的代码不再赘述。

2.1 访问图片指定位置像素信息

Vec3b cx = zmt.At<Vec3b>(y, x);

y,x 指定的位置;顺序不要搞错啦! 

返回的数据类型 Vec3b 是 byte[] 类型的;BGR顺序;cx[0] 为 Blue,cx[1] 为 Green;cx[2] 为 Red;这个与一般的图片信息 RGB 顺序不同;

2.2 提取局部图片(ROI)

Mat zmt = new Mat(src, new Rect(gx, gy, gw, gh));

gx,gy 为左上角坐标;

gw,gh 为局部图片的宽度与高度(像素)。

2.3 拾取框绘制

int gw = picResult.Width / PixelSize;
int gh = picResult.Height / PixelSize;
Bitmap bmp = new Bitmap(picSource.Width, picSource.Height);
Graphics g = Graphics.FromImage(bmp);
g.DrawImage(
    image: img,
    destRect: new Rectangle(0, 0, bmp.Width, bmp.Height),
    srcX: 0,
    srcY: 0,
    srcWidth: img.Width,
    srcHeight: img.Height,
    srcUnit: GraphicsUnit.Pixel);
g.DrawRectangle(new Pen(Color.Yellow), mouseAtX - gw / 2, mouseAtY - gh / 2, gw, gh);
picSource.Image = bmp;

2.4 光滑的(插值的)局部放大

图片的局部放大是取局部图片,放大显示。或者直接将图片放大显示并进行移位。

非 OpenCV 方式:

Bitmap bmp = new Bitmap(picResult.Width, picResult.Height);
Graphics g = Graphics.FromImage(bmp);
g.DrawImage(
    image: picSource.Image,
    destRect: new Rectangle(0, 0, bmp.Width, bmp.Height),
    srcX: gx,
    srcY: gy,
    srcWidth: gw,
    srcHeight: gh,
    srcUnit: GraphicsUnit.Pixel);
picResult.Image = bmp;

OpenCV方式

Mat zmt = new Mat(src, new Rect(gx, gy, gw, gh));
picResult.Image = CVUtility.Mat2Bitmap(zmt);
PicAutosize(picResult);

2.5 非插值的大像素型放大

非插值的大像素型放大是指将原图的每个像素放大为一个矩形(块)显示。

Mat zmt = new Mat(src, new Rect(gx, gy, gw, gh));
Bitmap bmp = new Bitmap(picResult.Width, picResult.Height);
Graphics g = Graphics.FromImage(bmp);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
for (int y = 0; y < gh; y++)
{
    for (int x = 0; x < gw; x++)
    {
        Vec3b cx = zmt.At<Vec3b>(y, x);
        SolidBrush sb = new SolidBrush(Color.FromArgb(cx[2], cx[1], cx[0]));
        g.FillRectangle(sb, new Rectangle(x * PixelSize, y * PixelSize, PixelSize, PixelSize));
    }
}
picResult.Image = bmp;

2.6 圆圈型像素放大算法

圆圈型像素放大算法是指将原图的每个像素放大为一个圆圈(派)显示。

Mat zmt = new Mat(src, new Rect(gx, gy, gw, gh));
Bitmap bmp = new Bitmap(picResult.Width, picResult.Height);
Graphics g = Graphics.FromImage(bmp);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
for (int y = 0; y < gh; y++)
{
    for (int x = 0; x < gw; x++)
    {
        Vec3b cx = zmt.At<Vec3b>(y, x);
        SolidBrush sb = new SolidBrush(Color.FromArgb(cx[2], cx[1], cx[0]));
        g.FillEllipse(sb, new Rectangle(x * PixelSize, y * PixelSize, PixelSize, PixelSize));
    }
}
picResult.Image = bmp;

2.7 鼠标事件与拾取框

private void PicSource_MouseMove(object? sender, MouseEventArgs? e)
{
    if (picSource == null) { return; }
    if (picSource.Image == null) { return; }
    mouseAtX = e.X;
    mouseAtY = e.Y;
    abLocation.Text = mouseAtX + "," + mouseAtY + " " + (int)(mouseAtX / original_scale) + "," + (int)(mouseAtY / original_scale);

    int gw = picResult.Width / PixelSize;
    int gh = picResult.Height / PixelSize;
    Bitmap bmp = new Bitmap(picSource.Width, picSource.Height);
    Graphics g = Graphics.FromImage(bmp);
    g.DrawImage(
        image: img,
        destRect: new Rectangle(0, 0, bmp.Width, bmp.Height),
        srcX: 0,
        srcY: 0,
        srcWidth: img.Width,
        srcHeight: img.Height,
        srcUnit: GraphicsUnit.Pixel);
    g.DrawRectangle(new Pen(Color.Yellow), mouseAtX - gw / 2, mouseAtY - gh / 2, gw, gh);
    picSource.Image = bmp;

    Zoom();
}

3 运行效果

3.1 一般的插值放大算法效果

3.2 块状大像素放大效果

3.3 圆圈像素放大效果

 

4 完整的源代码

无需做修改,界面设计,直接复制粘贴替换原来的 Form1.cs 即可。

using OpenCvSharp;

#pragma warning disable CS8602

namespace Legal.Truffer.CVStar
{
    public partial class Form1 : Form
    {
        string[] ImgExtentions = {
            "*.*|*.*",
            "JPEG|*.jpg;*.jpeg",
            "GIF|*.gif",
            "PNG|*.png",
            "TIF|*.tif;*.tiff",
            "BMP|*.bmp"
        };
        private int original_width { get; set; } = 0;
        private int original_height { get; set; } = 0;
        private double original_scale { get; set; } = 0.0;
        private string sourceImage { get; set; } = "";

        private int PixelSize { get; set; } = 15;
        private int mouseAtX { get; set; } = 0;
        private int mouseAtY { get; set; } = 0;

        Panel? panelTop { get; set; } = null;
        Panel? panelBotton { get; set; } = null;
        PictureBox? picSource { get; set; } = null;
        PictureBox? picResult { get; set; } = null;
        Button? btnLoad { get; set; } = null;

        RadioButton? rbSmooth { get; set; } = null;
        RadioButton? rbBlock { get; set; } = null;
        RadioButton? rbPie { get; set; } = null;

        Label? abLocation { get; set; }

        Image? img { get; set; } = null;
        Mat? src { get; set; } = null;

        public Form1()
        {
            InitializeComponent();

            this.Text = "OPENCV C#编程入手教程 POWERED BY 深度混淆(CSDN.NET)";
            this.StartPosition = FormStartPosition.CenterScreen;

            GUI();
            this.Resize += FormResize;
            this.DoubleBuffered = true;
        }

        private void FormResize(object? sender, EventArgs? e)
        {
            if (this.Width < 200) { this.Width = 320; return; }
            if (this.Height < 200) { this.Height = 320; return; }
            mouseAtX = 0;
            mouseAtY = 0;
            GUI();
        }

        private void GUI()
        {
            if (panelTop == null) panelTop = new Panel();
            panelTop.Parent = this;
            panelTop.Top = 5;
            panelTop.Left = 5;
            panelTop.Height = 75;
            panelTop.Dock = DockStyle.Top;
            panelTop.BorderStyle = BorderStyle.FixedSingle;
            panelTop.BackColor = Color.FromArgb(220, 220, 255);

            if (panelBotton == null) panelBotton = new Panel();
            panelBotton.Parent = this;
            panelBotton.Top = panelTop.Top + panelTop.Height + 1;
            panelBotton.Left = 0;
            panelBotton.Width = panelTop.Width;
            panelBotton.Height = this.Height - panelBotton.Top - 55;
            panelBotton.BorderStyle = BorderStyle.FixedSingle;

            if (picSource == null) picSource = new PictureBox();
            picSource.Parent = panelBotton;
            picSource.Left = 5;
            picSource.Top = 5;
            picSource.Width = (panelBotton.Width - 10) / 2;
            picSource.Height = (panelBotton.Height - 10);
            picSource.BorderStyle = BorderStyle.FixedSingle;
            picSource.MouseMove += PicSource_MouseMove;
            picSource.DoubleClick += Load_Image;

            original_width = picSource.Width;
            original_height = picSource.Height;

            if (picResult == null) picResult = new PictureBox();
            picResult.Parent = panelBotton;
            picResult.Left = picSource.Left + picSource.Width + 5;
            picResult.Top = picSource.Top;
            picResult.Width = picSource.Width - (picSource.Width % PixelSize);
            picResult.Height = picResult.Width;
            picResult.BorderStyle = BorderStyle.FixedSingle;

            if (btnLoad == null) btnLoad = new Button();
            btnLoad.Parent = panelTop;
            btnLoad.Left = 5;
            btnLoad.Top = 5;
            btnLoad.Width = 90;
            btnLoad.Height = 38;
            btnLoad.Cursor = Cursors.Hand;
            btnLoad.Text = "Load";
            btnLoad.BackColor = Color.FromArgb(255, 110, 0);
            btnLoad.Click += Load_Image;

            if (rbSmooth == null) rbSmooth = new RadioButton();
            rbSmooth.Parent = panelTop;
            rbSmooth.Left = btnLoad.Left + btnLoad.Width + 5;
            rbSmooth.Top = btnLoad.Top + 5;
            rbSmooth.Text = "smooth";
            rbSmooth.Cursor = Cursors.Hand;
            rbSmooth.Checked = true;

            if (rbBlock == null) rbBlock = new RadioButton();
            rbBlock.Parent = panelTop;
            rbBlock.Left = rbSmooth.Left + rbSmooth.Width + 5;
            rbBlock.Top = btnLoad.Top + 5;
            rbBlock.Text = "block";
            rbBlock.Cursor = Cursors.Hand;

            if (rbPie == null) rbPie = new RadioButton();
            rbPie.Parent = panelTop;
            rbPie.Left = rbBlock.Left + rbBlock.Width + 5;
            rbPie.Top = btnLoad.Top + 5;
            rbPie.Text = "pie";
            rbPie.Cursor = Cursors.Hand;

            if (abLocation == null) abLocation = new Label();
            abLocation.Parent = panelTop;
            abLocation.Left = btnLoad.Left;
            abLocation.Top = btnLoad.Top + btnLoad.Height + 5;
            abLocation.Text = mouseAtX + "," + mouseAtY;

            PicAutosize(picSource);
            Zoom();
        }

        private void Load_Image(object? sender, EventArgs? e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = String.Join("|", ImgExtentions);
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                sourceImage = openFileDialog.FileName;
                img = Image.FromFile(sourceImage);
                picSource.Image = img;
                src = Cv2.ImRead(sourceImage);
                PicAutosize(picSource);
                Zoom();
            }
        }

        private void PicAutosize(PictureBox pb)
        {
            if (pb == null) return;
            if (pb.Image == null) return;
            Image img = pb.Image;
            int w = original_width;
            int h = w * img.Height / img.Width;
            if (h > original_height)
            {
                h = original_height;
                w = h * img.Width / img.Height;
            }
            if (pb == picSource)
            {
                original_scale = (double)w / (double)src.Width;
            }
            pb.SizeMode = PictureBoxSizeMode.Zoom;
            pb.Width = w;
            pb.Height = h;
            pb.Image = img;
            pb.Refresh();
        }

        private void Zoom()
        {
            if (picSource == null) return;
            if (picSource.Image == null) return;

            // ROI
            int gw = picResult.Width / PixelSize;
            int gh = picResult.Height / PixelSize;
            int gx = (int)(mouseAtX / original_scale) - gw / 2;
            if (gx < 0) gx = 0;
            int gy = (int)(mouseAtY / original_scale) - gh / 2;
            if (gy < 0) gy = 0;

            if (rbSmooth.Checked)
            {
#if GRAPHICS_METHOD
                Bitmap bmp = new Bitmap(picResult.Width, picResult.Height);
                Graphics g = Graphics.FromImage(bmp);
                g.DrawImage(
                    image: picSource.Image,
                    destRect: new Rectangle(0, 0, bmp.Width, bmp.Height),
                    srcX: gx,
                    srcY: gy,
                    srcWidth: gw,
                    srcHeight: gh,
                    srcUnit: GraphicsUnit.Pixel);
                picResult.Image = bmp;
#else
                Mat zmt = new Mat(src, new Rect(gx, gy, gw, gh));
                picResult.Image = CVUtility.Mat2Bitmap(zmt);
                PicAutosize(picResult);
#endif
            }
            else if (rbBlock.Checked)
            {
                Mat zmt = new Mat(src, new Rect(gx, gy, gw, gh));
                Bitmap bmp = new Bitmap(picResult.Width, picResult.Height);
                Graphics g = Graphics.FromImage(bmp);
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                for (int y = 0; y < gh; y++)
                {
                    for (int x = 0; x < gw; x++)
                    {
                        Vec3b cx = zmt.At<Vec3b>(y, x);
                        SolidBrush sb = new SolidBrush(Color.FromArgb(cx[2], cx[1], cx[0]));
                        g.FillRectangle(sb, new Rectangle(x * PixelSize, y * PixelSize, PixelSize, PixelSize));
                    }
                }
                picResult.Image = bmp;
            }
            else if (rbPie.Checked)
            {
                Mat zmt = new Mat(src, new Rect(gx, gy, gw, gh));
                Bitmap bmp = new Bitmap(picResult.Width, picResult.Height);
                Graphics g = Graphics.FromImage(bmp);
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                for (int y = 0; y < gh; y++)
                {
                    for (int x = 0; x < gw; x++)
                    {
                        Vec3b cx = zmt.At<Vec3b>(y, x);
                        SolidBrush sb = new SolidBrush(Color.FromArgb(cx[2], cx[1], cx[0]));
                        g.FillEllipse(sb, new Rectangle(x * PixelSize, y * PixelSize, PixelSize, PixelSize));
                    }
                }
                picResult.Image = bmp;
            }
        }

        private void PicSource_MouseMove(object? sender, MouseEventArgs? e)
        {
            if (picSource == null) { return; }
            if (picSource.Image == null) { return; }
            mouseAtX = e.X;
            mouseAtY = e.Y;
            abLocation.Text = mouseAtX + "," + mouseAtY + " " + (int)(mouseAtX / original_scale) + "," + (int)(mouseAtY / original_scale);

            int gw = picResult.Width / PixelSize;
            int gh = picResult.Height / PixelSize;
            Bitmap bmp = new Bitmap(picSource.Width, picSource.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.DrawImage(
                image: img,
                destRect: new Rectangle(0, 0, bmp.Width, bmp.Height),
                srcX: 0,
                srcY: 0,
                srcWidth: img.Width,
                srcHeight: img.Height,
                srcUnit: GraphicsUnit.Pixel);
            g.DrawRectangle(new Pen(Color.Yellow), mouseAtX - gw / 2, mouseAtY - gh / 2, gw, gh);
            picSource.Image = bmp;

            Zoom();
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UWB高精度定位系统项目源码

在现代社会中&#xff0c;精准定位技术对于各行各业都至关重要。为了满足对高精度定位的需求&#xff0c;超宽带&#xff08;Ultra-Wideband, UWB&#xff09;技术应运而生。UWB高精度定位系统以其出色的定位精度和多样化的应用领域而备受关注。本文将深入探讨UWB高精度定位系统…

ntopng如何将漏洞扫描与流量监控相结合,以提高网络安全性

来源&#xff1a;艾特保IT 虹科干货 | ntopng如何将漏洞扫描与流量监控相结合&#xff0c;以提高网络安全性 欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; ntopng为人所知的“身份”是被动流量监控。然而&#xff0c;如今的ntopng6.0也进化出主动监控功能来&#xf…

springboot自定义校验注解的实现

自定义校验注解的实现 通过谷粒商城项目学习了自定义校验器的实现一、编写自定义校验注解二、自定义注解的校验器三、关联自定义的校验器和自定义的校验注解总结 通过谷粒商城项目学习了自定义校验器的实现 近日在学习雷神的谷粒商城项目&#xff0c;其中有一个自定义校验的实…

力扣日记11.30-【二叉树篇】平衡二叉树

力扣日记&#xff1a;【二叉树篇】平衡二叉树 日期&#xff1a;2023.11.30 参考&#xff1a;代码随想录、力扣 110. 平衡二叉树 题目描述 难度&#xff1a;简单 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#…

【LeetCode】每日一题 2023_11_29 无限集中的最小数字(哈希/堆)

文章目录 刷题前唠嗑题目&#xff1a;无限集中的最小数字题目描述代码与解题思路偷看大佬题解 结语 刷题前唠嗑 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 今天的题目也比较的简单&#xff0c;因为数据量不大&#xff0c;所以什么做法都能过的去 题目&a…

java 对象大小计算

说明&#xff1a; 对于64位机&#xff1a;一个对象由三部分组成 对象头(object header) mark word &#xff1a;64bitkclass pointer &#xff1a;32bit(默认使用指针压缩)&#xff0c;如果取消指针压缩( XX:-UseCompressedOops)&#xff0c;则占用64bit数组长度&#xff1a;数…

Linux:docker镜像的创建(5)

1.基于已有镜像创建 步骤&#xff1a; 1.将原始镜像加入容器并运行 2.在原始镜像中部署各种服务 3.退出容器 4.使用下面命令将容器生成新的镜像 现在我们在这个容器里做了一些配置&#xff0c;我们要把他做成自己镜像 docker commit -m "centos7_123" -a "tarr…

ArkTS-DevEco Studio打开预览器报错

下载官网Codelab案例&#xff0c;打开预览器报错 Failed to start the service process. Make sure the path specified by nodejs.dir in the local.properties file is correct. 解决方案 在编辑器设置中找到node安装路径 将" local.properties"文件中的"nod…

老泮识趣:难忘何家桥

回忆何家桥往事&#xff0c;写了篇《消失的何家桥》&#xff0c;没想到点击率如此高&#xff0c;出乎意料。网友的共鸣可见&#xff0c;城市发展的今天&#xff0c;乡情是个美好的存在&#xff0c;清贫、朴实&#xff0c;丝毫不影响美感。由于大家的鼓励&#xff0c;触动了我再…

电力变压器行业分析:预计2029年将达到84亿元

随着中国“节能降耗”政策的不断深入&#xff0c;国家鼓励发展节能型、低噪音、智能化的配电变压器产品。在网运行的部分高能耗配电变压器已不符合行业发展趋势&#xff0c;面临着技术升级、更新换代的需求&#xff0c;未来将逐步被节能、节材、环保、低噪音的变压器所取代。 电…

PyBullet安装与学习

PyBullet 支持加载 URDF、SDF、MJCF 等多种机器人描述文件&#xff0c;并提供正/逆向运动学、正/逆向动力学、碰撞检测、射线相交查询等功能。 pip install pybullet 安装后会在 Python 环境的 lib/site-packages 中出现以下文件夹&#xff1a; pybullet_data&#xff1a;存放…

VERAS:AI驱动的Revit可视化渲染插件

Veras 是一款基于生成式AI 的可视化工具&#xff0c;可以使用自然语言生成3D渲染效果&#xff0c;兼容Revit、Rhino 和 SketchUp。Veras for Revit工具使用 Revit 模型内部的 3D 视图。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编…

Spine深入学习 —— 换装

Spine深入学习————换装 数据对象和实例对象的关系与区别 数据对象是无状态的&#xff0c;可在任意数量的骨架实例间共用。有对应实例数据的数据对象类名称以“Data”结尾&#xff0c;没有对应实例数据的数据对象则没有后缀&#xff0c;如附件、皮肤及动画。 实例对象有许…

【FMC140】 基于VITA57.4标准的双通道5.2GSPS(或单通道10.4GSPS)射频采样FMC+子卡模块

板卡概述 FMC140是一款具有缓冲模拟输入的低功耗、12位、双通道&#xff08;5.2GSPS/通道&#xff09;、单通道10.4GSPS、射频采样ADC模块&#xff0c;该板卡为FMC标准&#xff0c;符合VITA57.1规范&#xff0c;该模块可以作为一个理想的IO单元耦合至FPGA前端&#xff0c;8通道…

【嵌入式Linux开发一路清障-连载04】虚拟机VirtualBox7.0安装Ubuntu22.04后挂载Windows平台共享文件夹

虚拟机安装Ubuntu22.04后挂载Windows平台共享文件夹 障碍07-虚拟机VirtualBox7.0完装完Ubuntu22.04后&#xff0c;无法成功挂载Windows平台中共享文件夹&#xff0c;无法访问电脑中的各类重要文件&#xff0c;我该怎么办&#xff1f;一、问题的模样&#xff1a;VirtualBox7.0设…

Python异常处理:try...except语句

Python是一门非常灵活且易于学习的编程语言&#xff0c;在日常开发中被广泛应用。然而&#xff0c;由于各种原因&#xff0c;我们的代码可能会出现异常情况&#xff0c;例如输入错误、文件读写异常等等。Python异常处理是Python中重要的一部分&#xff0c;为了保证程序的稳定性…

IPv6地址介绍

当前我国的网络正在快速向IPv6升级中&#xff0c;从网络基础设施如运营商骨干网、城域网&#xff0c;到互联网服务商如各类云服务&#xff0c;以及各类终端设备厂商如手机、电脑、路由器、交换机等。 一、网络IP地址 IP地址是英文Internet Protocol的缩写&#xff0c;是网络之…

Fiddler 抓包高级进阶

Fiddler 抓包高级进阶 安装直接下载我附件提供的,中文版的,可以直接使用。如果不能使用就安装.NET 框架。 HTTPS配置工具》》选项: https 都配置上。 配置HTTS证书,然后动作》》 Trust Root Certificate 。 一路确认或者 是,就行了。

【C++】程序题( STL标准模板库)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

dbCAN碳水化合物酶基因数据库及run_dbCAN4工具安装配置及使用

dbCAN&#xff08;碳水化合物酶基因数据库&#xff09;是一个专门用于在基因组中预测碳水化合物酶基因的在线工具。它基于隐马尔可夫模型&#xff08;HMM&#xff09;和BLAST搜索&#xff0c;能够在蛋白质序列中识别和注释不同类型的碳水化合物酶基因&#xff0c;包括纤维素酶、…