C#按边框切检验仪器图

news2024/11/16 2:50:38

最近碰到一个检验设备是生成PDF文件报告的。imedicallis监听程序把PDF解析出来之后发现PDF里面图不是多个小图,而是一张大图。但用户又要传到检验系统的是小图,而且小图位置和数量不固定,也不能用固定位置截取实现。为此开启一段“高端设备局”之旅。以前咱取过图、还原过图、按数据和约定画过图,从来没按边框切过图。

仪器源PDF如下,里面是一张大图,监听已经可以得到一页页的大图了。
在这里插入图片描述

现在需要把他的小图边界分析出来并切割。我的思路是:
1.先把图片二极化,出来成纯黑白图。然后得到01和二维数组值。
2.遍历二维数组找到所有长度大于指定值的线段。
3.遍历线段得到所有矩形。
4.去除矩形面向叠加超过百分之九十的矩形。
5.在矩形基础每次给每个边外扩指定像素。然后检查二维数组里外扩后矩形边上有没有像素在。从开始没像素或有像素到外扩到无像素即到了文字边界。这里就是每个小图的边界。
6.按外扩的边框切割得到目标图片。

1.原始图
在这里插入图片描述

2.二极化得到黑白图
在这里插入图片描述

3.从两级化图得到二维数组
在这里插入图片描述

4.变量数组找到所有目标线段并且画出来看对不对
在这里插入图片描述

5.从所有线段算出所有矩形,并且画出来看对不对
在这里插入图片描述

6.把矩形进行面积叠加计算去除重复的,然后画剩余的矩形看对不对
在这里插入图片描述

7.按矩形外扩法推断得到文字边界,并且把外扩矩形画出来看对不对
在这里插入图片描述

8.按外扩矩形切割得到小图
在这里插入图片描述

至此就实现了PDF解析的整个大图切割得到小图了。C#实现算法逻辑如下:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;

namespace ImageCutTest
{
    public class RectCutImageUtil
    {
        /// <summary>
        /// 按边框切图
        /// </summary>
        /// <param name="path"></param>
        public static void CutImage(string path)
        {
            //加载图片
            Image img = Image.FromFile(path);
            //先把图片二极化,只有黑白
            Bitmap step1bmp = ConvertToBipolar(img as Bitmap);
            string step1Path = path + "step1.bmp";
            step1bmp.Save(step1Path);
            //得到二极化值后的数组
            List<List<int>> numList = GetNumberMap(step1bmp);
            string txtPath = path + "step2.txt";
            //输出值到文本
            OutPutValueToTxt(numList, txtPath);
            //得到所有大于90的线条
            List<LineCls> lineList = GetAllLine(numList, 80);
            string lineImgPath = path + "step3.bmp";
            //画出找到的线条
            ShowLine(step1bmp, lineList, lineImgPath);
            //通过线算出矩形
            List<Rectangle> recList = GetRectangle(lineList, 60);
            string rectImgPath = path + "step4.bmp";
            //画出找到的矩形
            ShowRectangle(step1bmp, recList, rectImgPath);
            //删除重叠的矩形
            DeleteOverlapRect(recList);
            string rectImgPath1 = path + "step5.bmp";
            //画出找到的矩形,已经矩形去重了
            ShowRectangle(step1bmp, recList, rectImgPath1);
            //把矩形外扩到文字范围
            List<Rectangle> newRectList = OuterToChar(recList, numList, 8);
            string rectImgPath2 = path + "step6.bmp";
            //画出找到的矩形,已经矩形去重了
            ShowRectangle(step1bmp, newRectList, rectImgPath2);
            //按矩形切割小图
            CutRectImage(img as Bitmap, newRectList, path);
        }

