CT图像处理实现杂记

用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


    

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一份MATLAB语音信号变声系统的代码示例,其中包含了三种语音信号变声的方法: ```matlab % 读取音频文件 [y, Fs] = audioread('input.wav'); % 时域变声(将音调提高一个八度) y_pitch = y(1:2:end); % 去掉一半的采样点,相当于将原信号的采样率提高一倍 audiowrite('pitch.wav', y_pitch, Fs*2); % 输出变声后的音频文件 % 频域变声(将语音信号加入白噪声) y_noise = y + 0.1*randn(size(y)); % 将原信号加入均值为0、方差为0.1的高斯白噪声 audiowrite('noise.wav', y_noise, Fs); % 输出变声后的音频文件 % 共振峰变声(将音色变为女性声音) [B, A] = butter(6, 700/(Fs/2), 'high'); % 生成高通滤波器,去除低频分量 y_filtered = filter(B, A, y); [R, P, G] = formant(y_filtered, Fs); % 求取共振峰 R_new = 0.8*R; % 将共振峰的位置向高频方向移动 y_formant = formant_synthesis(R_new, P, G, length(y_filtered)); % 合成新的语音信号 audiowrite('formant.wav', y_formant, Fs); % 输出变声后的音频文件 ``` 在以上代码中,时域变声是通过去掉一半的采样点,相当于将原信号的采样率提高一倍,实现的。频域变声是通过将原信号加入均值为0、方差为0.1的高斯白噪声实现的。共振峰变声是通过使用高通滤波器和共振峰合成算法实现的。具体来说,共振峰变声首先使用一个高通滤波器去除低频分量,然后求取语音信号的共振峰,最后合成新的语音信号。 需要注意的是,以上代码仅演示了基本的语音信号变声方法,实际应用中还需要进行更多的优化和改进,例如:合成算法的优化、声音质量的评估等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值