iOS製作電子簽章

火之玉發表於2017-12-14

原文連結:http://www.jianshu.com/p/8a951ceec54f

吃晚飯的時候, 朋友發了一組圖片過來, 讓我摳個圖; 對, 沒聽錯, 知道我是程式猿, 所以讓我摳個圖;

iOS製作電子簽章

摳圖這個說法, UI設計師們都接受不了; 身為程式猿當然要更高雅一些, 能敲程式碼的就絕不摳圖;

iOS製作電子簽章

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

iOS製作電子簽章

首先說一下返回原始圖片文字, 程式碼如下:

//返回原始影像(將圖片的背景置為透明)
- (UIImage *)returnOrginImage:(UIImage *)sourceImage
{
    //分配記憶體
    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);
    
    //建立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++){
        //接近白色
        //將畫素點轉成子節陣列來表示---RGBA
        //ptr[0]:透明度,ptr[1]:R,ptr[2]:G,ptr[3]:B
        
        //分別取出RGB值後。進行判斷需不需要設成透明
        uint8_t* ptr = (uint8_t*)pCurPtr;
        if (ptr[1] > 140 && ptr[2] > 140 && ptr[3] > 140) {
            //當RGB值都大於140則比較接近白色的都將透明度設為0
            //demo中的圖片有點灰, 所以設定了140, 可以根據需要自行設定
            ptr[0] = 0;
        }
    }
    
    //將記憶體轉成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;
}
複製程式碼

引數bytesPerRow代表被渲染記憶體區域中每行所佔位元組, 其中4代表每個畫素點RGBA所佔的位元組, 之後乘以imageHeight得到所佔記憶體空間;colorspace用於被渲染記憶體區域的顏色;最關鍵的程式碼就是將RGB值為白色或趨近與白色的透明度設為0;我這裡取了140是因為這張圖片背景有點灰, 大家可以自行設定; 效果如下:

原圖影像

看得出文字有點模糊, 這是因為處理過程是對每一個畫素直接設定為透明, 圖片並不是只有黑白兩個顏色, 所以文字周邊的區域就會變暗了, 再加上原圖給的也不是很清晰, 那應該怎麼處理呢? 這裡涉及到灰度圖, 程式碼如下:

//返回灰度影像
-(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;
}
複製程式碼

效果圖:

灰度圖

說到灰度圖, 就不得不提一個很著名的彩色轉灰度的心理學公式: Gray = R*0.299 + G*0.587 + B*0.114

演算法處理如下: a. 首先將彩色影像轉換為灰度影像 b. 計算灰度影像的算術平均值M c. 以M為閾值,完成對灰度圖二值化( 大於閾值M,畫素點賦值為白色,否則賦值為黑 色)

上面的程式碼還看不出來, 應該是方法內部做了處理, 我們們來看?下面的:

 for (size_t i = 0; i < height; i++) {
        for (size_t j = 0; j < width; j++){
            // 設定每個畫素的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
        }
    }
複製程式碼

附: 維基灰度演算法

處理成灰度圖後基本只剩下黑白兩種顏色; 為了尋求最優解, 我們再把螢幕中白色變透明, 黑色加深, 就得到所謂的高保真效果了; 程式碼如下:

//返回高保真影像(只返回黑白兩種顏色)
- (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 每個component bits數目
    size_t bytesPerRow = width * 4; // 一張圖片每行位元組數目 (每個畫素點包含r g b a 四個位元組)
    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); // 建立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++)
        {
            // 設定每個畫素的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下載

擴充套件 用 C 語言畫光--Milo Yip

效果圖

相關文章