        /// <summary>
        /// 在原图上截图出指定区域的小图
        /// </summary>
        /// <param name="source"></param>
        /// <param name="recList"></param>
        private static void CutRectImage(Bitmap source, List<Rectangle> recList, string oldFilePath)
        {
            FileInfo fi = new FileInfo(oldFilePath);
            string dirPath = fi.Directory.FullName;
            for(int i=0;i<recList.Count;i++)
            {
                Bitmap bmp = new Bitmap(recList[i].Width, recList[i].Height);
                Graphics g = Graphics.FromImage(bmp);
                g.Clear(Color.White);
                g.DrawImage(source, -recList[i].Left, -recList[i].Top);
                bmp.Save(oldFilePath+"-imedicalliscut-"+i+".bmp");
            }
        }

        /// <summary>
        /// 把矩形外扩到文字,返回包含文字的矩形
        /// </summary>
        /// <param name="recList"></param>
        /// <param name="numList"></param>
        /// <param name="maxOuter"></param>
        /// <returns></returns>
        private static List<Rectangle> OuterToChar(List<Rectangle> recList, List<List<int>> numList, int maxOuter)
        {
            List<Rectangle> ret = new List<Rectangle>();
            for (int i = 0; i < recList.Count; i++)
            {
                bool start = false;
                int topAdd = 0;
                int rightAdd = 0;
                int buttomAdd = 0;
                int leftAdd = 0;
                for (int j = 1; j < maxOuter; j++)
                {
                    topAdd = 3 * j;
                    rightAdd = 1 * j;
                    buttomAdd = 5 * j;
                    leftAdd = 5 * j;
                    bool hasPixel = OuterToRectHasPixel(recList[i], numList, topAdd, rightAdd, buttomAdd, leftAdd);
                    if (hasPixel == true && start == false)
                    {
                        start = true;
                    }
                    if (start == true && hasPixel == false)
                    {
                        break;
                    }
                }
                Rectangle newRec = new Rectangle(recList[i].Left - leftAdd, recList[i].Top - topAdd, recList[i].Width + leftAdd + rightAdd, recList[i].Height + topAdd + buttomAdd);
                ret.Add(newRec);
            }
            return ret;
        }

        /// <summary>
        /// 外扩到文字,每次扩展矩形边框特定像素,看对应变上是否有像素值。从没像素或有像素落在四条边上到没像素落在四个边就找到文字边界了
        /// 该方法检查外扩之后是否有像素落在四个边,有就返回true,没有就返回false
        /// </summary>
        /// <param name="rect"></param>
        private static bool OuterToRectHasPixel(Rectangle rect, List<List<int>> numList, int topAdd, int rightAdd, int buttomAdd, int leftAdd)
        {
            bool ret = false;
            int left = rect.Left - leftAdd;
            int top = rect.Top - topAdd;
            int width = rect.Width + leftAdd + rightAdd;
            int height = rect.Height + topAdd + buttomAdd;
            int maxX = left + width;
            int maxY = top + height;
            //外扩之后还没出图形范围就判断是否有像素落在边框
            if (left > 0 && top > 0 && maxX < numList[0].Count && maxY < numList.Count)
            {
                //判断是否有像素落在横线上
                for (int i = left; i <= maxX; i++)
                {
                    if (numList[top][i] == 1)
                    {
                        ret = true;
                        break;
                    }
                    if (numList[maxY][i] == 1)
                    {
                        ret = true;
                        break;
                    }
                }
                //判断是否有像素落在横线上
                for (int i = top; i <= maxY; i++)
                {
                    if (numList[i][left] == 1)
                    {
                        ret = true;
                        break;
                    }
                    if (numList[i][maxX] == 1)
                    {
                        ret = true;
                        break;
                    }
                }
            }
            return ret;
        }

        /// <summary>
        /// 删除重叠的矩形
        /// </summary>
        /// <param name="recList"></param>
        private static void DeleteOverlapRect(List<Rectangle> recList)
        {
            int len = recList.Count;
            if (len > 1)
            {
                for (int i = 0; i < len - 1; i++)
                {
                    for (int j = i + 1; j < len; j++)
                    {
                        float rate = OverlapRate(recList[i].X, recList[i].Y, recList[i].Height, recList[i].Width, recList[j].X, recList[j].Y, recList[j].Height, recList[j].Width);
                        if (rate > 0.92)
                        {
                            recList.RemoveAt(j);
                            j--;
                            len--;
                        }
                    }
                }
            }

        }

