根據(jù)灰度值填充字符-單文件單線程版
更新時(shí)間:2013年01月08日 11:46:05 作者:
本文介紹如何實(shí)現(xiàn):類似于一個(gè)圖片,處理后,根據(jù)不同的灰度值,填充不同的字符等相關(guān)功能,感興趣的朋友可以了解下哦
看到軟二的群里,某童鞋發(fā)了個(gè)自己的java大作業(yè)的截圖,類似于一個(gè)圖片,處理后,根據(jù)不同的灰度值,填充不同的字符。故,我也用C#來寫個(gè)玩玩~
首先,圖片讀入內(nèi)存,然后- - 有三種處理方式,
第一種是getPixel,然后setPixel。。。
第二種是將圖片數(shù)據(jù)讀出,放到byte數(shù)組中,然后去讀,再copy回去。
第三種是不copy數(shù)據(jù),直接在當(dāng)前圖片數(shù)據(jù)流上操作,然后再解鎖就可以了。
由于去年做過類似的工作,所以知道效率問題,第一種。。。速度會(huì)很慢(相比較第二種和第三種)。第二種和第三種相差不是很多。但是第三種需要使用unsafe字段。
在這里,我選用第三種進(jìn)行操作。
界面就不說了。一個(gè)按鈕,兩個(gè)pictureBox,一個(gè)存原圖,一個(gè)存字符圖。
處理代碼如下:
private void button1_Click(object sender, EventArgs e)
{
//打開文件
OpenFileDialog open = new OpenFileDialog();
open.Filter = "jpeg圖片文件|*.jpg";
if (open.ShowDialog() != DialogResult.OK)
return;
string filePath = open.FileName;
//打開圖片,顯示原始圖
Image img = Image.FromFile(filePath);
sourcePicturebox.Image =img ;
//再次讀取一次圖潘
Bitmap bitmap = new Bitmap(img);
//鎖定圖片處理區(qū)域
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
//初始化字符串?dāng)?shù)組
string[] str = new string[bitmap.Height];
//不安全代碼塊
unsafe
{
//獲取首指針
byte* ptr = (byte*)(bmpData.Scan0);
for (int height = 0; height < bmpData.Height; height++)
{
//由于測(cè)試圖片是32RGB圖,所以,ptr一次移動(dòng)四位
for (int width = 0; width < bmpData.Width; width++,ptr+=4)
{
str[height] += ((byte)(0.333 * ptr[0] + 0.333 * ptr[1] + 0.333 * ptr[2])) > 128 ? "*" : " ";
}
//為防止圖片數(shù)據(jù)占用不為4的倍數(shù),所以這里用stride,跳過多余的字節(jié)
ptr += bmpData.Stride - bmpData.Width * 4;
}
}
//圖片解鎖
bitmap.UnlockBits(bmpData);
//初始化打印字符參數(shù),以及新建一個(gè)空白圖片
//空白圖片大小。。。。。測(cè)試得到的。后續(xù)改進(jìn)
Font font=new System.Drawing.Font ("宋體",20.5f);
Bitmap resultBitmap = new Bitmap(img.Width * 14, img.Height * 14);
Graphics graphics = Graphics.FromImage(resultBitmap);
graphics.Clear(Color.White);
SolidBrush brush=new SolidBrush(Color.Black);
//打印字符,一次一行
for (int i = 0; i < img.Height; i++)
{
graphics.DrawString(str[i], font, brush,0,i*14);
}
//MessageBox.Show(font.GetHeight().ToString());
resultPicturebox.Image = resultBitmap;
//文件保存
SaveFileDialog saveDialog = new SaveFileDialog();
saveDialog.Filter = "jpeg圖片文件|*.jpg";
if(saveDialog.ShowDialog()==DialogResult.OK)
resultBitmap.Save(saveDialog.FileName, ImageFormat.Jpeg);
}
實(shí)驗(yàn)結(jié)果如下:
實(shí)驗(yàn)的結(jié)果經(jīng)過多次調(diào)整打印參數(shù),結(jié)果還可以。但是,略顯緊湊。
***********************************************分隔線******************************************************
小結(jié):
由于選的圖,是32rgb的,在第一次處理的時(shí)候,還有點(diǎn)問題,當(dāng)成24rgb處理了。。。。出錯(cuò)。。。然后,改后,一開始以為多處的透明分量在低位,結(jié)果。。。原來在高位。不過還好最后成功了(其實(shí)就試下唄。。。先不處理成字符,而直接改變一個(gè)分量為0,然后看效果唄~)
原始圖片只有500*500不到,轉(zhuǎn)換后。。。圖片為6700+*6700+。。。太大了。。。所以,以后得先處理圖片,使其縮小,再進(jìn)行處理。這里也有二種處理方式,第一是處理原始圖像,而是處理那個(gè)字符串?dāng)?shù)組。感覺吧,還是第一種直觀些。
然后,后續(xù)打印,可以考慮不用string存,而用char[,]來存,這樣,又可以用指針了,總覺得指針會(huì)快些~
看官可見,上面有一個(gè)測(cè)試按鈕,但是,我沒有給出代碼,其實(shí)那個(gè)是用來測(cè)試多線程的。打開文件的時(shí)候,允許同時(shí)選中多個(gè)文件,然后操作。這里就用到多線程。但是不知道為何,多線程操作的時(shí)候,出來的圖片就不對(duì)了,如果多線程多了后,還會(huì)拋出異常。。。。。就算我就開一個(gè)線程,操作一幅圖片,也會(huì)導(dǎo)致錯(cuò)誤的結(jié)果。。。所以多線程的代碼沒有上傳,等改好再說吧。
PS.這個(gè)改好,估計(jì)還要很多天。。。。。畢竟。。。要考試了。。。還是復(fù)習(xí)去吧。。。前2天在家一點(diǎn)書沒看啊。
當(dāng)然后續(xù)改進(jìn),不一定就一個(gè)多線程,還可以進(jìn)行字符自定義填充?。ㄟ@個(gè)簡(jiǎn)單點(diǎn))?;蛘?,給個(gè)字符串填充額。然后多個(gè)灰度級(jí),不同的灰度級(jí)給不同的字符填充。再比如。。??梢宰屗幚硭蓄愋偷撵o態(tài)圖。。。。(由于那個(gè)32RGB的關(guān)系。。。特意看了下,還有好多種的。。。)
首先,圖片讀入內(nèi)存,然后- - 有三種處理方式,
第一種是getPixel,然后setPixel。。。
第二種是將圖片數(shù)據(jù)讀出,放到byte數(shù)組中,然后去讀,再copy回去。
第三種是不copy數(shù)據(jù),直接在當(dāng)前圖片數(shù)據(jù)流上操作,然后再解鎖就可以了。
由于去年做過類似的工作,所以知道效率問題,第一種。。。速度會(huì)很慢(相比較第二種和第三種)。第二種和第三種相差不是很多。但是第三種需要使用unsafe字段。
在這里,我選用第三種進(jìn)行操作。
界面就不說了。一個(gè)按鈕,兩個(gè)pictureBox,一個(gè)存原圖,一個(gè)存字符圖。
處理代碼如下:
復(fù)制代碼 代碼如下:
private void button1_Click(object sender, EventArgs e)
{
//打開文件
OpenFileDialog open = new OpenFileDialog();
open.Filter = "jpeg圖片文件|*.jpg";
if (open.ShowDialog() != DialogResult.OK)
return;
string filePath = open.FileName;
//打開圖片,顯示原始圖
Image img = Image.FromFile(filePath);
sourcePicturebox.Image =img ;
//再次讀取一次圖潘
Bitmap bitmap = new Bitmap(img);
//鎖定圖片處理區(qū)域
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
//初始化字符串?dāng)?shù)組
string[] str = new string[bitmap.Height];
//不安全代碼塊
unsafe
{
//獲取首指針
byte* ptr = (byte*)(bmpData.Scan0);
for (int height = 0; height < bmpData.Height; height++)
{
//由于測(cè)試圖片是32RGB圖,所以,ptr一次移動(dòng)四位
for (int width = 0; width < bmpData.Width; width++,ptr+=4)
{
str[height] += ((byte)(0.333 * ptr[0] + 0.333 * ptr[1] + 0.333 * ptr[2])) > 128 ? "*" : " ";
}
//為防止圖片數(shù)據(jù)占用不為4的倍數(shù),所以這里用stride,跳過多余的字節(jié)
ptr += bmpData.Stride - bmpData.Width * 4;
}
}
//圖片解鎖
bitmap.UnlockBits(bmpData);
//初始化打印字符參數(shù),以及新建一個(gè)空白圖片
//空白圖片大小。。。。。測(cè)試得到的。后續(xù)改進(jìn)
Font font=new System.Drawing.Font ("宋體",20.5f);
Bitmap resultBitmap = new Bitmap(img.Width * 14, img.Height * 14);
Graphics graphics = Graphics.FromImage(resultBitmap);
graphics.Clear(Color.White);
SolidBrush brush=new SolidBrush(Color.Black);
//打印字符,一次一行
for (int i = 0; i < img.Height; i++)
{
graphics.DrawString(str[i], font, brush,0,i*14);
}
//MessageBox.Show(font.GetHeight().ToString());
resultPicturebox.Image = resultBitmap;
//文件保存
SaveFileDialog saveDialog = new SaveFileDialog();
saveDialog.Filter = "jpeg圖片文件|*.jpg";
if(saveDialog.ShowDialog()==DialogResult.OK)
resultBitmap.Save(saveDialog.FileName, ImageFormat.Jpeg);
}
實(shí)驗(yàn)結(jié)果如下:
實(shí)驗(yàn)的結(jié)果經(jīng)過多次調(diào)整打印參數(shù),結(jié)果還可以。但是,略顯緊湊。
***********************************************分隔線******************************************************
小結(jié):
由于選的圖,是32rgb的,在第一次處理的時(shí)候,還有點(diǎn)問題,當(dāng)成24rgb處理了。。。。出錯(cuò)。。。然后,改后,一開始以為多處的透明分量在低位,結(jié)果。。。原來在高位。不過還好最后成功了(其實(shí)就試下唄。。。先不處理成字符,而直接改變一個(gè)分量為0,然后看效果唄~)
原始圖片只有500*500不到,轉(zhuǎn)換后。。。圖片為6700+*6700+。。。太大了。。。所以,以后得先處理圖片,使其縮小,再進(jìn)行處理。這里也有二種處理方式,第一是處理原始圖像,而是處理那個(gè)字符串?dāng)?shù)組。感覺吧,還是第一種直觀些。
然后,后續(xù)打印,可以考慮不用string存,而用char[,]來存,這樣,又可以用指針了,總覺得指針會(huì)快些~
看官可見,上面有一個(gè)測(cè)試按鈕,但是,我沒有給出代碼,其實(shí)那個(gè)是用來測(cè)試多線程的。打開文件的時(shí)候,允許同時(shí)選中多個(gè)文件,然后操作。這里就用到多線程。但是不知道為何,多線程操作的時(shí)候,出來的圖片就不對(duì)了,如果多線程多了后,還會(huì)拋出異常。。。。。就算我就開一個(gè)線程,操作一幅圖片,也會(huì)導(dǎo)致錯(cuò)誤的結(jié)果。。。所以多線程的代碼沒有上傳,等改好再說吧。
PS.這個(gè)改好,估計(jì)還要很多天。。。。。畢竟。。。要考試了。。。還是復(fù)習(xí)去吧。。。前2天在家一點(diǎn)書沒看啊。
當(dāng)然后續(xù)改進(jìn),不一定就一個(gè)多線程,還可以進(jìn)行字符自定義填充?。ㄟ@個(gè)簡(jiǎn)單點(diǎn))?;蛘?,給個(gè)字符串填充額。然后多個(gè)灰度級(jí),不同的灰度級(jí)給不同的字符填充。再比如。。??梢宰屗幚硭蓄愋偷撵o態(tài)圖。。。。(由于那個(gè)32RGB的關(guān)系。。。特意看了下,還有好多種的。。。)
相關(guān)文章
c#不使用系統(tǒng)api實(shí)現(xiàn)可以指定區(qū)域屏幕截屏功能
這篇文章主要介紹了不使用系統(tǒng)API通過純c#實(shí)現(xiàn)屏幕指定區(qū)域截屏功能,截屏后還可以保存圖象文件,大家參考使用吧2014-01-01
VSCode調(diào)試C#程序及附缺失.dll文件的解決辦法
這篇文章主要介紹了VSCode調(diào)試C#程序及附缺失.dll文件的解決辦法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
C#實(shí)現(xiàn)TCP客戶端和服務(wù)器的基本功能
本文將介紹如何使用C#實(shí)現(xiàn)TCP客戶端和服務(wù)器的基本功能,客戶端與服務(wù)器可以相互發(fā)送消息,文章通過代碼講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-12-12
C#實(shí)現(xiàn)左截取和右截取字符串實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)左截取和右截取字符串實(shí)例,是針對(duì)字符串的常用操作,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10
C#結(jié)合html2canvas切割圖片并導(dǎo)出到PDF
html2canvas?是一個(gè)?JavaScript?庫(kù),它可以把任意一個(gè)網(wǎng)頁中的元素繪制到指定的?canvas?中,本文將結(jié)合html2canvas進(jìn)行切割圖片并導(dǎo)出到PDF,感興趣的可以了解下2025-02-02
c#多線程中Lock()關(guān)鍵字的用法小結(jié)
本篇文章主要是對(duì)c#多線程中Lock()關(guān)鍵字的用法進(jìn)行了詳細(xì)的總結(jié)介紹,需要的朋友可以過來參考下,希望對(duì)大家有所幫助2014-01-01
c#winform窗口頁面一打開就加載的實(shí)現(xiàn)方式
這篇文章主要介紹了c#winform窗口頁面一打開就加載的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
基于C#實(shí)現(xiàn)一個(gè)溫濕度監(jiān)測(cè)小工具
這篇文章主要為大家詳細(xì)介紹了如何基于C#實(shí)現(xiàn)一個(gè)溫濕度監(jiān)測(cè)小工具,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下2023-01-01

