久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

一個(gè)菜鳥的圖像處理入門

 Fredanf 2013-03-16

一個(gè)菜鳥的圖像處理入門

Posted on 2012-03-07 21:43 assassinx 閱讀(1412) 評(píng)論(4) 編輯 收藏

本來就是入門的 那就先說下gdi 跟 bmp 這些東西吧。

1 gdi跟bmp


vc里的CDC 也就是設(shè)備上下文 相當(dāng)于c#里的graphics ,也有l(wèi)ineTo等方法,。
其實(shí)我們?cè)赾#中使用graphics的時(shí)候就已經(jīng)在使用gdi+了我們卻渾然不覺
那么gdi到底在哪里呢 試著在c盤搜索gdiplus或者gdi32名字的文件 你應(yīng)該會(huì)找到 就像這個(gè)
直接刪除應(yīng)該刪不掉 不過你可以給他改個(gè)名字 別改了自己都搞忘了O(∩_∩)O哈!。
然后你隨便運(yùn)行個(gè)程序比如QQ 腫么樣
Initialization failure:0x0000000E
使用windows自帶的圖片查看器
加載 c:\windows\system32\shimgvw.dll時(shí)出錯(cuò) 系統(tǒng)找不到指定的文件,。
所以說windows下到處都是gdi
不要以為bitmap是一種圖像格式 就像jpg gif 一樣 實(shí)際上他們是兩個(gè)完全不同的概念。
在vc++里叫cbitmap 也就是對(duì)應(yīng)的gdi數(shù)據(jù)模型 等同于c#里的bitmap
可以這樣說.bmp的位圖文件是gdi的文件表現(xiàn)形式,。 位圖文件不進(jìn)行圖像壓縮算法操作直接存儲(chǔ)像素矩陣信息所以文件體積非常大
jpg文件體積非常小為什么 jpg實(shí)際上它是按照完全不同的算法跟理念來存儲(chǔ)圖像的 都知道人的視覺效應(yīng)
主要體現(xiàn)在兩個(gè)方面 色彩的明暗度 色彩的飽和度 也就是色調(diào)(說俗點(diǎn)就是赤橙黃綠青藍(lán)紫 )并且肉眼根本達(dá)不到每個(gè)像素那么大的分辨能力
jpg就是按照這種方式來存儲(chǔ)的

2 位圖文件格式


那么就先說下bmp文件格式吧,,本人不是那種長(zhǎng)篇大論型的 不說廢話。
這個(gè)壓縮包里工程的bin目錄有一個(gè)叫bmpTestImg.bmp兩色的位圖文件,。以16進(jìn)制編輯器打開對(duì)照下圖看 :
這里是下載鏈接

vc++里有定義好的bitmapheader 用來表示位圖頭信息 在C#里沒有,。
其實(shí)沒多大關(guān)系的這只是一種數(shù)據(jù)組織方式
如果你愿意也可以定義這么一個(gè)結(jié)構(gòu)。
gdi與設(shè)備無(wú)關(guān) 但是他并不代表跟設(shè)備沒有任何關(guān)聯(lián) 計(jì)算機(jī)之間傳遞的是圖片文件或者圖像數(shù)據(jù) 并不是gdi對(duì)象,。
微軟幫我們搞了這一層?xùn)|西,,就是說只要是接入windows的設(shè)備我們都可以通過
gdi在那個(gè)設(shè)備上顯示 輸出東西 而不用關(guān)系設(shè)備本身 ,可以說整個(gè)windows提供給我們的就是gdi 所有窗體
等等都是gdi繪制的,,比如說做啥xx編程的時(shí)候要直接操縱顯卡 實(shí)際上直接操縱的方式速度更快但是沒有必要