        /// <summary>
        /// 计算两个矩形框的重叠比率。参数:x、y为矩阵左下角坐标值,h、w为矩形的高和宽
        /// </summary>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="h1"></param>
        /// <param name="w1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <param name="h2"></param>
        /// <param name="w2"></param>
        /// <returns></returns>
        private static float OverlapRate(int x1, int y1, int h1, int w1, int x2, int y2, int h2, int w2)
        {
            int endx = Math.Max(x1 + w1, x2 + w2);
            int startx = Math.Min(x1, x2);
            //重叠部分宽
            int width = w1 + w2 - (endx - startx);
            int endy = Math.Max(y1 + h1, y2 + h2);
            int starty = Math.Min(y1, y2);
            //重叠部分高
            int height = h1 + h2 - (endy - starty);
            if (width > 0 && height > 0)
            {
                //重叠部分面积
                int area = width * height;
                int area1 = w1 * h1;
                int area2 = w2 * h2;
                float ratio = (float)area / (area1 + area2 - area);
                return ratio;
            }
            else
            {
                // 不重叠:算出来的width或height小于等于0
                return 0;
            }
        }


        /// <summary>
        /// 在黑白图上画出找到的矩形
        /// </summary>
        /// <param name="source"></param>
        /// <param name="lineList"></param>
        /// <param name="savepath"></param>
        private static void ShowRectangle(Bitmap source, List<Rectangle> recList, string savepath)
        {
            Bitmap bmp = new Bitmap(source.Width, source.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.Clear(Color.White);
            g.DrawImage(source, 0, 0);
            Pen pen = new Pen(Color.Orange);
            pen.Width = 1;
            foreach (Rectangle t in recList)
            {
                g.DrawRectangle(pen, t);
            }
            bmp.Save(savepath);
        }

        /// <summary>
        /// 得到线条相交的矩形
        /// </summary>
        /// <param name="lineList"></param>
        /// <returns></returns>
        private static List<Rectangle> GetRectangle(List<LineCls> lineList, int length)
        {
            List<Rectangle> ret = new List<Rectangle>();
            //横线
            List<LineCls> lineList0 = new List<LineCls>();
            //竖线
            List<LineCls> lineList1 = new List<LineCls>();
            //挑出横线和竖线
            for (int i = 0; i < lineList.Count; i++)
            {
                if (lineList[i].Drict == 0)
                {
                    lineList0.Add(lineList[i]);
                }
                else
                {
                    lineList1.Add(lineList[i]);
                }
            }
            //计算矩形的噪音值
            int flaotNum = 20;
            //从每条横线开始找出矩形
            for (int i = 0; i < lineList0.Count; i++)
            {
                //左竖线
                LineCls left = null;
                //右竖线
                LineCls right = null;
                //先找到两根相交且在他下方的竖线
                for (int j = 0; j < lineList1.Count; j++)
                {
                    if (lineList1[j].IsUse == true)
                    {
                        continue;
                    }
                    //左相交竖线
                    if (lineList1[j].EndY - lineList0[i].EndY > length && Math.Abs(lineList1[j].StartY - lineList0[i].StartY) < flaotNum && Math.Abs(lineList1[j].StartX - lineList0[i].StartX) < flaotNum)
                    {
                        left = lineList1[j];
                    }
                    //右相交竖线
                    if (lineList1[j].EndY - lineList0[i].EndY > length && Math.Abs(lineList1[j].StartY - lineList0[i].StartY) < flaotNum && Math.Abs(lineList1[j].StartX - lineList0[i].EndX) < flaotNum)
                    {
                        right = lineList1[j];
                    }
                }
                //找到左右竖线了再找下面线条
                if (left != null && right != null)
                {
                    for (int k = 0; k < lineList0.Count; k++)
                    {
                        if (lineList0[k].IsUse == true)
                        {
                            continue;
                        }
                        //水平长度一致
                        if (Math.Abs(lineList0[k].StartX - lineList0[i].StartX) < flaotNum && Math.Abs(lineList0[k].EndX - lineList0[i].EndX) < flaotNum)
                        {
                            //垂直能封顶
                            if (Math.Abs(lineList0[k].StartY - left.EndY) < flaotNum && Math.Abs(lineList0[k].EndY - right.EndY) < flaotNum)
                            {
                                Rectangle rec = new Rectangle(left.StartX, lineList0[i].StartY, right.StartX - left.StartX, lineList0[k].StartY - lineList0[i].StartY);
                                if (rec.Width < length || rec.Height < length)
                                {
                                    continue;
                                }
                                lineList0[i].IsUse = true;
                                left.IsUse = true;
                                right.IsUse = true;
                                lineList0[k].IsUse = true;
                                ret.Add(rec);
                            }
                        }
                    }
                }
            }
            return ret;
        }

        /// <summary>
        /// 在黑白图上画出找到的线条
        /// </summary>
        /// <param name="source"></param>
        /// <param name="lineList"></param>
        /// <param name="savepath"></param>
        private static void ShowLine(Bitmap source, List<LineCls> lineList, string savepath)
        {
            Bitmap bmp = new Bitmap(source.Width, source.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.Clear(Color.White);
            g.DrawImage(source, 0, 0);
            Pen pen = new Pen(Color.Red);
            pen.Width = 1;
            foreach (LineCls t in lineList)
            {
                g.DrawLine(pen, t.StartX, t.StartY, t.EndX, t.EndY);
            }
            bmp.Save(savepath);
        }

        /// <summary>
        /// 得到所有大于指定长度的线条
        /// </summary>
        /// <param name="numList"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private static List<LineCls> GetAllLine(List<List<int>> numList, int length)
        {
            List<LineCls> ret = new List<LineCls>();
            //噪音值
            int floatNum = 2;
            //先找横线
            for (int i = 0; i < numList.Count; i++)
            {
                int startX = -1;
                for (int j = 0; j < numList[i].Count; j++)
                {
                    //开始点
                    if (startX < 0 && numList[i][j] == 1)
                    {
                        startX = j;
                    }
                    //结束点
                    if (startX > 0 && numList[i][j] == 0)
                    {
                        if (j - startX > length)
                        {
                            LineCls dto = new LineCls();
                            dto.StartX = startX;
                            dto.StartY = i;
                            dto.EndX = j;
                            dto.EndY = i;
                            dto.Drict = 0;
                            if (ret.Count > 0)
                            {
                                LineCls preLine = ret[ret.Count - 1];
                                //降噪,去除挨着的先
                                if ((dto.StartY - preLine.StartY < 2) && Math.Abs(dto.StartX - preLine.StartX) < floatNum && Math.Abs(dto.EndX - preLine.EndX) < floatNum)
                                {
                                    continue;
                                }
                            }
                            ret.Add(dto);
                        }
                        startX = -1;
                    }
                }
            }

            //再找竖线
            for (int j = 0; j < numList[0].Count; j++)
            {
                int startY = -1;
                for (int i = 0; i < numList.Count; i++)
                {
                    //开始点
                    if (startY < 0 && numList[i][j] == 1)
                    {
                        startY = i;
                    }
                    //结束点
                    if (startY > 0 && numList[i][j] == 0)
                    {
                        if (i - startY > length)
                        {
                            LineCls dto = new LineCls();
                            dto.StartX = j;
                            dto.StartY = startY;
                            dto.EndX = j;
                            dto.EndY = i;
                            dto.Drict = 1;
                            if (ret.Count > 0)
                            {
                                LineCls preLine = ret[ret.Count - 1];
                                //降噪,去除挨着的先
                                if ((dto.StartX - preLine.StartX < 2) && Math.Abs(dto.StartY - preLine.StartY) < floatNum && Math.Abs(dto.EndY - preLine.EndY) < floatNum)
                                {
                                    continue;
                                }
                            }
                            ret.Add(dto);
                        }
                        startY = -1;
                    }
                }

            }
            return ret;
        }

        /// <summary>
        /// 输出值到文本
        /// </summary>
        /// <param name="numList"></param>
        /// <param name="path"></param>
        private static void OutPutValueToTxt(List<List<int>> numList, string path)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < numList.Count; i++)
            {
                string oneRow = "";
                for (int j = 0; j < numList[i].Count; j++)
                {
                    oneRow += numList[i][j];
                }
                sb.AppendLine(oneRow);
            }
            WriteTxt(path, sb.ToString(), true);
        }

