国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

位圖圖像原圖修改

2019-11-09 15:52:07
字體:
供稿:網(wǎng)友
(via:泰然網(wǎng)) 想象一張最好的生活自拍照。它是很高大尚滴并且以后會有用武之地。轉(zhuǎn)發(fā),票選將會使你獲得成千上萬份的關(guān)注,因?yàn)樗_實(shí)很酷很帥。現(xiàn)在,如果你有什么辦法,可以讓它看起來更加的高大尚。。。 這就是圖形圖像處理要做到的!它可以讓你的照片帶上更多的特殊效果,比如修改顏色,與其它的圖片進(jìn)行合成等等。 在這兩部分教程中,你需要先弄明白一些圖形圖像處理的基礎(chǔ)知識。接著,你可以利用如下四個(gè)流行的圖形圖像處理方法編寫一個(gè)實(shí)現(xiàn)“幽靈圖像過濾器”的程序:1:位圖圖像原圖修改2:使用Core Graphics庫3:使用Core Image庫4:使用GPUImage庫的第三部分 在圖形圖像處理教程的第一節(jié),主要講解位圖圖像原圖的修改。一但你明白基本的圖形處理方法,那么其它的相關(guān)內(nèi)容你也會較容易的弄明白。在教程的第二部分,主要介紹另外的三種修改圖像方法。 本教程假設(shè)你擁有關(guān)于IOS系統(tǒng)和Object-C的基礎(chǔ),但在開始本教程前不需要擁有任何關(guān)于圖形圖像處理的知識。 開始在開始寫代碼之前,先理解一些關(guān)于圖形圖像處理的基本概念很是需要。所以,先別急,放輕松,讓我們在最短的時(shí)間里去了解一下圖形圖像的內(nèi)部工作原理。 第一件事情,看一下我們本教程中的新朋友...幽靈!  不要怕,幽靈不是真的鬼魂。實(shí)際上,它只是一張圖像。簡單來說,它就是由一堆1和0組成的。這樣說聽上去會更好一些。 什么是圖形圖像一張圖像就是像素點(diǎn)的集合,每一個(gè)像素都是一個(gè)單獨(dú),明了的顏色。圖像一般情況下都存儲成數(shù)組,你可以把他們相像成2維數(shù)組。 這一張是縮放版本的幽靈,被放大后:  圖像中這些小的“方塊”就是像素,每一像素只表示一種顏色。當(dāng)成百上千萬的像素集體到一起后,就構(gòu)成了圖形圖像。 如何用字節(jié)來表示顏色表示圖形的方式有許多種。在本教程中使用的是最簡單的:32位RGBA模式。 如同它的名字一樣,32位RGBA模式會將一個(gè)顏色值存儲在32位,或者4個(gè)字節(jié)中。每一個(gè)字節(jié)存儲一個(gè)部分或者一個(gè)顏色通道。這4個(gè)部分分別是: ~ R代表紅色 ~ G代表綠色 ~ B代表藍(lán)色 ~ A代表透明度 正如你所知道的,紅,綠和藍(lán)是所有顏色的基本顏色集。你幾乎可以使用他們創(chuàng)建搭配出任何想要的顏色。 由于使用8位表示每一種顏色值,那么使用32位RGBA模式實(shí)際上可以創(chuàng)建出不透明的顏色的總數(shù)是256256256種,已經(jīng)接近17億種。驚嘆,那是好多好多好多的顏色! alpha通道與其它的不同。你可以把它當(dāng)成透明的東西,就像UIView的alpah屬性。 透明顏色意味著沒有任何的顏色,除非在它的后面有另外一種顏色;它的主要功能就是要告訴圖像處理這個(gè)像素的透明度是多少,于是,就會有多少顏色值穿透過它而顯示出來。 你將會通過本節(jié)后面的內(nèi)容更新深入的了解。 總結(jié)一下,一個(gè)圖形就是像素的集體,并且每一個(gè)像素只能表示一種顏色。本節(jié),你已經(jīng)了解了32位RGBA模式。 提示:你有沒有想過,位圖的結(jié)構(gòu)組成?一張位圖就是一張2D的地圖,每一塊就是一個(gè)像素!像素就是地圖的每一塊。哈哈! 現(xiàn)在你已經(jīng)了解了用字節(jié)表示顏色的基礎(chǔ)了。不過在你開始著手寫代碼前,還有三個(gè)以上的概念需要你了解。 顏色空間使用RGB模式表示顏色是顏色空間的一個(gè)例子。它只是眾多存儲顏色方法中的一種。另外一種顏色空間是灰階空間。像它的名字一樣,所有的圖形都只有黑和白,只需要保存一個(gè)值來表示這種顏色。 下面這種使用RGB模式表示的顏色,人類的肉眼是很難識別的。Red: 0 Green:104 Blue:55 你認(rèn)為RGB值為[0,104,55]會產(chǎn)生一種什么顏色? 認(rèn)真的思考一下,你也許會說是一種藍(lán)綠色或者綠色,但那是錯(cuò)的。原來,你所看到的是深綠色。 另外兩種比較常見的顏色空間是HSV和YUV。 HSV,使用色調(diào),飽和度和亮度來直觀的存儲顏色值。你可以把這三個(gè)部分這樣來看: ·色調(diào)就是顏色·飽和度就是這個(gè)顏色有多么的飽滿·值就是顏色的亮度有多亮 在這種顏色空間中,如果你發(fā)現(xiàn)自己并不知道HSV的值,那么通過它的三個(gè)值,可以很容易的相像出大概是什么顏色。 RGB和HSV顏色空間的區(qū)別是很容易理解的,請看下面的圖像: YUV是另外一種常見的顏色空間,電視機(jī)使用的就是這種方式。 最開始的時(shí)候,電視機(jī)只有灰階空間一種顏色通道。后來,當(dāng)彩色電影出現(xiàn)后,就有了2種通道。當(dāng)然,如果你想在本教程中使用YUV,那么你需要去研究更多關(guān)于YUV和其它顏色空間的相關(guān)知識。 NOTE:同樣的顏色空間,你也可以使用不同的方法表示顏色。比如16位RGB模式,可以使用5個(gè)字節(jié)存儲R,6個(gè)字節(jié)存儲G,5個(gè)字節(jié)存儲B。 為什么用6個(gè)字節(jié)存儲綠色,5個(gè)字節(jié)存儲藍(lán)色?這是一個(gè)有意思的問題,答案就是因?yàn)?#30524;球。人類的眼球?qū)G色比較敏感,所以人類的眼球更空間分辨出綠色的顏色值變化。 坐標(biāo)系統(tǒng)既然一個(gè)圖形是由像素構(gòu)成的平面地圖,那么圖像的原點(diǎn)需要說明一下。通常原點(diǎn)在圖像的左上角,Y軸向下;或者原點(diǎn)在圖像的左下,Y軸向上。 沒有固定的坐標(biāo)系統(tǒng),蘋果在不同的地方可能會使用不同的坐標(biāo)系。 目前,UIImage和UIView使用的是左上原點(diǎn)坐標(biāo),Core Image和Core Graphics使用的是左下原點(diǎn)坐標(biāo)。這個(gè)概念很重要,當(dāng)你遇到圖像繪制倒立問題的時(shí)候你就知道了。 圖形壓縮這是在你開始編寫代碼前的最后一個(gè)需要了解的概念了!原圖的每一個(gè)像素都被存儲在各自的內(nèi)存中。 如果你使用一張8像素的圖形做運(yùn)算,它將會消耗810^6像素4比特/像素=32兆字節(jié)內(nèi)存。關(guān)注一下數(shù)據(jù)! 這就是為什么會出現(xiàn)jpeg,png和其它圖形格式的原因。這些都是圖形壓縮格式。 當(dāng)GPU在繪制圖像的時(shí)候,會使用大量內(nèi)存把圖像的原始尺寸進(jìn)行解壓縮。如果你的程序占用了過多的內(nèi)存,那么操作系統(tǒng)會將進(jìn)程殺死(程序崩潰)。所以請確定你的程序使用較大的圖像進(jìn)行過測試。 我需要一些行動(dòng)… 關(guān)注一下像素現(xiàn)在,你已經(jīng)基礎(chǔ)了解了圖形圖像的內(nèi)部工作原理,已經(jīng)可以開始編寫代碼嘍。今天你將會開發(fā)一款改變自己照片的程序,叫做SpookCam,該程序會把一張幽靈的圖像放到你的照片中! 下載工具包在xcode中打開該項(xiàng)目,編譯并運(yùn)行。在你的手機(jī)上會看到如下的圖像:  在控制臺,你會看到如下的輸出:   當(dāng)前的程序可以加載這張幽靈的圖像,并得到圖像的所有像素值,打印出每個(gè)像素的亮度值到日志中。 亮度值是神馬?它就是紅色,綠色和藍(lán)色通過的平均值。 注意輸出日志外圍的亮度值都為0,這意味著他們代碼的是黑色。然而,他們的透明度的值是0,所以它們是透明不可見的。為了證明這一點(diǎn),試著將imageView的背景顏色設(shè)置成紅色,然后再次編譯并運(yùn)行。   現(xiàn)在快速的瀏覽一下代碼。ViewController.m 中使用 UIImagePickerController 來在相冊中取得圖像或者使用機(jī)機(jī)獲得圖像。 當(dāng)它選定一張圖像后,調(diào)用-setupWithImage:在這行中,輸出了每一像素的亮度值到日志中。定位到ViewController.m中的logPixelsOfImage,查看方法中的開始部分: 
// 1. CGImageRef inputCGImage = [image CGImage]; NSUInteger width =                 CGImageGetWidth(inputCGImage); NSUInteger height = CGImageGetHeight(inputCGImage);  // 2. NSUInteger bytesPerPixel = 4; NSUInteger bytesPerRow = bytesPerPixel *     width; NSUInteger bitsPerComponent = 8;  UInt32 * pixels; pixels = (UInt32 *) calloc(height * width,     sizeof(UInt32));  // 3. CGColorSpaceRef colorSpace =     CGColorSpaceCreateDeviceRGB(); CGContextRef context =     CGBitmapContextCreate(pixels, width, height,     bitsPerComponent, bytesPerRow, colorSpace,     kCGImageAlphaPRemultipliedLast |     kCGBitmapByteOrder32Big);  // 4. CGContextDrawImage(context, CGRectMake(0,     0, width, height), inputCGImage);  // 5. Cleanup CGColorSpaceRelease(colorSpace); CGContextRelease(context);  現(xiàn)在,讓我們分段的來看一下: 1:第一部分:把UIImage對象轉(zhuǎn)換為需要被核心圖形庫調(diào)用的CGImage對象。同時(shí),得到圖形的寬度和高度。 2:第二部分:由于你使用的是32位RGB顏色空間模式,你需要定義一些參數(shù)bytesPerPixel(每像素大小)和bitsPerComponent(每個(gè)顏色通道大小),然后計(jì)算圖像bytesPerRow(每行有大)。最后,使用一個(gè)數(shù)組來存儲像素的值。 3:第三部分:創(chuàng)建一個(gè)RGB模式的顏色空間CGColorSpace和一個(gè)容器CGBitmapContext,將像素指針參數(shù)傳遞到容器中緩存進(jìn)行存儲。在后面的章節(jié)中將會進(jìn)一步研究核圖形庫。 4:第四部分:把緩存中的圖形繪制到顯示器上。像素的填充格式是由你在創(chuàng)建context的時(shí)候進(jìn)行指定的。 5:第五部分:清除colorSpace和context. NOTE:當(dāng)你繪制圖像的時(shí)候,設(shè)備的GPU會進(jìn)行解碼并將它顯示在屏幕。為了訪問本地?cái)?shù)據(jù),你需要一份像素的復(fù)制,就像剛才做的那樣。 此時(shí)此刻,pixels存儲著圖像的所有像素信息。下面的幾行代碼會對pixels進(jìn)行遍歷,并打印:
// 1. #define Mask8(x) ( (x) & 0xFF ) #define R(x) ( Mask8(x) ) #define G(x) ( Mask8(x >> 8 ) ) #define B(x) ( Mask8(x >> 16) )  NSLog(@"Brightness of image:"); // 2. UInt32 * currentPixel = pixels; for (NSUInteger j = 0; j < height; j++) {   for (NSUInteger i = 0; i < width; i++) {     // 3.     UInt32 color = *currentPixel;     printf("%3.0f ",     (R(color)+G(color)+B(color))/3.0);     // 4.     currentPixel++;   }   printf("/n"); }  代碼解釋: 1:定義了一些簡單處理32位像素的宏。為了得到紅色通道的值,你需要得到前8位。為了得到其它的顏色通道值,你需要進(jìn)行位移并取截取。 2:定義一個(gè)指向第一個(gè)像素的指針,并使用2個(gè)for循環(huán)來遍歷像素。其實(shí)也可以使用一個(gè)for循環(huán)從0遍歷到width*height,但是這樣寫更容易理解圖形是二維的。 3:得到當(dāng)前像素的值賦值給currentPixel并把它的亮度值打印出來。 4:增加currentPixel的值,使它指向下一個(gè)像素。如果你對指針的運(yùn)算比較生疏,記住這個(gè):currentPixel是一個(gè)指向UInt32的變量,當(dāng)你把它加1后,它就會向前移動(dòng)4字節(jié)(32位),然后指向了下一個(gè)像素的值。 提示:還有一種非正統(tǒng)的方法就是把currentPiexl聲明為一個(gè)指向8字節(jié)的類型的指針,比如char。這種方法,你每增加1,你將會移動(dòng)圖形的下一個(gè)顏色通道。與它進(jìn)行位移運(yùn)算,你會得到顏色通道的8位數(shù)值。 此時(shí)此刻,這個(gè)程序只是打印出了原圖的像素信息,但并沒有進(jìn)行任何修改!下面將會教你如何進(jìn)行修改。 SpookCame-原圖修改四種研究方法都會在本小節(jié)進(jìn)行,你將會花費(fèi)更多的時(shí)間在本節(jié),因?yàn)樗藞D形圖像處理的第一原則。掌握了這個(gè)方法你會明白其它庫所做的。 在本方法中,你會遍歷每一個(gè)像素,就像之前做的那個(gè),但這次,將會對每個(gè)像素進(jìn)行新的賦值。 這種方法的優(yōu)點(diǎn)是容易實(shí)現(xiàn)和理解;缺點(diǎn)就是掃描大的圖形和效果的時(shí)候會更復(fù)雜,不精簡。 正如你在程序開始看到的,ImageProcessor類已經(jīng)存在。將它應(yīng)用到ViewController中,替換-setupWithImage,代碼如下: 
- (void)setupWithImage:(UIImage*)image {   UIImage * fixedImage = [image     imageWithFixedOrientation];   self.workingImage = fixedImage;    // Commence with processing!   [ImageProcessor     sharedProcessor].delegate = self;   [[ImageProcessor sharedProcessor]     processImage:fixedImage]; }  注釋掉 -viewDidLoad 中下面的代碼:
// [self setupWithImage:[UIImage     imageNamed:@"Ghost_tiny.png"]]; 現(xiàn)在,打開 ImageProcessor.m。如你所見,ImageProcessor 是單例模式,調(diào)用 -processUsingPixels 來加載圖像,然后通過 ImageProcessorDelegate 返回輸出。 -processsUsingPixels:是之前你所看到獲得圖形像素代碼的一種復(fù)制品,如同inputImage。注意兩個(gè)額外的宏A(x)和RGBAMake(r,g,b,a)的定義,用來方便處理。 編譯,并運(yùn)行。從相冊(拍照)選擇一張圖片,它將會出現(xiàn)在屏幕上:   照片中的人看上去在放松,是時(shí)候把幽靈放進(jìn)去了! 在processUsingPixels的返回語句前,添加如下代碼,創(chuàng)建一個(gè)幽靈的CGImageRef對象。UIImage * ghostImage = [UIImage imageNamed:@"ghost"];CGImageRef ghostCGImage = [ghostImage CGImage]; 現(xiàn)在,做一些數(shù)學(xué)運(yùn)算來確定幽靈圖像放在原圖的什么位置。 
CGFloat ghostImageaspectRatio =     ghostImage.size.width /     ghostImage.size.height; NSInteger targetGhostWidth = inputWidth *     0.25; CGSize ghostSize =     CGSizeMake(targetGhostWidth, targetGhostWidth     / ghostImageAspectRatio); CGPoint ghostOrigin =     CGPointMake(inputWidth * 0.5, inputHeight *     0.2);  以上代碼會把幽靈的圖像寬度縮小25%,并把它的原點(diǎn)設(shè)定在點(diǎn)ghostOrigin。 下一步是創(chuàng)建一張幽靈圖像的緩存圖, 
NSUInteger ghostBytesPerRow = bytesPerPixel * ghostSize.width; UInt32 * ghostPixels = (UInt32     *)calloc(ghostSize.width * ghostSize.height,     sizeof(UInt32));   CGContextRef ghostContext =     CGBitmapContextCreate(ghostPixels,     ghostSize.width, ghostSize.height,                                        bit    sPerComponent, ghostBytesPerRow, colorSpace,                                        kCG    ImageAlphaPremultipliedLast |     kCGBitmapByteOrder32Big); CGContextDrawImage(ghostContext,     CGRectMake(0, 0, ghostSize.width,     ghostSize.height),ghostCGImage);  上面的代碼和你從inputImage中獲得像素信息一樣。不同的地方是,圖像會被縮小尺寸,變得更小了。 現(xiàn)在已經(jīng)到了把幽靈圖像合并到你的照片中的最佳時(shí)間了。 合并:像前面提到的,每一個(gè)顏色都有一個(gè)透明通道來標(biāo)識透明度。并且,你每創(chuàng)建一張圖像,每一個(gè)像素都會有一個(gè)顏色值。 所以,如果遇到有透明度和半透明的顏色值該如何處理呢? 答案是,對透明度進(jìn)行混合。在最頂層的顏色會使用一個(gè)公式與它后面的顏色進(jìn)行混合。公式如下: 
NewColor = TopColor * TopColor.Alpha + BottomColor * (1 - TopColor.Alpha)  這是一個(gè)標(biāo)準(zhǔn)的線性差值方程。 ·當(dāng)頂層透明度為1時(shí),新的顏色值等于頂層顏色值。·當(dāng)頂層透明度為0時(shí),新的顏色值于底層顏色值。·最后,當(dāng)頂層的透明度值是0到1之前的時(shí)候,新的顏色值會混合借于頂層和底層顏色值之間。 還可以用 premultiplied alpha的方法。 當(dāng)處理成千上萬像素的時(shí)候,他的性能會得以發(fā)揮。 好,回到幽靈圖。 如同其它位圖運(yùn)算一樣,你需要一些循環(huán)來遍歷每一個(gè)像素。但是,你只需要遍歷那些你需要修改的像素。 把下面的代碼添加到processUsingPixels的下面,還是放在返回語句的前面: 
NSUInteger offsetPixelCountForInput = ghostOrigin.y * inputWidth + ghostOrigin.x; for (NSUInteger j = 0; j < ghostSize.height; j++) { for (NSUInteger i = 0; i < ghostSize.width; i++) { UInt32 * inputPixel = inputPixels + j * inputWidth + i + offsetPixelCountForInput; UInt32 inputColor = *inputPixel;       UInt32 * ghostPixel = ghostPixels + j     * (int)ghostSize.width + i;     UInt32 ghostColor = *ghostPixel;       // Do some processing here         } }  通過對幽靈圖像像素?cái)?shù)的循環(huán)和offsetPixelCountForInput獲得輸入的圖像。記住,雖然你使用的是2維數(shù)據(jù)存儲圖像,但在內(nèi)存他它實(shí)際上是一維的。 下一步,添加下面的代碼到注釋語句 Do some processing here的下面來進(jìn)行混合: 
// Blend the ghost with 50% alpha CGFloat ghostAlpha = 0.5f * (A(ghostColor)     / 255.0); UInt32 newR = R(inputColor) * (1 -     ghostAlpha) + R(ghostColor) * ghostAlpha; UInt32 newG = G(inputColor) * (1 -     ghostAlpha) + G(ghostColor) * ghostAlpha; UInt32 newB = B(inputColor) * (1 -     ghostAlpha) + B(ghostColor) * ghostAlpha;   // Clamp, not really useful here :p newR = MAX(0,MIN(255, newR)); newG = MAX(0,MIN(255, newG)); newB = MAX(0,MIN(255, newB));   *inputPixel = RGBAMake(newR, newG, newB,     A(inputColor));  這部分有2點(diǎn)需要說明: 1:你將幽靈圖像的每一個(gè)像素的透明通道都乘以了0.5,使它成為半透明狀態(tài)。然后將它混合到圖像中像之前討論的那樣。 2:clamping部分將每個(gè)顏色的值范圍進(jìn)行限定到0到255之間,雖然一般情況下值不會越界。但是,大多數(shù)情況下需要進(jìn)行這種限定防止發(fā)生意外的錯(cuò)誤輸出。 最后一步,添加下面的代碼到 processUsingPixels 的下面,替換之前的返回語句:
// Create a new UIImage CGImageRef newCGImage =     CGBitmapContextCreateImage(context); UIImage * processedImage = [UIImage     imageWithCGImage:newCGImage];   return processedImage; 上面的代碼創(chuàng)建了一張新的UIImage并返回它。暫時(shí)忽視掉內(nèi)存泄露問題。編譯并運(yùn)行,你將會看到漂浮的幽靈圖像:  好了,完成了,這個(gè)程序簡直就像個(gè)病毒! 黑白顏色最后一種效果。嘗試自己實(shí)現(xiàn)黑白顏色效果。為了做到這點(diǎn),你需要把每一個(gè)像素的紅色,綠色,藍(lán)色通道的值設(shè)定成三個(gè)通道原始顏色值的平均值,就像開始的時(shí)候輸出幽靈圖像所有像素亮度值那樣。 在注釋語句// create a new UIImage前添加上一步的代碼 。 找到了嗎? 
// Convert the image to black and white for (NSUInteger j = 0; j < inputHeight; j++) { for (NSUInteger i = 0; i < inputWidth; i++) { UInt32 * currentPixel = inputPixels + (j * inputWidth) + i; UInt32 color = *currentPixel;       // Average of RGB = greyscale     UInt32 averageColor = (R(color) +     G(color) + B(color)) / 3.0;       *currentPixel = RGBAMake(averageColor,     averageColor, averageColor, A(color));   } }  最后的一步就是清除內(nèi)存。ARC不能代替你對CGImageRefs和CGContexts進(jìn)行管理。添加如下代碼到返回語句之前。
CGColorSpaceRelease(colorSpace); CGContextRelease(context); CGContextRelease(ghostContext); free(inputPixels); free(ghostPixels); 編譯并運(yùn)行,不要被結(jié)果嚇到: 
發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 新邵县| 淅川县| 张家港市| 淮北市| 固阳县| 阿坝县| 西华县| 保亭| 收藏| 曲沃县| 古丈县| 潮安县| 新乡县| 吕梁市| 鹤峰县| 辽宁省| 清河县| 抚远县| 玛多县| 汉阴县| 老河口市| 甘德县| 阜宁县| 新余市| 桑日县| 左权县| 临湘市| 松溪县| 神木县| 章丘市| 舞阳县| 揭阳市| 常山县| 黄山市| 清水县| 司法| 黄石市| 万宁市| 来安县| 永仁县| 交口县|