所謂的24位真彩色 mspaint畫圖新建圖像默認(rèn)存儲(chǔ) 就是24位真彩色 這并不是什么高深技術(shù)因?yàn)?一個(gè)像素的顏色用3個(gè)八位
來表示就是24位真彩色
真彩圖像是說他具有顯示 256x256x256種顏色的能力
還有就是c#里默認(rèn)新建的bitmap對(duì)象就是24位真彩的 并且graphic提供的函數(shù)不能操作非真彩色的位圖

3 水平不咋滴,,還是來敲點(diǎn)代碼吧,\(^o^)/~

先賣個(gè)關(guān)子哈 上面你下載的示例圖片你看到東西了嗎 還是黑乎乎一片 嘿嘿,。如果你看到了那你見鬼了 還是趕快拜拜春哥吧
最近在研究那啥dicom 也學(xué)會(huì)拽文了 嘿嘿
是否可以這么描述:bmp是一種約定俗成的有規(guī)律的數(shù)據(jù)組織方式 不論他在內(nèi)存中 在文件中 他跟特定編程語(yǔ)言無(wú)關(guān) 跟平臺(tái)無(wú)關(guān)
bmp格式簡(jiǎn)而言之一句話 前54字節(jié)存儲(chǔ)文件頭信息最主要就是圖像位數(shù)跟寬度高度,,從54位開始有調(diào)色板則是調(diào)色板信息 無(wú)調(diào)色板則是像素?cái)?shù)據(jù)。
由于本文不是專門探討bmp文件格式 詳細(xì)請(qǐng)參見bmp格式
好下面我們就來讀取這種有規(guī)律的數(shù)據(jù): 寫第一個(gè)按鈕事件的代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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個(gè)字節(jié) 因?yàn)閎mp格式"龜腚"在18字節(jié)那個(gè)地方開始用32位整型存儲(chǔ)圖像的寬度跟高度
    bmpData.Seek(18, SeekOrigin.Begin);
    int width = br.ReadInt32();
    int height = br.ReadInt32();
    MessageBox.Show(string.Format("寬{0},,高{1}", width, height));
    //第11個(gè)字節(jié)處儲(chǔ)存數(shù)據(jù)字節(jié)的起始位置
    bmpData.Seek(10, SeekOrigin.Begin);
    int dataStart = br.ReadInt32();
 
    byte[] datas = new byte[width * height];
    int indx = 0;
    bmpData.Seek(dataStart, SeekOrigin.Begin);
    //注意咯 這是調(diào)色板開始的位置 更改調(diào)色板將會(huì)讓"看不見"的圖像顯示出來
    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();
}

上面的代碼很簡(jiǎn)單滴 (⊙o⊙)哦 都看得懂吧 別忘了要在執(zhí)行文件同級(jí)目錄放上偶的圖片哦 嘿嘿,。
這個(gè)適合用來給girlfriend表白啊啥的O(∩_∩)O哈!
有幾個(gè)需要說明的地方
bmp文件的兩色 并非一定得是黑白 對(duì)吧  可以是紅色綠色,, 也可以是兩種相同的色兒 對(duì)吧
為什么寬度要在第19字節(jié)的位置開始存儲(chǔ) 沒有為什么 這是bmp格式的“龜腚”對(duì)吧 要問去問蓋茨大叔

對(duì)于“流”的操作 seek到前面去了 再進(jìn)行write操作 是否就把對(duì)應(yīng)位置的數(shù)據(jù)“擠”到后面去了呢,?
NO 數(shù)據(jù)流是一種游標(biāo) “覆蓋”型的操作 長(zhǎng)度會(huì)自動(dòng)標(biāo)識(shí)到游標(biāo)到過最遠(yuǎn)的地方 文件流內(nèi)存流都一樣
所以說想要做數(shù)據(jù)插入啊 文件合并啊之類的東東的話得弄兩個(gè)數(shù)據(jù)流對(duì)象哈 互相倒騰數(shù)據(jù) 這樣才能達(dá)到目的。
又扯遠(yuǎn)了哈 打住,。