        /// <summary>
        /// 图片二极化
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        private static Bitmap ConvertToBipolar(Bitmap source)
        {
            Bitmap bmp = new Bitmap(source.Width, source.Height);
            int iss = 50;
            for (int y = 0; y < bmp.Height; y++)
            {
                for (int x = 0; x < bmp.Width; x++)
                {
                    Color color = source.GetPixel(x, y);
                    int value = 255 - color.B;
                    Color newColor = value > iss ? Color.FromArgb(0, 0, 0) : Color.FromArgb(255, 255, 255);
                    bmp.SetPixel(x, y, newColor);
                }
            }
            return bmp;
        }

        /// <summary>
        /// 得到二极化后的二维List值
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        private static List<List<int>> GetNumberMap(Bitmap source)
        {
            List<List<int>> ret = new List<List<int>>();
            Bitmap bmp = new Bitmap(source.Width, source.Height);
            for (int y = 0; y < bmp.Height; y++)
            {
                List<int> oneRow = new List<int>();
                for (int x = 0; x < bmp.Width; x++)
                {
                    Color color = source.GetPixel(x, y);
                    int value = 255 - color.B;
                    if (value > 0)
                    {
                        value = 1;
                    }
                    oneRow.Add(value);
                }
                ret.Add(oneRow);
            }
            return ret;
        }

