CT图像处理实现杂记

发布时间:2023年12月31日

用c#实现:?

1 ct ?文件说明:
说明数据文件
说明图像文件(2进制 8位)
一张CT图像有 512x512 个像素点,在dicom文件中每个像素由2字节表示,所以每张图片约512KB大小。图像中每个像素都是整数,专业名称为 Hounsfield scale 或 CT Number,是描述物质的放射密度的量化值(参考Wikipedia)。上表为常见物质的HU值。


2、读取数据

原始数据:

Data.des中是xml格式数据:<whole>
  <Patient>
    <PatientName>zhang san</PatientName>
    <PatientSex>m</PatientSex>
    <PatientAge>60</PatientAge>
  </Patient>
  <Scan>
    <Hospital>ShenYang Hospital</Hospital>
    <WindowWidth>600</WindowWidth>
    <WindowCenter>400</WindowCenter>
    <ScanTime>2010/06/20 12:30:20</ScanTime>
    <ImageWidth>512</ImageWidth>
    <ImageHeight>512</ImageHeight>
    <XPixelSpacing>0.05</XPixelSpacing>
    <YPixelSpacing>0.05</YPixelSpacing>
    <Interval>0.1</Interval>
  </Scan>
  <Image>
    <Image1>D001_001.0.raw</Image1>
    <Image2>D002_001.0.raw</Image2>
    <Image3>D002_002.0.raw</Image3>
    <Image4>D002_003.0.raw</Image4>
    <Image5>D002_004.0.raw</Image5>
    <Image6>D002_005.0.raw</Image6>
    <Image7>D002_006.0.raw</Image7>
  </Image>
</whole>

患者信息等数据是xml格式通过XmlDocument读取

用记事本打开 des 文件

通过标签描述数据本身
患者姓名 ?性别 ?年龄 扫描(scan)等信息

1 我们要把这个文件内容读取到我们程序中

首先我们构建一个对象imageInfo 来描述这些信息?


1、目标 把xml 信息读取到对象中来
2、借用一个xmldocument 对象进行处理

3 xmldocument ?完整描述一个xml文件!

xml 文件又着严格的层级结构!

建议大家利用第三方AI 工具生成这些代码,再阅读!

读取图像数据 raw,要把它转为bitmap
ps:https://learn.microsoft.com/zh-CN/dotnet/api/system.drawing.bitmap?view=dotnet-plat-ext-8.0
打开文件:

准备:

? ? ? ? private string fileName= "d:/temp/D001_001.0.raw"; ? ? ? ?//文件名

? ? ? ? private short[] imageData; ? ? ? //原始数据
? ? ? ? Bitmap bmpImage; ? ? ? ? ? ? ? ?//显示位图
? ? ? ? private int windowWidth = 300; //窗宽
? ? ? ? private int windowCenter = 100; //窗位
? ? ? ? private int imgWidth = 512; ? ? //图像宽度
? ? ? ? private int imgHeight = 512; ? ?//图像高度

1) File.Open

2)BinaryReader

读取的文件放到一个临时数组当中
3)byte[] tempData = new byte[imgWidth * imgHeight * 2];

4)把tempData ?数据读取到imageData 数组当中,这时候要位移运算 ? ? ? imageData[j] = (short)((short)tempData[j * 2 + 1] << 8 | tempData[j*2]);

5)灰度转换