不是說讀取數(shù)據(jù)嗎 就是讀取像素值數(shù)據(jù)啊,,現(xiàn)在開始 
既然是讀取像素值,咱得一行一行的讀啊 就像掃描一樣的,。實(shí)際上他就是以這種方式存儲(chǔ)的哈 只不過稍微有點(diǎn)不一樣
那就是圖像數(shù)據(jù)每行以四倍字節(jié)為基數(shù)不足以0補(bǔ)齊 乃明白了木有 ,。
比如說這一個(gè)掃描行有3個(gè)像素 那么就是9字節(jié) ,4字節(jié)的倍數(shù)那么他必須要有12字節(jié) 那么剩下的3字節(jié)全是0,。
比如說這一個(gè)掃描行有20個(gè)像素 那么就是60字節(jié) ,,4字節(jié)的倍數(shù)那么他必須要有60字節(jié) 因?yàn)?0/4正好除凈,。

先來說下這個(gè)破公式 ((width * 24 + 31) / 32 * 4) 不知道是哪個(gè)頭腦發(fā)熱的人想出來的 ,注意這里的32是指32位 即4字節(jié),。
實(shí)際上我只想說兩個(gè)字非常扯淡 一定是很深入的掌握了數(shù)據(jù)長(zhǎng)度運(yùn)算的本質(zhì),, 一句把我上面那n多句都代替了
width是圖像寬度 24代表每個(gè)像素位數(shù),。 計(jì)算出實(shí)際字節(jié)數(shù) 先假設(shè)他會(huì)超出一位 補(bǔ)齊31位 然后通過整型數(shù)據(jù)相除的性質(zhì) 除以4字節(jié)得到4字節(jié)的倍數(shù)
注意最終得到的是掃描行的字節(jié)數(shù)
就這樣從圖像左下角第一個(gè)點(diǎn)開始 一行一行從左至右的往上掃描
然后是bmp圖像素的存儲(chǔ)方式是BGR的順序哈 而不是通常的RGB 哦 別搞錯(cuò)了,
以前很菜的時(shí)候用SetPixel()處理像素 被人罵慘了 現(xiàn)在俺依然來寫個(gè)setPix() 嘿嘿 第二個(gè)按鈕的代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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的順序存儲(chǔ)的哦
            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++) 可以看下效果 可以證明在文件中是按照?qǐng)D像從左至右往上 的方式存儲(chǔ)的
通過以上可以看出任何環(huán)境下他都是按照同種規(guī)律存儲(chǔ)存儲(chǔ)的,,就像dicom 只要遵循這種規(guī)律就能通過這種格式實(shí)現(xiàn)數(shù)據(jù)共享,。

都說lockBitmap的方式是最快的 ,確實(shí)是最快的哈 因?yàn)樗鞘褂弥羔樀姆绞?br>下面是把一個(gè)圖像轉(zhuǎn)成灰度圖 你看 不但代碼少了很多 并且還不用費(fèi)盡心思去確定每一個(gè)掃描行的索引 你看 刷的一下 就出來了 嘿嘿
注意有unsafe代碼 在項(xiàng)目->屬性 勾選“允許不安全代碼” :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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();
}

灰度圖 哎呀 跟你說得又俗又土點(diǎn)就是對(duì)每個(gè)像素 rgb三個(gè)值加起來除以3  不想跟你講那些我自己都不怎么明白的東西
但是還是不得不跟你說下所謂的yuv表示方式  y代表明度 說到這個(gè)又得要講下矩陣乘法 真麻煩ya,。

