前幾天有這樣一個(gè)需求,在手機(jī)上把公章給摳出來,做成PNG可以貼在其他圖片上面,于是就有了今天的主題.
先放完成后的效果圖

Demo中的圖片分辨率為440*387,處理只需要一毫秒~
可以看出來,沒有一點(diǎn)卡頓的感覺,,所以效率上還是很不錯(cuò)噠~
如果你很心急:<a href="#core">核心代碼</a>
理論基礎(chǔ)
了解一些基本概念還是很有必要滴
-
RGBA
R:紅色
G:綠色
B:藍(lán)色
A:透明度
紅綠藍(lán)為三原色,可以說我們看到的任何顏色都是由這三個(gè)顏色構(gòu)成的.所以是圖像組成必不可少的一部分
而加入了A則多了一個(gè)透明度的描述,常見于PNG格式的圖片.
例如微信的表情包,除了主要輪廓外,其他的色域都是采用的都是當(dāng)前聊天會(huì)話的背景色,這就是利用了Alpha來操作出的效果
-
像素
一條線可以看成是被無數(shù)個(gè)點(diǎn)構(gòu)成的.同理,我們可以認(rèn)為一張圖片也是由一定數(shù)量的點(diǎn)構(gòu)成的.
以Demo中圖片為例子,分辨率440*387的圖片,一行440的像素點(diǎn),有387行,那么他就包含了有約17萬個(gè)像素點(diǎn)
,對(duì)這些像素點(diǎn)的操作,將直接影響到圖片的顯示
-
灰色
在RGB的表現(xiàn)中,如何呈現(xiàn)出灰色呢?
說來慚愧,我一開始一度以為所謂的灰就是黑色的透明度不一樣,但事實(shí)當(dāng)然不是這樣啦!
可以參考下表
| R | G | B | 顏色 |
|---|---|---|---|
| 0 | 0 | 0 | 黑色 |
| 50 | 50 | 50 | 深灰色 |
| 178 | 178 | 178 | 淺灰色 |
| 255 | 255 | 255 | 白色 |
可以看出來,灰色其實(shí)是RGB三個(gè)值相等,并且隨著數(shù)值的增大,顏色逐漸變淺,和透明度是沒有任何關(guān)系的

-
二值化
所謂的二值化,其實(shí)是將圖片的色域空間變?yōu)榛疑?在CG框架中,可以直接使用CGColorSpaceCreateDeviceGray來進(jìn)行操作,不過因?yàn)槲覀兂俗屗兓抑?還需要對(duì)透明度做操作,所以這里自己使用算法來進(jìn)行計(jì)算.
RGB轉(zhuǎn)灰色的計(jì)算公式有很多種,我們這里使用一種較為經(jīng)典的算法
double Gray = R*0.3+G*0.59+B*0.11;
其中RGB都是以0~255取值,得到的結(jié)果即灰色的RGB色值
處理方法
因?yàn)槲覀兿胍玫街饕妮喞?所以只需要對(duì)像素進(jìn)行操作即可,那么就很簡(jiǎn)單啦,直接上代碼
UIImage *image = [UIImage imageNamed:@"1.png"];
// 分配內(nèi)存
const int imageWidth = image.size.width;
const int imageHeight = image.size.height;
size_t bytesPerRow = imageWidth * 4;
uint32_t* rgbImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);
// 創(chuàng)建context
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage);
// 遍歷像素
int pixelNum = imageWidth * imageHeight;
uint32_t* pCurPtr = rgbImageBuf;
for (int i = 0; i < pixelNum; i++, pCurPtr++)
{
// ABGR
uint8_t* ptr = (uint8_t*)pCurPtr;
int B = ptr[1];
int G = ptr[2];
int R = ptr[3];
double Gray = R*0.3+G*0.59+B*0.11;
if (Gray > filterValue || (Gray == filterValue && filterValue == 0)) {
ptr[0] = 0;
}else{
// ptr[3] = 0xff;
}
}
核心代碼也就是for循環(huán)那一段
因?yàn)槊總€(gè)像素都包含了RGBA的信息,而255在十六進(jìn)制中以0xFF表示
所以假設(shè)顏色為白色不透明的情況下,RGBA的表現(xiàn)方式應(yīng)該為0xFF FF FF FF,所以使用uint8_t來接收,
但是尷尬的是他的排列方式并不是RGBA,而是ABGR ??,一度讓我以為代碼寫錯(cuò)了.
代碼中的Gray就是轉(zhuǎn)換為灰度圖顯示的顏色,而filterValue則是過濾系數(shù),取值范圍在0~255;值越大,顯示的圖像也就越多,Demo中使用UISlider來控制.
這樣色彩變化與過濾都放在了一起,減少了頻繁操作像素信息.
因?yàn)檎率羌t色的,所以我當(dāng)時(shí)將需要顯示的像素點(diǎn)變?yōu)榱思t色,而被過濾掉的像素點(diǎn),則直接設(shè)置為了透明.大家可根據(jù)需求自行設(shè)置
<a id="core" > </a>
完整代碼
- (void)drawImage:(double)filterValue
{
UIImage *image = [UIImage imageNamed:@"1.png"];
// 分配內(nèi)存
const int imageWidth = image.size.width;
const int imageHeight = image.size.height;
size_t bytesPerRow = imageWidth * 4;
uint32_t* rgbImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);
// 創(chuàng)建context
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage);
// 遍歷像素
int pixelNum = imageWidth * imageHeight;
uint32_t* pCurPtr = rgbImageBuf;
for (int i = 0; i < pixelNum; i++, pCurPtr++)
{
// ABGR
uint8_t* ptr = (uint8_t*)pCurPtr;
int B = ptr[1];
int G = ptr[2];
int R = ptr[3];
double Gray = R*0.3+G*0.59+B*0.11;
if (Gray > filterValue || (Gray == filterValue && filterValue == 0)) {
ptr[0] = 0;
}else{
// ptr[3] = 0xff;
}
}
// 將內(nèi)存轉(zhuǎn)成image
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight,NULL);
CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, 8, 32, bytesPerRow, colorSpace,kCGImageAlphaLast | kCGBitmapByteOrder32Little, dataProvider,NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease(dataProvider);
UIImage* resultUIImage = [UIImage imageWithCGImage:imageRef scale:image.scale orientation:image.imageOrientation];
// 釋放
CGImageRelease(imageRef);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
self.outputImg.image = resultUIImage;
}