windowCenter、windowCenter和医学有关,自己去查。
? ? ? ? public void Rendering()
? ? ? ? {
? ? ? ? ? ? //使用窗宽窗位,创建灰度查找表。图像存储的数据都是正值,而CT值的最小值是1024,查表时下标必须为正,

? ? ? ? ? ? //所以此处调整了窗宽窗位
? ? ? ? ? ? int offset = 1024;
? ? ? ? ? ? Byte[] lookUpTable = new Byte[4096]; ? ?//数组元素默认值为0
? ? ? ? ? ? int low = windowCenter - windowWidth / 2 + offset;
? ? ? ? ? ? int high = windowCenter + windowWidth / 2 + offset;
? ? ? ? ? ? for (int i = low; i <= high; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? lookUpTable[i] = (Byte)((i - (windowCenter - windowWidth / 2 + offset)) / (double)windowWidth * 255);
? ? ? ? ? ? }
? ? ? ? ? ? for (int i = high + 1; i < 4096; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? lookUpTable[i] = 255;
? ? ? ? ? ? }

? ? ? ? ? ? grayBmp = new byte[imgWidth * imgHeight];
? ? ? ? ? ? for (int i = 0; i < imgHeight; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for (int j = 0; j < imgWidth; j++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? short data = (short)(imageData[i * imgWidth + j]);
? ? ? ? ? ? ? ? ? ? grayBmp[i * imgWidth + j] = lookUpTable[data];
? ? ? ? ? ? ? ? ? ? int temp = lookUpTable[data];
? ? ? ? ? ? ? ? ? ? Color pixel = Color.FromArgb(temp, temp, temp);
? ? ? ? ? ? ? ? ? ? bmpImage.SetPixel(j, i, pixel); ? ? //速度很慢,使用下面的方法把数据一次性传过去
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }

? ? ? ? }

把图像文件信息封装到类当中:

{
    [System.Serializable]
    class MImage
    {
        private string fileName;        //文件名

        private short[] imageData;       //原始数据
        Bitmap bmpImage;                //显示位图
        private int windowWidth = 300; //窗宽
        private int windowCenter = 100; //窗位
        private int imgWidth = 512;     //图像宽度
        private int imgHeight = 512;    //图像高度



        public int ImgWidth
        {
            get { return imgWidth; }
        }
        public int ImgHeight
        {
            get { return imgHeight; }
        }

        public int WindowWidth
        {
            get { return windowWidth; }
            set { windowWidth = value; }
        }
        public int WindowCenter
        {
            get { return windowCenter; }
            set { windowCenter = value; }
        }

        public Bitmap BmpImage
        {
            get { return bmpImage; }
        }

        public MImage(string fileName)
        {
            this.fileName = fileName;
            this.imgWidth = ImageInfo.ImageWidth;
            this.imgHeight = ImageInfo.ImageHeight;
            this.windowWidth = ImageInfo.WindowWidth;
            this.windowCenter = ImageInfo.WindowCenter;
        }

        public Bitmap LoadImage()
        {

        }

 
    }
}

6) 前端渲染? 通过graphics 把bitmap呈现在界面。
? ? ? ? ? ? Rectangle showRect = new Rectangle();
1 我们通过OnPaint 绘制图像?,搞个继承panel的自定义组件实现。

通过OnPaint 绘制图像

界面设计布局:

增加子面板的的例子代码:

for (int i = 1; i < 4; i++)
{
    for (int x = 1; x < 4; x++)
    {
        Panel panel1 = new Panel();
        // Initialize the Panel control.
        panel1.Location = new Point(56 + i * 264, 72 + x * 152);
        panel1.Size = new Size(264, 152);
        // Set the Borderstyle for the Panel to three-dimensional.
        panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
        // Initialize the Label and TextBox controls.
        // Add the Panel control to the form.
        this.Controls.Add(panel1);
        // Add the Label and TextBox controls to the Panel.
    }
}

Winform ?menu+status+SplitContainer

?? ?
?? ?splicePage ? :作为左半边的面板 ?这个是自定义的 继承了Panel
?? ?
?? ?Child panel: ?MImageWnd

? ??
?? ?创建panel
?? ?1 ? MImageWnd mw= new ?MImageWnd()
?? ?
?? ?2 ? 追加到父容器splicePage ? 当中 ? this.Controls.add(mw)

?定位: 左 上位置
?? ?
?? ?设置childPanel ?的 height、width:平分splicePage ? 的长度 宽度

