吃晚飯的時(shí)候, 朋友發(fā)了一組圖片過來, 讓我摳?jìng)€(gè)圖; 對(duì), 沒聽錯(cuò), 知道我是程序猿, 所以讓我摳?jìng)€(gè)圖;

摳圖這個(gè)說法, UI設(shè)計(jì)師們都接受不了; 身為程序猿當(dāng)然要更高雅一些, 能敲代碼的就絕不摳圖;

好了, 進(jìn)入正題; 其實(shí)分析一下, 需求也比較簡(jiǎn)單, 就是把圖片中的白色或者接近白色的背景換為透明, 黑色簽名露出來; 下面是圖片處理前:

首先說一下返回原始圖片文字, 代碼如下:
//返回原始圖像(將圖片的背景置為透明)
- (UIImage *)returnOrginImage:(UIImage *)sourceImage
{
//分配內(nèi)存
const int imageWidth = sourceImage.size.width;
const int imageHeight = sourceImage.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), sourceImage.CGImage);
//遍歷像素
int pixelNum = imageWidth * imageHeight;
uint32_t* pCurPtr = rgbImageBuf;
for (int i = 0; i < pixelNum; i++, pCurPtr++){
//接近白色
//將像素點(diǎn)轉(zhuǎn)成子節(jié)數(shù)組來表示---RGBA
//ptr[0]:透明度,ptr[1]:R,ptr[2]:G,ptr[3]:B
//分別取出RGB值后。進(jìn)行判斷需不需要設(shè)成透明
uint8_t* ptr = (uint8_t*)pCurPtr;
if (ptr[1] > 140 && ptr[2] > 140 && ptr[3] > 140) {
//當(dāng)RGB值都大于140則比較接近白色的都將透明度設(shè)為0
//demo中的圖片有點(diǎn)灰, 所以設(shè)置了140, 可以根據(jù)需要自行設(shè)置
ptr[0] = 0;
}
}
//將內(nèi)存轉(zhuǎn)成image
CGDataProviderRef dataProvider =CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight, nil);
CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight,8, 32, bytesPerRow, colorSpace,kCGImageAlphaLast |kCGBitmapByteOrder32Little, dataProvider,NULL, true,kCGRenderingIntentDefault);
CGDataProviderRelease(dataProvider);
UIImage *resultUIImage = [UIImage imageWithCGImage:imageRef];
//釋放
CGImageRelease(imageRef);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return resultUIImage;
}
參數(shù)bytesPerRow代表被渲染內(nèi)存區(qū)域中每行所占字節(jié), 其中4代表每個(gè)像素點(diǎn)RGBA所占的字節(jié), 之后乘以imageHeight得到所占內(nèi)存空間;colorspace用于被渲染內(nèi)存區(qū)域的顏色;最關(guān)鍵的代碼就是將RGB值為白色或趨近與白色的透明度設(shè)為0;我這里取了140是因?yàn)檫@張圖片背景有點(diǎn)灰, 大家可以自行設(shè)置;
效果如下:

看得出文字有點(diǎn)模糊, 這是因?yàn)樘幚磉^程是對(duì)每一個(gè)像素直接設(shè)置為透明, 圖片并不是只有黑白兩個(gè)顏色, 所以文字周邊的區(qū)域就會(huì)變暗了, 再加上原圖給的也不是很清晰, 那應(yīng)該怎么處理呢?
這里涉及到灰度圖, 代碼如下:
//返回灰度圖像
-(UIImage*)returnGrayImage:(UIImage*)sourceImage{
int width = sourceImage.size.width;
int height = sourceImage.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGContextRef context = CGBitmapContextCreate (nil,width,height,8,0,colorSpace,kCGImageAlphaNone);
CGColorSpaceRelease(colorSpace);
if (context == NULL) {
return nil;
}
CGContextDrawImage(context,CGRectMake(0, 0, width, height), sourceImage.CGImage);
UIImage *grayImage = [UIImage imageWithCGImage:CGBitmapContextCreateImage(context)];
CGContextRelease(context);
return grayImage;
}
效果圖:

說到灰度圖, 就不得不提一個(gè)很著名的彩色轉(zhuǎn)灰度的心理學(xué)公式:
Gray = R*0.299 + G*0.587 + B*0.114
算法處理如下:
a. 首先將彩色圖像轉(zhuǎn)換為灰度圖像
b. 計(jì)算灰度圖像的算術(shù)平均值M
c. 以M為閾值,完成對(duì)灰度圖二值化( 大于閾值M,像素點(diǎn)賦值為白色,否則賦值為黑
色)
上面的代碼還看不出來, 應(yīng)該是方法內(nèi)部做了處理, 咱們來看??下面的:
for (size_t i = 0; i < height; i++) {
for (size_t j = 0; j < width; j++){
// 設(shè)置每個(gè)像素的rgba值
size_t pixelIndex = i * width * 4 + j * 4;
unsigned char red = data[pixelIndex];
unsigned char green = data[pixelIndex + 1];
unsigned char blue = data[pixelIndex + 2];
//取灰度值
unsigned char gray = 0.299 * red + 0.587 * green + 0.114 * blue;
data[pixelIndex] = gray; // r
data[pixelIndex + 1] = gray; // g
data[pixelIndex + 2] = gray; // b
}
}
附:
維基灰度算法
處理成灰度圖后基本只剩下黑白兩種顏色; 為了尋求最優(yōu)解, 我們?cè)侔哑聊恢邪咨兺该? 黑色加深, 就得到所謂的高保真效果了;
代碼如下:
//返回高保真圖像(只返回黑白兩種顏色)
- (UIImage *)returnHightQualityImage:(UIImage *)sourceImage{
NSData *imageData = UIImagePNGRepresentation(sourceImage);
CGImageSourceRef sourceRef = CGImageSourceCreateWithData((CFDataRef)imageData, NULL);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, NULL);
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
unsigned char *data = calloc(width * height * 4, sizeof(unsigned char)); // 取圖片首地址
size_t bitsPerComponent = 8; // r g b a 每個(gè)component bits數(shù)目
size_t bytesPerRow = width * 4; // 一張圖片每行字節(jié)數(shù)目 (每個(gè)像素點(diǎn)包含r g b a 四個(gè)字節(jié))
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); // 創(chuàng)建rgb顏色空間
CGContextRef context = CGBitmapContextCreate(data, width, height, bitsPerComponent, bytesPerRow, space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
for (size_t i = 0; i < height; i++)
{
for (size_t j = 0; j < width; j++)
{
// 設(shè)置每個(gè)像素的rgba值
size_t pixelIndex = i * width * 4 + j * 4;
unsigned char red = data[pixelIndex];
unsigned char green = data[pixelIndex + 1];
unsigned char blue = data[pixelIndex + 2];
//取灰度值
unsigned char gray = 0.299 * red + 0.587 * green + 0.114 * blue;
gray = gray > 150 ? 255 : 0;
data[pixelIndex] = gray; // r
data[pixelIndex + 1] = gray; // g
data[pixelIndex + 2] = gray; // b
data[pixelIndex + 3] = gray == 255 ? 0 : 255; // a (255 代表透明)
}
}
CGImageRef newImage = CGBitmapContextCreateImage(context);
UIImage *image = [[UIImage alloc] initWithCGImage:newImage];
return image;
}
最終效果圖:

最后附上github地址;
demo下載
擴(kuò)展
用 C 語言畫光--Milo Yip
效果圖