        /// <summary>
        /// 写入数据到指定文件
        /// </summary>
        /// <param name="path">文件全路径</param>
        /// <param name="str">数据</param>
        /// <param name="isReplace">是否提换,默认为替换,否则为添加</param>
        /// <returns></returns>
        public static bool WriteTxt(string path, string str, bool isReplace = true)
        {
            FileStream fs = null;
            StreamWriter sw1 = null;
            try
            {
                //如果文件不存在,先创建一个
                if (!File.Exists(path))
                {
                    //创建写入文件  
                    fs = new FileStream(path, FileMode.Create, FileAccess.Write);
                    sw1 = new StreamWriter(fs, Encoding.UTF8);
                    //开始写入值  
                    sw1.WriteLine(str);
                }
                else
                {
                    //如果是替换,先清除之前的内容
                    if (isReplace)
                    {
                        using (StreamWriter sw = new StreamWriter(path, false, Encoding.UTF8))
                        {
                            sw.Write("");
                            sw.Close();
                        }
                    }
                    fs = new FileStream(path, FileMode.Append, FileAccess.Write);
                    sw1 = new StreamWriter(fs, Encoding.UTF8);
                    sw1.WriteLine(str);
                }
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (sw1 != null)
                {
                    sw1.Close();
                }
                if (fs != null)
                {
                    fs.Close();
                }
            }
        }


        /// <summary>
        /// 描述线坐标
        /// </summary>
        private class LineCls
        {
            /// <summary>
            /// 开始X
            /// </summary>
            public int StartX
            {
                get;
                set;
            }

            /// <summary>
            /// 开始Y
            /// </summary>
            public int StartY
            {
                get;
                set;
            }

            /// <summary>
            /// 结束X
            /// </summary>
            public int EndX
            {
                get;
                set;
            }

            /// <summary>
            /// 结束Y
            /// </summary>
            public int EndY
            {
                get;
                set;
            }