显示 与显示数据分离 ? ??
?? ?
?? ?List<MImageWnd > ?
?? ?
? ? 我们要干三件工作
?? ?
?? ?1 ?改变colNum ?.rowNum
?? ?
?? ?//清理现场的工作
?? ?2 ?清空wndList wndList 是我们记录子面板的集合 ?可以看成是前端的数据模型
?? ?
?? ?3、Controls ?remove MImageWnd?

? ? ? ? ? ?InitializeComponent();
? ? ? ? ? ?Rectangle rect = Screen.GetWorkingArea(this); ? //获得屏幕分辨率
? ? ? ? ? ?this.WindowState = FormWindowState.Maximized;
? ? ? ? ? ?this.mainSplit.SplitterDistance = (int)(this.mainSplit.Width * 4f / 5f);

? ? ? ? ? ?this.slicePage = new SlicePage();
? ? ? ? ? ?slicePage.Dock = DockStyle.Fill;
? ? ? ? ? ?this.mainSplit.Panel1.Controls.Add(slicePage);
? ? ? ? ? ?slicePage.CreateControls();

slicePage: 起到父容器及控制器作用:

{
    public partial class SlicePage : Panel
    {
        List<MImageWnd> wndList = new List<MImageWnd>();  //窗口数组
        List<MImage> imgList = new List<MImage>();      //图像数组
        List<AnnoTool> annoList = new List<AnnoTool>();  //标注数组,和图像一一对应

        int rowNum = 3;     //子窗口行数

        int columnNum = 3;  //子窗口列数


        VScrollBar sb = new VScrollBar();
        int scrollBarWidth = 20;

        public List<MImage> ImgList
        {
            get { return imgList; }
            set { imgList = value; }
        }

        public List<AnnoTool> AnnoList
        {
            get { return annoList; }
            set { annoList = value; }
        }

        public SlicePage()
        {
            sb.Minimum = 0;
            sb.Maximum = 0;
            sb.Scroll += new ScrollEventHandler(sb_Scroll);
        }

        public void LoadImage()
        {
            if (imgList == null)
            {
                return;
            }

            int num = imgList.Count < wndList.Count ? imgList.Count : wndList.Count;
            for (int i = 0; i < num; i++)
            {
                wndList[i].SetImage(imgList[i], annoList[i]);
                wndList[i].Invalidate();
                wndList[i].Update();
               // ((MainForm)(this.Parent.Parent.Parent)).ProcessProgressBar();
            }

            sb.Minimum = 0;
            if (imgList.Count % columnNum != 0)
            {
                sb.Maximum = imgList.Count / columnNum;
            }
            else
                sb.Maximum = imgList.Count / columnNum - 1;
            sb.SmallChange = 1;
            sb.LargeChange = rowNum;
            sb.Value = 0;
        }

        public void Rotate()
        {
            foreach (MImageWnd wnd in wndList)
            {
                wnd.Rotate();
            }
        }

        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // SlicePage
            // 
            this.Resize += new System.EventHandler(this.SlicePage_Resize);
            this.ResumeLayout(false);
        }

        void sb_Scroll(object sender, ScrollEventArgs e)
        {
            if (imgList == null)
            {
                return;
            }
            int start = e.NewValue * columnNum; //点击滚动条后,从此幅图像开始显示

            int num = (imgList.Count - start) < wndList.Count ? (imgList.Count - start) : wndList.Count;
            for (int i = 0; i < num; i++)
            {
                wndList[i].SetImage(imgList[i + start], annoList[i + start]);
                wndList[i].Invalidate();
                wndList[i].Update();
            }
            for (int i = num; i < wndList.Count; i++)  //多余的窗口,清除它的图像
            {
                wndList[i].ClearImage();
            }
        }

        public void Relayout(int row, int col)
        {
            rowNum = row;
            columnNum = col;
            foreach (MImageWnd wnd in wndList)
            {
                this.Controls.Remove(wnd);
            }
            this.wndList.Clear();
            CreateControls();
            ArrangeControls();
            LoadImage();    //改变了窗口个数以后,重新加载图像
        }

        public void CreateControls()
        {
            for (int i = 0; i < rowNum * columnNum; i++)
            {
                MImageWnd wnd = new MImageWnd();
                wnd.BorderStyle = BorderStyle.FixedSingle;
                this.Controls.Add(wnd);
                wndList.Add(wnd);
            }

            this.Controls.Add(sb);
        }

        private void ArrangeControls()
        {
            if (wndList.Count == 0)
                return;

            int width = (this.Width - scrollBarWidth) / columnNum;
            int height = this.Height / rowNum;
            for (int i = 0; i < rowNum * columnNum; i++)
            {
                wndList[i].Location = new Point(i % columnNum * width, i / columnNum * height);
                wndList[i].ClientSize = new Size(width, height);
            }

            sb.Location = new Point(this.Width - scrollBarWidth, 0);
            sb.Size = new Size(scrollBarWidth, this.Height);
        }

        //此两种写法的详细区别。

        protected override void OnResize(EventArgs eventargs)
        {
            ArrangeControls();
            base.OnResize(eventargs);
        }

        private void SlicePage_Resize(object sender, EventArgs e)
        {
        }

        //public const int USER = 0x0400;
        //public const int WM_TEST = USER + 101;

        //protected override void DefWndProc(ref System.Windows.Forms.Message m)
        //{
        //    switch (m.Msg)
        //    {
        //        case WM_TEST: //处理消息 
        //            MessageBox.Show("haha");
        //            break;
        //        default:
        //            base.DefWndProc(ref m);//调用基类函数处理非自定义消息。 
        //            break;
        //    }
        //} 
    }
}

