iOS制作電子簽章

吃晚飯的時(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

效果圖

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,724評(píng)論 25 709
  • 大寶上晚自習(xí),想著自己都沒去過學(xué)校,趁著送大寶,和班主任去交流一下。 去的時(shí)候,班主任正好在教室布置工作,不方便打...
    鷹飛飛閱讀 250評(píng)論 1 2
  • 又是一年開學(xué),還依稀記得去年的這個(gè)時(shí)候爸爸陪我吃了午飯后便走了,留我一個(gè)在宿舍等待著未來三年的到來。 那時(shí)因?yàn)闆]有...
    遇見遇羅克閱讀 197評(píng)論 0 0
  • 文/意小禮 小的時(shí)候,父母常教育我們,不要跟"小混混"做朋友,以免學(xué)壞。這顯然是不僅僅出于關(guān)心,更是出于經(jīng)驗(yàn)的叮囑...
    意小禮閱讀 1,101評(píng)論 2 2

友情鏈接更多精彩內(nèi)容