這個(gè)什么意思呢,,先說說矩陣乘法吧
比如你商店里有帽子鞋子 襪子 單價(jià)分別表示為:
[25] [80] [15]
然后今天帽子賣了3件 鞋子賣了1件 襪子賣了兩件,可表示為:
[3]
[1]
[2]
然后今天的收入呢=25x3+80x1+15x2 總共185 用矩陣表示為[185]
我第二天帽子賣了1件 鞋子賣了兩件 襪子賣了3件,,那么這兩天的銷售可表示為:
[3] [1]
[1] [2]
[2] [3]
那么這兩天總共的收入呢=(25x3+80x1+15x2)+(25x1+80x2+15x3) 總共185+230=415 用矩陣表示為[185][230]
沒錯(cuò) 你看到的這就是矩陣乘法 不想講什么線性代數(shù) 什么的那么高深的理論

比如上面RGB轉(zhuǎn)轉(zhuǎn)YUV公式的3行3列 乘 3行1列 乘出來是 3行1列 ,,
規(guī)律就是第一個(gè)矩陣的列跟第二個(gè)矩陣的行一致,得到一個(gè)首行尾列數(shù)的二維矩陣,。
如果rgb值分別是{115,20,65} 那么轉(zhuǎn)換成yuv表示應(yīng)該是
y=115x0.299+20x0.587+65x0.114
u=115x-0.148+20x-0.289+65x0.437
v=115x0.615+20x-0.515+65x-0.1
貌似很難理解 因?yàn)檫@個(gè)跟前面那個(gè)賣東西的又不一樣了可以換個(gè)角度看 ,。
把第二個(gè)矩陣往左“倒下來” 就是說讓他的行跟第一個(gè)矩陣的列 對(duì)齊 是不是感覺好多了O(∩_∩)O哈!
整點(diǎn)復(fù)雜的 那再來隨便整個(gè)吧
[2] [4]      [-1] [6]
[1] [0]      [3 ] [5]
結(jié)果是多少
2x-1+4x3      2x6+4x5
1x-1+0x3      1x6+0x5
最終結(jié)果
[10]      [32]
[-1]      [6]
也可以把它分解為單行單列的來看 就簡(jiǎn)單多了哈
有種很特殊的矩陣 有點(diǎn)像對(duì)角線
任何跟他相乘的矩陣都等于那個(gè)矩陣本身 有點(diǎn)像 “任何數(shù)乘以1 都等于那個(gè)數(shù)本身”
[1][0]
[0][1]

看吧矩陣乘法就是這么神奇的東東,,通過矩陣乘法還可以進(jìn)行角度旋轉(zhuǎn) 縮放等等 
這個(gè)是很高深的研究課題了O(∩_∩)O哈,! 這里就不討論了

終于說完了 俺喝口水了先。也不知講清楚了沒 下面是各種矩陣乘法的示例代碼 關(guān)于為什么是5x5的矩陣這個(gè)可以看下msdn
知識(shí)學(xué)無(wú)止境
第三個(gè)按鈕:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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個(gè)能量值 我們看到屏幕上花花綠綠 顏色是因?yàn)槿齻€(gè)能量值產(chǎn)生差異化 說俗點(diǎn)就是三個(gè)值的比例不一樣
如果三個(gè)值一樣的話那就跟電燈泡無(wú)異了 就是純亮度表示 即我們常說的灰度圖,。
來寫個(gè)手工滴 很山寨滴 效率很低滴 更改亮度的函數(shù)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void light(ref int r, ref int g, ref int b)
{
    //計(jì)算后的平均值
    //增加亮度
    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;

 其實(shí)呢也遠(yuǎn)可以不必這樣 直接rgb分別乘以1.2 或者1.1之類的就可以了 只不過顏色會(huì)失真
示例文件及代碼

 好了終于寫完啦 好累ya

完了 ( ⊙ o ⊙ ) 本來就很菜 這點(diǎn)破秘密全被你們曉得了 以后出去俺還雜混吶

當(dāng)然作為一個(gè)商業(yè)化的軟件 代碼的容錯(cuò)也是很重要的 你看acdsee 你把文件數(shù)據(jù)部分刪除一些他照樣能夠顯示 當(dāng)然這些都是很簡(jiǎn)單的哈,。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多