其中,重要的一点是把数据源设置到子面板当中

                wndList[i].SetImage(imgList[i]);
                wndList[i].Invalidate();
                wndList[i].Update();

?为scrollBar留空间 ? int scrollBarWidth = 20;

查官网 scrollBar 用法

1 、创建 :VScrollBar sb = new VScrollBar();
?2 、设置 scrollBarWidth ?height
3 、add 到父容器

scrollBar5个属性:


value=0 ?: 滚动的刻度
Maximum= ? this.imglist.count%columnNum*rowNum!=0

? ? ? ? ? ? ? ?this.imglist.count/columnNum*rowNum
?? ??? ??? ? ??
?? ??? ??? ? ? this.imglist.count/columnNum*rowNum-1

Minimum=0
SmallChange=1 ? ?
LargeChange=rowNum
?

?? ?设置滚动事件

?? ?3*3 ??
?? ?改参数 ?2*2 ?、4*4
?? ?清除 数据模型、实际展示
?? ?
?? ?? ??
? ? ps:Delegate Class
?? ?类比c函数指针:
?? ?https://learn.microsoft.com/en-us/dotnet/csharp/delegates-overview
?? ?
?? ?
?? ? ?通过委托来实现事件处理的过程,通常需要以下4个步骤:
?? ?? 定义委托类型,并在发布者类中定义一个该类型的公有成员。
?? ?? 在订阅者类中定义委托处理方法。
?? ?? 订阅者对象将其事件处理方法链接到发布者对象的委托成员(一个委托类型的引用)上。
?? ?? 发布者对象在特定的情况下“激发”委托操作,从而自动调用订阅者对象的委托处理方法。
?? ?? ??
1 打开文件

根据dilalog 中的选择 动态读取xml 文件
xml文件当中的信息 转换为ImageInfo对象、设置? MIMage

我们已经能读取数据,也能形成九宫格,现在要组装到项目中:

form ? :读取文件 ?——》

?? ?xml----->imageInfo

?? ? 数据文件---》 ? MIMage

