iOS二值化扣圖

前幾天有這樣一個需求,在手機上把公章給摳出來,做成PNG可以貼在其他圖片上面,于是就有了今天的主題.

先放完成后的效果圖

filter.gif

Demo中的圖片分辨率為440*387,處理只需要一毫秒~
可以看出來,沒有一點卡頓的感覺,,所以效率上還是很不錯噠~

如果你很心急:<a href="#core">核心代碼</a>

理論基礎

了解一些基本概念還是很有必要滴

  • RGBA

R:紅色
G:綠色
B:藍色
A:透明度
紅綠藍為三原色,可以說我們看到的任何顏色都是由這三個顏色構成的.所以是圖像組成必不可少的一部分
而加入了A則多了一個透明度的描述,常見于PNG格式的圖片.

例如微信的表情包,除了主要輪廓外,其他的色域都是采用的都是當前聊天會話的背景色,這就是利用了Alpha來操作出的效果

  • 像素

一條線可以看成是被無數(shù)個點構成的.同理,我們可以認為一張圖片也是由一定數(shù)量的點構成的.
以Demo中圖片為例子,分辨率440*387的圖片,一行440的像素點,有387行,那么他就包含了有約17萬個像素點
,對這些像素點的操作,將直接影響到圖片的顯示

  • 灰色

在RGB的表現(xiàn)中,如何呈現(xiàn)出灰色呢?
說來慚愧,我一開始一度以為所謂的灰就是黑色的透明度不一樣,但事實當然不是這樣啦!
可以參考下表

R G B 顏色
0 0 0 黑色
50 50 50 深灰色
178 178 178 淺灰色
255 255 255 白色

可以看出來,灰色其實是RGB三個值相等,并且隨著數(shù)值的增大,顏色逐漸變淺,和透明度是沒有任何關系的


  • 二值化

所謂的二值化,其實是將圖片的色域空間變?yōu)榛疑?在CG框架中,可以直接使用CGColorSpaceCreateDeviceGray來進行操作,不過因為我們除了讓他變灰之外,還需要對透明度做操作,所以這里自己使用算法來進行計算.

RGB轉(zhuǎn)灰色的計算公式有很多種,我們這里使用一種較為經(jīng)典的算法
double Gray = R*0.3+G*0.59+B*0.11;
其中RGB都是以0~255取值,得到的結(jié)果即灰色的RGB色值

處理方法

因為我們想要得到主要的輪廓,所以只需要對像素進行操作即可,那么就很簡單啦,直接上代碼

    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)那一段
因為每個像素都包含了RGBA的信息,而255在十六進制中以0xFF表示
所以假設顏色為白色不透明的情況下,RGBA的表現(xiàn)方式應該為0xFF FF FF FF,所以使用uint8_t來接收,
但是尷尬的是他的排列方式并不是RGBA,而是ABGR ??,一度讓我以為代碼寫錯了.

代碼中的Gray就是轉(zhuǎn)換為灰度圖顯示的顏色,而filterValue則是過濾系數(shù),取值范圍在0~255;值越大,顯示的圖像也就越多,Demo中使用UISlider來控制.
這樣色彩變化與過濾都放在了一起,減少了頻繁操作像素信息.
因為章是紅色的,所以我當時將需要顯示的像素點變?yōu)榱思t色,而被過濾掉的像素點,則直接設置為了透明.大家可根據(jù)需求自行設置

<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;
}

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末危喉,一起剝皮案震驚了整個濱河市土匀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌澜汤,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菠秒,死亡現(xiàn)場離奇詭異拙泽,居然都是意外死亡,警方通過查閱死者的電腦和手機幕随,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宿接,“玉大人赘淮,你說我怎么就攤上這事∧丽” “怎么了梢卸?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長副女。 經(jīng)常有香客問我蛤高,道長,這世上最難降的妖魔是什么碑幅? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任戴陡,我火速辦了婚禮,結(jié)果婚禮上沟涨,老公的妹妹穿的比我還像新娘恤批。我一直安慰自己,他們只是感情好裹赴,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布喜庞。 她就那樣靜靜地躺著诀浪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪延都。 梳的紋絲不亂的頭發(fā)上雷猪,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機與錄音晰房,去河邊找鬼春宣。 笑死,一個胖子當著我的面吹牛嫉你,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播躏惋,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼幽污,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了簿姨?” 一聲冷哼從身側(cè)響起距误,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扁位,沒想到半個月后准潭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡域仇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年刑然,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片暇务。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡泼掠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出垦细,到底是詐尸還是另有隱情择镇,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布括改,位于F島的核電站腻豌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嘱能。R本人自食惡果不足惜吝梅,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望焰檩。 院中可真熱鬧憔涉,春花似錦、人聲如沸析苫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妈嘹。三九已至爵政,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間路翻,已是汗流浹背跪但。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工履羞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屡久。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓忆首,卻偏偏與公主長得像,于是被迫代替她去往敵國和親被环。 傳聞我的和親對象是個殘疾皇子糙及,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 前幾天有這樣一個需求,在手機上把公章給摳出來,做成PNG可以貼在其他圖片上面,于是就有了今天的主題.先放完成后的效...
    念念不忘一個丫頭的容閱讀 726評論 1 2
  • 卷首語 歡迎來到 objc.io 的第三期! 這一期都是關于視圖層的筛欢。當然視圖層有很多方面浸锨,我們需要把它們縮小到幾...
    評評分分閱讀 1,765評論 0 18
  • 繪制像素到屏幕上 answer-huang22 Mar 2014 分享文章 一個像素是如何繪制到屏幕上去的?有很多...
    阿貍旅途T恤閱讀 1,635評論 0 7
  • 接下來我們看Base文件夾下的UIKIt文件夾的內(nèi)容版姑。 1.UIColor+YYAdd 這里看了這個類柱搜,里面有許多...
    充滿活力的早晨閱讀 2,274評論 0 1
  • 古奇,不是名貴包包剥险,沒錯聪蘸,這是一只狗的名字。普通的不能再普通的一只狗表制。多年前的我甚至沒有想到這個名字究竟意味著什么...
    風聽月閱讀 557評論 0 11