            /// <summary>
            /// 方向,0横线,1竖线
            /// </summary>
            public int Drict
            {
                get;
                set;
            }

            /// <summary>
            /// 是否参与了矩形
            /// </summary>
            public bool IsUse
            {
                get;
                set;
            }
        }

    }
}

花了整整大半天,喜欢就给检验点个赞吧,源自imedicallis

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

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

相关文章

Linux生产者消费模型

1.生产者消费者模型 1.1 为何要使用生产者消费者模型 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯&#xff0c;而通过阻塞队列来进行通讯&#xff0c;所以生产者生产完数据之后不用等待消费者处理&#xff0c;直接…

【淄博正大光明】收藏|三分钟带你全面了解这个神奇的镜片

对于孩子的东西 家长总是谨慎再谨慎 而对于夜间戴在眼睛里的镜片 家长更是存在很多顾虑 安全吗&#xff1f;有效吗&#xff1f; 影响孩子睡觉吗&#xff1f; 别着急淄博正大光明眼科医院 带你深度了解角膜塑形镜 01 角膜塑形镜究竟是什么&#xff1f; 角膜塑形镜是一种使用高分…

一文搞懂Linux内核进程CPU调度基本原理

为什么需要调度 进程调度的概念比较简单&#xff0c;我们假设在一个单核处理器的系统中&#xff0c;同一时刻只有一个进程可以拥有处理器资源&#xff0c;那么其他的进程只能在就绪队列中等待&#xff0c;等到处理器空闲之后才有计划获得处理器资源来运行。在这种场景下&#…

k8s快速入门

文章目录一、Kubernetes&#xff08;K8S&#xff09;简介1、概念1.1 Kubernetes (K8S) 是什么1.2 核心特性1.3 部署方案2、Kubernetes 集群架构2.1 架构2.2 重要概念 Pod2.3 Kubernetes 组件二、Kubernetes集群安装1、安装方式介绍2、minikubute安装3、裸机搭建&#xff08;Bar…

python实用脚本(六)—— pandas库的使用(生成、读取表格)

本期主题&#xff1a; python的pandas使用 往期链接&#xff1a; python实用脚本&#xff08;一&#xff09;—— 批量修改目标文件夹下的文件名python实用脚本&#xff08;二&#xff09;—— 使用xlrd读取excelpython实用脚本&#xff08;三&#xff09;—— 通过有道智云AP…

Linux 日志查找常用命令

1.1 cat、zcat cat -n app.log | grep "error"&#xff1a;查询日志中含有某个关键字error的信息&#xff0c;显示行号。 cat -n app.log | grep "error" --color&#xff1a;查询日志中含有某个关键字error的信息&#xff0c;显示行号&#xff0c;带颜色…

基于Detectron2模型和深度学习方法的改进森林火灾检测方法

1.文章信息本次介绍的文章是来自韩国科研团队的一篇2023年火灾检测文章&#xff0c;文章立足于森林火灾检测&#xff0c;题目为《An Improved Forest Fire Detection Method Based on the Detectron2 Model and a Deep Learning Approach》。2.摘要随着全球变暖和人口的增加&am…

【java】真正理解NIO

文章目录前言1、线程不够用, 就算使用了线程池复用线程也无济于事;2、阻塞I/O模式下,会有大量的线程被阻塞,一直在等待数据,这个时候的线程被挂起,只能干等,CPU利用率很低,换句话说,系统的吞吐量差;3、如果网络I/O堵塞或者有网络抖动或者网络故障等,线程的阻塞时间可能很长。整…

Spring boot实现热部署

1.说明 在我们进行Spring Boot项目的编写过程中&#xff0c;会有局部的代码&#xff0c;发生一些变动&#xff0c;这时候&#xff0c;我们只有将项目重启&#xff0c;发生变动的代码才能够生效&#xff0c;为了解决这个问题&#xff0c;我们可以设置Spring Boot热部署&#xf…

React Hooks之useRef详解