? ? ? ? ImageInfo info = new ImageInfo();
?? ? ? ?XmlDocument myxml = new XmlDocument();

? ? ?ps:? ? ?OpenFileDialog
?? ?? ? 来自 <https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.openfiledialog?view=windowsdesktop-7.0>?

? 更改openfileDialog过滤条件
? ? ?dlg.Filter = "图像描述文件 (*.des)|*.des|生数据 (*.raw)|*.raw";
?
调用?? ?

? ? ? ? if (dlg.ShowDialog() == DialogResult.OK)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? slicePage.ImgList.Clear();

? ? ? ? ? ? ? ? if (dlg.FilterIndex == 1)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ParseDesFile(dlg.FileName);
? ? ? ? ? ? ? ? ? ? //slicePage.LoadImage(); ?//重绘界面
?? ??? ??? ?}
? ? ? ? ? ? ?}

? ? ? ?

        private void ParseDesFile(string fileName)
        {
            ImageInfo info = new ImageInfo();
            XmlDocument myxml = new XmlDocument();
            myxml.Load(fileName);
            XmlNode root = myxml.DocumentElement;
            XmlNode node = root["Patient"];
            ImageInfo.PatientName = node["PatientName"].InnerText;
            ImageInfo.PatientSex = Convert.ToChar(node["PatientSex"].InnerText);
            ImageInfo.PatientAge = Convert.ToInt32(node["PatientAge"].InnerText);
            node = root["Scan"];
            ImageInfo.HospitalName = node["Hospital"].InnerText;
            ImageInfo.WindowWidth = Convert.ToInt32(node["WindowWidth"].InnerText);
            ImageInfo.WindowCenter = Convert.ToInt32(node["WindowCenter"].InnerText);
            ImageInfo.ScanTime = node["ScanTime"].InnerText;
            ImageInfo.ImageWidth = Convert.ToInt32(node["ImageWidth"].InnerText);
            ImageInfo.ImageHeight = Convert.ToInt32(node["ImageHeight"].InnerText);
            ImageInfo.XPixelSpacing = Convert.ToDouble(node["XPixelSpacing"].InnerText);
            ImageInfo.YPixelSpacing = Convert.ToDouble(node["YPixelSpacing"].InnerText);
            ImageInfo.Interval = Convert.ToDouble(node["Interval"].InnerText);

            node = root["Image"];
            int pos = fileName.LastIndexOf('\\');
            string path = fileName.Substring(0, pos + 1);
            foreach (XmlNode var in node.ChildNodes)
            {
                MImage img = new MImage(path + var.InnerText);
                slicePage.ImgList.Add(img);
            }
        }

? ? Draw object:
?? ?1. Rectangle
?? ?2. Line
?? ?3. Ellipse
?? ?4. Text

1?? ?创建MImage、 imageInfo 2个文件,? 修改namespace ?MImage中增加loadImage方法

2 ? slicePage 起到连接数据与显示模型的桥梁的作用,我们在它当中增加字段与属性,让它认识MImage等
? ? ? ? private List<MImage> imgList = new List<MImage>(); ? ? ?//图像数组
?? ??? ?internal List<MImage> ImgList { get => imgList; set => imgList = value; }
?
控制层:
? ? ? ? /**
? ? ? ? ?* 把数据模型的数据塞给显示模型 ?这里不负责渲染 ?但调用invalate() update() 方法 引起渲染
? ? ? ? ?*?
? ? ? ? ?*/

? ? ? ? public void LoadImage() {

? ? ? ? ? ? for (int i = 0; i < 7; i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? wndList[i].SetImage(imgList[i]);
? ? ? ? ? ? ? ? wndList[i].Invalidate();
? ? ? ? ? ? ? ? wndList[i].Update();
? ? ? ? ? ? }
? ? ? ? }


Mimage: 准备数据,把原始raw数据转换为bitmap数据:

