您的位置 首页 FPGA

一个菜鸟的图画处理入门

本来就是入门的那就先说下gdi跟bmp这些东西吧。1gdi跟bmpvc里的CDC也就是设备上下文相当于c#里的graphics,也有lineTo等方法。其实我们在c#中使用graphi

  原本便是入门的 那就先说下gdi 跟 bmp 这些东西吧。

  1 gdi跟bmp

  vc里的CDC 也便是设备上下文 相当于c#里的graphics ,也有lineTo等办法。

  其实咱们在c#中运用graphics的时分就已经在运用gdi+了咱们却浑然不觉

  那么gdi到底在哪里呢 试着在c盘查找gdiplus或许gdi32姓名的文件 你应该会找到 就像这个  

 

  直接删去应该删不掉 不过你可以给他改个姓名 别改了自己都搞忘了O(∩_∩)O哈!。

  然后你随意运转个程序比方QQ 肿么样

  Initialization failure:0x0000000E

  运用windows自带的图片查看器

  加载 c:\windows\system32\shimgvw.dll时犯错 体系找不到指定的文件。

  所以说windows下到处都是gdi

  不要认为bitmap是一种图画格局 就像jpg gif 相同 实践上他们是两个彻底不同的概念。

  在vc++里叫cbitmap 也便是对应的gdi数据模型 等同于c#里的bitmap

  可以这样说.bmp的位图文件是gdi的文件表现形式。 位图文件不进行图画紧缩算法操作直接存储像素矩阵信息所以文件体积十分大

  jpg文件体积十分小为什么 jpg实践上它是依照彻底不同的算法跟理念来存储图画的 都知道人的视觉效应

  首要体现在两个方面 色彩的明暗度 色彩的饱和度 也便是色彩(说俗点便是赤橙黄绿青蓝紫 )而且肉眼底子达不到每个像素那么大的分辩才干

  jpg便是依照这种方法来存储的

  2 位图文件格局

  那么就先说下bmp文件格局吧,自己不是那种长篇大论型的 不说废话。

  这个紧缩包里工程的bin目录有一个叫bmpTestImg.bmp两色的位图文件。以16进制编辑器翻开对照下图看 :

  这儿是下载链接

 

  vc++里有界说好的bitmapheader 用来表明位图头信息 在C#里没有。

  其实没多大联系的这仅仅一种数据安排方法

  假如你乐意也可以界说这么一个结构。

  gdi与设备无关 可是他并不代表跟设备没有任何相关 核算机之间传递的是图片文件或许图画数据 并不是gdi目标。

  微软帮咱们搞了这一层东西,便是说只需是接入windows的设备咱们都可以经过

  gdi在那个设备上显现 输出东西 而不用联系设备自身 ,可以说整个windows供给给咱们的便是gdi 一切窗体

  等等都是gdi制作的,比方说做啥xx编程的时分要直接操作显卡 实践上直接操作的方法速度更快可是没有必要

  所谓的24位真彩色 mspaint画图新建图画默许存储 便是24位真彩色 这并不是什么深邃技能由于 一个像素的色彩用3个八位

  来表明便是24位真彩色

  真彩图画是说他具有显现 256x256x256种色彩的才干

  还有便是c#里默许新建的bitmap目标便是24位真彩的 而且graphic供给的函数不能操作非真彩色的位图

  3 水平不咋滴,仍是来敲点代码吧,\(^o^)/~

  先卖个关子哈 上面你下载的示例图片你看到东西了吗 仍是黑乎乎一片 嘿嘿。假如你看到了那你见鬼了 仍是从速拜拜春哥吧

  最近在研讨那啥dicom 也学会拽文了 嘿嘿

  是否可以这么描绘:bmp是一种约定俗成的有规矩的数据安排方法 不管他在内存中 在文件中 他跟特定编程言语无关 跟渠道无关

  bmp格局简而言之一句话 前54字节存储文件头信息最首要便是图画位数跟宽度高度,从54位开端有调色板则是调色板信息 无调色板则是像素数据。

  由于本文不是专门评论bmp文件格局 具体请拜见bmp格局

  好下面咱们就来读取这种有规矩的数据: 写第一个按钮事情的代码

  void bmpRead()//读取bmp文件格局

  {

  Image bmp = (Bitmap)Image.FromFile("bmpTestImg.bmp");

  MemoryStream bmpData = new MemoryStream();

  bmp.Save(bmpData, ImageFormat.Bmp);

  BinaryReader br = new BinaryReader(bmpData);

  //为什么要偏移18个字节 由于bmp格局"龟腚"在18字节那个当地开端用32位整型存储图画的宽度跟高度

  bmpData.Seek(18, SeekOrigin.Begin);

  int width = br.ReadInt32();

  int height = br.ReadInt32();

  MessageBox.Show(string.Format("宽{0},高{1}", width, height));

  //第11个字节处贮存数据字节的开端方位

  bmpData.Seek(10, SeekOrigin.Begin);

  int dataStart = br.ReadInt32();

  byte[] datas = new byte[width * height];

  int indx = 0;

  bmpData.Seek(dataStart, SeekOrigin.Begin);

  //留意咯 这是调色板开端的方位 更改调色板将会让"看不见"的图画显现出来

  bmpData.Seek(54, SeekOrigin.Begin);

  Random rd = new Random();

  bmpData.Write(new byte[] { (byte)rd.Next(0, 255), (byte)rd.Next(0, 255),

  (byte)rd.Next(0, 255), 0 }, 0, 4);

  bmpData.Write(new byte[] { (byte)rd.Next(0, 255), (byte)rd.Next(0, 255),

  (byte)rd.Next(0, 255), 0 }, 0, 3);

  Image newbmp = Bitmap.FromStream(bmpData);

  Graphics.FromHwnd(this.Handle).DrawImage(newbmp, new Point(0, 0));

  bmpData.Close();

  br.Close();

  }

  上面的代码很简略滴 (⊙o⊙)哦 都看得懂吧 别忘了要在履行文件同级目录放上偶的图片哦 嘿嘿。

  这个合适用来给girlfriend表达啊啥的O(∩_∩)O哈!

  有几个需求阐明的当地

  bmp文件的两色 并非必定得是是非 对吧 可以是赤色绿色, 也可以是两种相同的色儿 对吧

  为什么宽度要在第19字节的方位开端存储 没有为什么 这是bmp格局的“龟腚”对吧 要问去问盖茨大叔

  关于“流”的操作 seek到前面去了 再进行write操作 是否就把对应方位的数据“挤”到后边去了呢?

  NO 数据流是一种游标 “掩盖”型的操作 长度会主动标识到游标到过最远的当地 文件流内存流都相同

  所以说想要做数据刺进啊 文件兼并啊之类的东东的话得弄两个数据流目标哈 相互倒腾数据 这样才干到达意图。

  又扯远了哈 打住。

  不是说读取数据吗 便是读取像素值数据啊,现在开端

  既然是读取像素值,咱得一行一行的读啊 就像扫描相同的。实践上他便是以这种方法存储的哈 只不过略微有点不相同

  那便是图画数据每行以四倍字节为基数不足以0补齐 乃了解了木有 。

  比方说这一个扫描行有3个像素 那么便是9字节 ,4字节的倍数那么他有必要要有12字节 那么剩余的3字节满是0。

  比方说这一个扫描行有20个像素 那么便是60字节 ,4字节的倍数那么他有必要要有60字节 由于60/4正好除净。

  先来说下这个破公式 ((width * 24 + 31) / 32 * 4) 不知道是哪个头脑发热的人想出来的 ,留意这儿的32是指32位 即4字节。

  实践上我只想说两个字十分扯淡 必定是很深化的把握了数据长度运算的实质, 一句把我上面那n多句都替代了

  width是图画宽度 24代表每个像素位数。 核算出实践字节数 先假定他会超出一位 补齐31位 然后经过整型数据相除的性质 除以4字节得到4字节的倍数

  留意终究得到的是扫描行的字节数

  就这样从图画左下角第一个点开端 一行一行从左至右的往上扫描

  然后是bmp图画素的存储方法是BGR的次序哈 而不是一般的RGB 哦 别搞错了,

  曾经很菜的时分用SetPixel()处理像素 被人骂惨了 现在俺仍然来写个setPix() 嘿嘿 第二个按钮的代码:

  void setPix()//

  {

  FileStream bmpData = File.Open("mm.bmp", FileMode.Open); BinaryReader br = new BinaryReader(bmpData);

  bmpData.Seek(10, SeekOrigin.Begin);int bmpDataStart = br.ReadInt32();

  bmpData.Seek(18, SeekOrigin.Begin);int width = br.ReadInt32();int height = br.ReadInt32();

  Bitmap newBmp = (Bitmap)new Bitmap(width, height, PixelFormat.Format24bppRgb);

  MemoryStream newBmpData = new MemoryStream();

  newBmp.Save(newBmpData, ImageFormat.Bmp);BinaryReader br2 = new BinaryReader(newBmpData);

  newBmpData.Seek(10, SeekOrigin.Begin); int newBmpDataStart = br2.ReadInt32();

  newBmpData.Seek(newBmpDataStart, SeekOrigin.Begin);

  for (int i = 0; i < height; i++)

  {

  bmpData.Seek(((width * 24 + 31) / 32 * 4) * i + bmpDataStart, SeekOrigin.Begin);

  newBmpData.Seek(((width * 24 + 31) / 32 * 4) * i + newBmpDataStart, SeekOrigin.Begin);

  for (int j = 0; j < width; j++)

  {

  //留意bmp的像素值是依照bgr的次序存储的哦

  byte[] data = new byte[3];

  bmpData.Read(data, 0, 3);

  newBmpData.Write(new byte[] { data[2], data[1], data[0] }, 0, 3);

  }

  //下面的填充值要不要都可以

  int fill = ((width * 24 + 31) / 32 * 4) – width * 3;

  if (fill > 0)

  {

  byte[] fills = new byte[] { 0, 0, 0 };

  newBmpData.Write(fills, 0, fills.Length);

  }

  }

  newBmpData.Flush();

  newBmp = (Bitmap)Bitmap.FromStream(newBmpData);

  Graphics.FromHwnd(this.Handle).DrawImage(newBmp, new Point(0, 0));

  bmpData.Close(); newBmpData.Close();

  br.Close(); br2.Close();

  }

  假如你把for (int i = 0; i < height; i++)改成 for (int i = 0; i < height/2; i++) 可以看下作用 可以证明在文件中是依照图画从左至右往上 的方法存储的

  经过以上可以看出任何环境下他都是依照同种规矩存贮存储的,就像dicom 只需遵从这种规矩就能经过这种格局完成数据同享。

  都说lockBitmap的方法是最快的 ,确实是最快的哈 由于他是运用指针的方法

  下面是把一个图画转成灰度图 你看 不光代码少了许多 而且还不用费尽心思去确认每一个扫描行的索引 你看 刷的一下 就出来了 嘿嘿

  留意有unsafe代码 在项目->特点 勾选“答应不安全代码” :

  void lockPix()

  {

  Bitmap bmp = (Bitmap)Image.FromFile("mm.bmp");

  BitmapData datas = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),

  System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

  unsafe

  {

  byte* p = (byte*)datas.Scan0;

  int indx = 0;

  for (int i = 0; i < bmp.Height/2; i++)

  {

  for (int j = 0; j < bmp.Width; j++)

  {

  byte b, g, r; b = p[indx + 1]; g = p[indx + 2]; r = p[indx + 3];

  //byte lightLv = (byte)(r * 0.3 + g * 0.59 + b * 0.11);

  byte gray = (byte)((r + g + b) / 3);

  p[indx++] = gray; p[indx++] = gray; p[indx++] = gray;

  }

  }

  }

  bmp.UnlockBits(datas);

  Graphics.FromHwnd(this.Handle).DrawImage(bmp, new Point(0, 0));

  bmp.Dispose();

  }

  灰度图 哎呀 跟你说得又俗又土点便是对每个像素 rgb三个值加起来除以3 不想跟你讲那些我自己都不怎样了解的东西

  可是仍是不得不跟你说下所谓的yuv表明方法 y代表明度 提到这个又得要讲下矩阵乘法 真费事ya。

    

 

  这个什么意思呢,先说说矩阵乘法吧

  比方你商店里有帽子鞋子 袜子 单价别离表明为:

  [25] [80] [15]

  然后今日帽子卖了3件 鞋子卖了1件 袜子卖了两件,可表明为:

  [3]

  [1]

  [2]

  然后今日的收入呢=25×3+80×1+15×2 一共185 用矩阵表明为[185]

  我第二天帽子卖了1件 鞋子卖了两件 袜子卖了3件,那么这两天的出售可表明为:

  [3] [1]

  [1] [2]

  [2] [3]

  那么这两天一共的收入呢=(25×3+80×1+15×2)+(25×1+80×2+15×3) 一共185+230=415 用矩阵表明为[185][230]

  没错 你看到的这便是矩阵乘法 不想讲什么线性代数 什么的那么深邃的理论

  比方上面RGB转转YUV公式的3行3列 乘 3行1列 乘出来是 3行1列 ,

  规矩便是第一个矩阵的列跟第二个矩阵的行共同,得到一个首行尾列数的二维矩阵。

  留意必定是得到一个首行尾列数的二维矩阵,假如不符合这个规范 那么运算是无意义的。

  运算规矩:要从成果矩阵的往前推,先确认成果矩阵元素地点行数列数 C(i,j)。然后把A矩阵对应行 与B矩阵对应列

  相同索引方位的数两两相乘 然后加起来 即为C(i,j)的值。 其实仍是挺简略的。

  这儿有关于矩阵乘法 运算规矩的简介:http://www2.edu-edu.com.cn/lesson_crs78/self/j_0022/soft/ch0605.html

  假如rgb值别离是{115,20,65} 那么转换成yuv表明应该是

  y=115×0.299+20×0.587+65×0.114

  u=115x-0.148+20x-0.289+65×0.437

  v=115×0.615+20x-0.515+65x-0.1

  形似很难了解 由于这个跟前面那个卖东西的又不相同了可以换个视点看 。

  把第二个矩阵往左“倒下来” 便是说让他的行跟第一个矩阵的列 对齐 是不是感觉好多了O(∩_∩)O哈!

  整点杂乱的 那再来随意整个吧

  [2] [4] [-1] [6]

  [1] [0] [3 ] [5]

  成果是多少

  2x-1+4×3 2×6+4×5

  1x-1+0x3 1×6+0x5

  终究成果

  [10] [32]

  [-1] [6]

  也可以把它分解为单行单列的来看 就简略多了哈

  有种很特别的矩阵 有点像对角线

  任何跟他相乘的矩阵都等于那个矩阵自身 有点像 “任何数乘以1 都等于那个数自身”

  [1][0]

  [0][1]

  看吧矩阵乘法便是这么奇特的东东,经过矩阵乘法还可以进行视点旋转 缩放等等

  这个是很深邃的研讨课题了O(∩_∩)O哈! 这儿就不评论了

  总算说完了 俺喝口水了先。也不知讲清楚了没 下面是各种矩阵乘法的示例代码 关于为什么是5×5的矩阵这个可以看下msdn:

  常识学无止境

  第三个按钮:

  void matrixColor()

  {

  Bitmap bmp = (Bitmap)Image.FromFile("mm.bmp");

  ImageAttributes ia = new ImageAttributes();

  //灰阶

  //float[][] colorMatrix ={

  // new float[]{0.299f,0.299f, 0.299f, 0, 0},

  // new float[]{0.587f,0.587f, 0.587f, 0, 0},

  // new float[]{0.114f,0.114f, 0.114f, 0, 0},

  // new float[]{0, 0, 0, 1, 0},

  // new float[]{0, 0, 0, 0, 1}

  // };

  //灰阶

  //float[][] colorMatrix ={

  // new float[]{0.3f, 0.3f, 0.3f, 0, 0},

  // new float[]{0.3f, 0.3f, 0.3f, 0, 0},

  // new float[]{0.3f, 0.3f, 0.3f, 0, 0},

  // new float[]{0, 0, 0, 1, 0},

  // new float[]{0, 0, 0, 0, 1}

  // };

  //反色

  //float[][] colorMatrix ={

  // new float[]{-1, 0, 0, 0, 0},

  // new float[]{0, -1, 0, 0, 0},

  // new float[]{0, 0, -1, 0, 0},

  // new float[]{0, 0, 0, 1, 0},

  // new float[]{1, 1, 1, 0, 1}

  // };

  //亮度

  float[][] colorMatrix ={

  new float[]{1, 0, 0, 0, 0},

  new float[]{0, 1, 0, 0, 0},

  new float[]{0, 0, 1, 0, 0},

  new float[]{0, 0, 0, 1, 0},

  new float[]{l, l, l, 0, 1}};

  l -= 0.1f;

  ColorMatrix cm = new ColorMatrix(colorMatrix);

  ia.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

  Graphics.FromHwnd(this.Handle).DrawImage(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height),

  0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, ia);

  }

  float l = 0.5f;

  rgb为3个能量值 咱们看到屏幕上花花绿绿 色彩是由于三个能量值发生差异化 说俗点便是三个值的份额不相同

  假如三个值相同的话那就跟电灯泡无异了 便是纯亮度表明 即咱们常说的灰度图。

  来写个手艺滴 很山寨滴 功率很低滴 更改亮度的函数

  void light(ref int r, ref int g, ref int b)

  {

  //核算后的平均值

  //添加亮度

  float gray= (r + g + b) + level * 90 > 255 * 3 ? 255 * 3 : (r + g + b) + level * 90;

  //下降亮度

  //float gray = (r + g + b) – level * 90 < 0 ? 0 : (r + g + b) – level * 90; ;

  float percentR = (float)r / (r + g + b), percentG = (float)g / (r + g + b), percentB = (float)b / (r + g + b);

  r = (int)(gray * percentR > 255 ? 255 : gray * percentR);

  g = (int)(gray * percentG > 255 ? 255 : gray * percentG);

  b = (int)(gray * percentB > 255 ? 255 : gray * percentB);

  float ren = gray – (r + g + b);

  if (ren >= 3)

  {

  r = (r + (int)ren) > 255 ? 255 : (r + (int)ren);

  g = (g + (int)ren) > 255 ? 255 : (g + (int)ren);

  b = (b + (int)ren) > 255 ? 255 : (b + (int)ren);

  }

  }

  int level = 0;

  其实呢也远可以不用这样 直接rgb别离乘以1.2 或许1.1之类的就可以了 只不过色彩会失真

  好了总算写完啦 好累ya

  完了 ( ⊙ o ⊙ ) 原本就很菜 这道破隐秘全被你们晓得了 今后出去俺还杂混呐

  当然作为一个商业化的软件 代码的容错也是很重要的 你看acdsee 你把文件数据部分删去一些他照样可以显现 当然这些都是很简略的哈。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/fangan/fpga/114850.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部