一、什么是useRef const refContainer useRef(initialValue); useRef 返回一个可变的 ref 对象&#xff0c;其内部只有一个 current 属性被初始化为传入的参数&#xff08;initialValue&#xff09;useRef 返回的 ref 对象在组件的整个生命周期内持续存在更新 current 值时并不…

Java 洛谷 P1739 表达式括号匹配

题目描述&#xff1a; 题目链接&#xff1a;https://www.luogu.com.cn/problem/P1739 代码实例&#xff1a; import java.util.Scanner; import java.util.Stack;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);String…

本地生成动漫风格 AI 绘画 图像|Stable Diffusion WebUI 的安装和部署教程

Stable Diffusion WebUI 的安装和部署教程1. 简介2. Windows安装环境3. 运行4. 模型下载链接5. 其他资源1. 简介 先放一张WebUI的图片生成效果图&#xff0c;以给大家学习的动力 &#xff1a;&#xff09; 怎么样&#xff0c;有没有小小的心动&#xff1f;这里再补充一下&…

Linux文件系统中的硬链接及常见面试题

如果能对inode的概念有所了解&#xff0c;对理解本文会有所帮助。如果对inode的概念不太清楚也没有关系&#xff0c;我们会捎带介绍一下。在文件系统的实现层面&#xff0c;我们可以认为包含两个组件&#xff1a;一个是包含数据块的池子&#xff0c;池子中的数据块是等大小的&a…

K3S 系列文章-5G IoT 网关设备 POD 访问报错 DNS ‘i/o timeout‘分析与解决

开篇 《K3s 系列文章》《Rancher 系列文章》 问题概述 20220606 5G IoT 网关设备同时安装 K3S Server, 但是 POD 却无法访问互联网地址&#xff0c;查看 CoreDNS 日志提示如下&#xff1a; ... [ERROR] plugin/errors: 2 update.traefik.io. A: read udp 10.42.0.3:38545-&…

进程间通信(重点)

概念 进程是一个独立的资源分配单元&#xff0c;不同进程之间的资源是独立的进程并非孤立的&#xff0c;不同进程需要进行信息的交互和状态的传递&#xff0c;因此需要进程之间的通信【IPC: Inter processes communication】 如qq聊天&#xff0c;qq在每个人的手机上是独立的…

记录--滚动视差动画和解决方法

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 最简单的代码&#xff0c;最极致的享受&#xff0c;主打的就是一个炫酷&#xff5e; 滚动视差 滚动视差效果(Parallax Scrolling)是指让多层背景以不同的速度位移&#xff0c;形成立体的运动效果的视觉…

代码随想录算法训练营第三天 | 链表理论基础 、203.移除链表元素、707.设计链表、206.反转链表

打卡第三天&#xff0c;今天还是认真做了两道题目&#xff0c;明天要6点早起&#xff0c;想早点睡&#xff0c;加上昨天的螺旋数组&#xff08;昨天想到怎么做&#xff0c;但是代码实现解不出来&#xff0c;还没有仔细看视频讲解&#xff09;&#xff0c;和今天的设计链表&…

内网渗透(三十九)之横向移动篇-pass the ticket 票据传递攻击(PTT)横向攻击

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

Pytorch 物体检测 App 体验

物体检测 App 介绍 它是使用 YOLOv5 进行对象检测的 Android 示例应用程序&#xff0c;使用 PyTorch 脚本化 YOLOv5 模型来检测使用该模型训练的 80 个物体对象。 YOLO&#xff08;You Only Look Once&#xff09;是最快和最受欢迎的对象检测模型之一&#xff0c;而YOLOv5 是…

pytorch零基础实现语义分割项目(三)——语义分割模型(U-net和deeplavb3+)

文章目录项目列表前言U-net模型概况下采样过程上采样过程模型代码上采样代码U-net模型构建deeplabv3模型概况模型代码resNetASPPdeeplabv3模型构建结尾项目列表 语义分割项目&#xff08;一&#xff09;——数据概况及预处理 语义分割项目&#xff08;二&#xff09;——标签…