? ? public Bitmap LoadImage()
? ? {
? ? ? ? imageData = new short[imgWidth * imgHeight];
? ? ? ? bmpImage = new System.Drawing.Bitmap(imgWidth, imgHeight);

? ? ? ? byte[] tempData = new byte[imgWidth * imgHeight * 2];
? ? ? ? BinaryReader sr = new BinaryReader(File.Open(fileName, FileMode.Open));
? ? ? ? //没有办法直接把数据读到short数组里面,所以借助byte数组转化一下。

? ? ? ? sr.Read(tempData, 0, imgWidth * imgHeight * 2);
? ? ? ? sr.Close();

? ? ? ? for (int i = 0; i < imgWidth * imgHeight; i++)
? ? ? ? {
? ? ? ? ? ? imageData[i] = (short)((short)tempData[2 * i + 1] << 8 | tempData[2 * i]);
? ? ? ? }

? ? ? ? Rendering();
? ? ? ? return bmpImage;
? ? }

? ? ? ? ? ? public void Rendering()
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //使用窗宽窗位,创建灰度查找表。图像存储的数据都是正值,而CT值的最小值是1024,查表时下标必须为正,

? ? ? ? ? ? ? ? //所以此处调整了窗宽窗位
? ? ? ? ? ? ? ? int offset = 1024;
? ? ? ? ? ? ? ? Byte[] lookUpTable = new Byte[4096]; ? ?//数组元素默认值为0
? ? ? ? ? ? ? ? int low = windowCenter - windowWidth / 2 + offset;
? ? ? ? ? ? ? ? int high = windowCenter + windowWidth / 2 + offset;
? ? ? ? ? ? ? ? for (int i = low; i <= high; i++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? lookUpTable[i] = (Byte)((i - (windowCenter - windowWidth / 2 + offset)) / (double)windowWidth * 255);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? for (int i = high + 1; i < 4096; i++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? lookUpTable[i] = 255;
? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? //grayBmp = new byte[imgWidth * imgHeight];
? ? ? ? ? ? ? ? for (int i = 0; i < imgHeight; i++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? for (int j = 0; j < imgWidth; j++)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? short data = (short)(imageData[i * imgWidth + j]);
? ? ? ? ? ? ? ? ? ? ? ? //grayBmp[i * imgWidth + j] = lookUpTable[data];
? ? ? ? ? ? ? ? ? ? ? ? int temp = lookUpTable[data];
? ? ? ? ? ? ? ? ? ? ? ? Color pixel = Color.FromArgb(temp, temp, temp);
? ? ? ? ? ? ? ? ? ? ? ? bmpImage.SetPixel(j, i, pixel); ? ? //速度很慢,使用下面的方法把数据一次性传过去
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }

? ? ? ? ? ? }


//绘制数据? ? ?

? ? ? if (null != mImage)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? double tempFactor;
? ? ? ? ? ? ? ? if (this.Width / (double)this.Height > mImage.ImgWidth / (double)mImage.ImgHeight) ?//如果窗口较宽
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? tempFactor = this.Height / (double)mImage.ImgHeight * factor;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? tempFactor = this.Width / (double)mImage.ImgWidth * factor;
? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? showRect.X = (int)((this.Width - mImage.ImgWidth * tempFactor) / 2) + offset.X;
? ? ? ? ? ? ? ? showRect.Y = (int)((this.Height - mImage.ImgHeight * tempFactor) / 2) + offset.Y;
? ? ? ? ? ? ? ? showRect.Width = (int)(mImage.ImgWidth * tempFactor);
? ? ? ? ? ? ? ? showRect.Height = (int)(mImage.ImgHeight * tempFactor);

? ? ? ? ? ? ? ? e.Graphics.DrawImage(mImage.BmpImage, showRect);

? ? ? ? ? ? }


?? ?滚动事件:
?? ?
?? ? ? ?动态计算偏移量
?? ? ?重新设置Imgwnd 的图像数据源
?? ?
?? ?
?? ? ? ?加载的时候,loadimg
?? ?
?? ?

?? ? ? ? ? ? ? ?sb.Minimum = 0;
?? ? ? ? ? ? ? ?if (imgList.Count % columnNum != 0)
?? ? ? ? ? ? ? ?{
?? ? ? ? ? ? ? ? ? ?sb.Maximum = imgList.Count / columnNum;
?? ? ? ? ? ? ? ?}
?? ? ? ? ? ? ? ?else
?? ? ? ? ? ? ? ? ? ?sb.Maximum = imgList.Count / columnNum - 1;
?? ? ? ? ? ? ? ?sb.SmallChange = 1;
?? ? ? ? ? ? ? ?sb.LargeChange = rowNum;
?? ? ? ? ? ? ? ?sb.Value = 0;


?? ?
鼠标状态
?? ?

?? ? ? ?enum MOUSESTATE
?? ? ? ?{
?? ? ? ? ? ?NONE,
?? ? ? ? ? ?WL,
?? ? ? ? ? ?ZOOM,
?? ? ? ? ? ?MOVE,
?? ? ? ? ? ?LINE,
?? ? ? ? ? ?RECT,
?? ? ? ? ? ?ELLIPSE,
?? ? ? ? ? ?TEXT
?? ? ? ?}

??
?? ?
?? ?
?? ?图像旋转:
?? ? ? ? 这个基于矩阵变化
?? ? Matrix rotateMatrix = new Matrix();
?? ?
?? ?绘制的时候设置gr转换
?? ?
?? ?? ??
?? ?Wnd:

?? ? ? ? ? ?public void Rotate()
?? ? ? ? ? ?{
?? ? ? ? ? ? ? ?Point center = new Point();
?? ? ? ? ? ? ? ?center.X = (showRect.Left + showRect.Right) / 2;
?? ? ? ? ? ? ? ?center.Y = (showRect.Top + showRect.Bottom) / 2;
?? ? ? ? ? ? ? ?rotateMatrix.RotateAt(90, center);
?? ? ? ? ? ? ? ?Invalidate();
?? ? ? ? ? ? ? ?Update();
?? ? ? ? ? ?}

? ?
?? ?
?? ?slicePage 循环调用它
?? ?委托:
?? ?

?? ?using System;
?? ?using System.IO;
?? ?
?? ?namespace DelegateAppl
?? ?{
?? ? ? class PrintString
?? ? ? {
?? ? ? ? ?static FileStream fs;
?? ? ? ? ?static StreamWriter sw;
?? ? ? ? ?// 委托声明
?? ? ? ? ?public delegate void printString(string s);
?? ?
?? ? ? ? ?// 该方法打印到控制台
?? ? ? ? ?public static void WriteToScreen(string str)
?? ? ? ? ?{
?? ? ? ? ? ? Console.WriteLine("The String is: {0}", str);
?? ? ? ? ?}
?? ? ? ? ?// 该方法打印到文件
?? ? ? ? ?public static void WriteToFile(string s)
?? ? ? ? ?{
?? ? ? ? ? ? fs = new FileStream("c:\\message.txt", FileMode.Append, FileAccess.Write);
?? ? ? ? ? ? sw = new StreamWriter(fs);
?? ? ? ? ? ? sw.WriteLine(s);
?? ? ? ? ? ? sw.Flush();
?? ? ? ? ? ? sw.Close();
?? ? ? ? ? ? fs.Close();
?? ? ? ? ?}
?? ? ? ? ?// 该方法把委托作为参数,并使用它调用方法
?? ? ? ? ?public static void sendString(printString ps)
?? ? ? ? ?{
?? ? ? ? ? ? ps("Hello World");
?? ? ? ? ?}
?? ? ? ? ?static void Main(string[] args)
?? ? ? ? ?{
?? ? ? ? ? ? printString ps1 = new printString(WriteToScreen);
?? ? ? ? ? ? printString ps2 = new printString(WriteToFile);
?? ? ? ? ? ? sendString(ps1);
?? ? ? ? ? ? sendString(ps2);
?? ? ? ? ? ? Console.ReadKey();
?? ? ? ? ?}
?? ? ? }
?? ?}


?? ?? ? 绘制线段:

1、MImageWnd中 规定位置字段,记录点击位置       

        private int start_x = -1;
        private int start_y = -1;

        private int end_x = -1;
        private int end_y = -1;
		
		
		
		2、构建  MLine 类
		    internal class MLine
    {
        private int start_x = -1;
        private int start_y = -1;

        private int end_x = -1;
        private int end_y = -1;

        public int Start_x { get=> start_x; set=> start_x=value; }
        public int Start_y { get => start_y; set => start_y = value; }

        public int End_x { get => end_x; set => end_x = value; }
        public int End_Y { get => end_y; set => end_y = value; }
    }
	
	
	3、MImageWnd中 添加集合 List<MLine> lines
	
	
	4、   划线方法 public void DrawLineInt(int x0, int y0, int x1, int y1, Graphics g)
    {
     ......
    }

? ??
?? ?事件定义、订阅、发布处理 的例子代码:
?? ?

using System;
?? ?namespace SimpleEvent
?? ?{
?? ? ?using System;
?? ? ?/***********发布器类***********/
?? ? ?public class EventTest
?? ? ?{
?? ? ? ?private int value;
?? ?
?? ? ? ?public delegate void NumManipulationHandler();
?? ?
?? ?
?? ? ? ?public event NumManipulationHandler ChangeNum;
?? ? ? ?protected virtual void OnNumChanged()
?? ? ? ?{
?? ? ? ? ?if ( ChangeNum != null )
?? ? ? ? ?{
?? ? ? ? ? ?ChangeNum(); /* 事件被触发 */
?? ? ? ? ?}else {
?? ? ? ? ? ?Console.WriteLine( "event not fire" );
?? ? ? ? ? ?Console.ReadKey(); /* 回车继续 */
?? ? ? ? ?}
?? ? ? ?}
?? ?
?? ?
?? ? ? ?public EventTest()
?? ? ? ?{
?? ? ? ? ?int n = 5;
?? ? ? ? ?SetValue( n );
?? ? ? ?}
?? ?
?? ?
?? ? ? ?public void SetValue( int n )
?? ? ? ?{
?? ? ? ? ?if ( value != n )
?? ? ? ? ?{
?? ? ? ? ? ?value = n;
?? ? ? ? ? ?OnNumChanged();
?? ? ? ? ?}
?? ? ? ?}
?? ? ?}
?? ?
?? ?
?? ? ?/***********订阅器类***********/
?? ?
?? ? ?public class subscribEvent
?? ? ?{
?? ? ? ?public void printf()
?? ? ? ?{
?? ? ? ? ?Console.WriteLine( "event fire" );
?? ? ? ? ?Console.ReadKey(); /* 回车继续 */
?? ? ? ?}
?? ? ?}
?? ?
?? ? ?/***********触发***********/
?? ? ?public class MainClass
?? ? ?{
?? ? ? ?public static void Main()
?? ? ? ?{
?? ? ? ? ?EventTest e = new EventTest(); /* 实例化对象,第一次没有触发事件 */
?? ? ? ? ?subscribEvent v = new subscribEvent(); /* 实例化对象 */
?? ? ? ? ?e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 注册 */
?? ? ? ? ?e.SetValue( 7 );
?? ? ? ? ?e.SetValue( 11 );
?? ? ? ?}
?? ? ?}
?? ?}
?? ?
?? ?e.KeyCode == Keys.Enter


?? ?

文章来源:https://blog.csdn.net/wh_xia_jun/article/details/135312751
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。