iOS中圖片處理之馬賽克算法

馬賽克

馬賽克在圖片效果中應(yīng)該是一種最常見的處理方式匹涮,日常生活中也幾乎處處可見贴硫。前段時間項目中要實現(xiàn)圖片馬賽克處理,就研究了一下目派。其實坤候,用蘋果濾鏡CIFilter就能非常便捷的實現(xiàn)加碼,但使用的過程中我發(fā)現(xiàn)濾鏡只能處理.png格式的圖片企蹭,如果遇到.jpeg格式的圖片就沒有效果了白筹,于是決定研究一下馬賽克算法,發(fā)現(xiàn)可以通過操作圖片的像素點來實現(xiàn)同樣的效果谅摄。當(dāng)然后者的實用性更加廣泛徒河,隨便你什么類型的圖片都可以。文章最后還有涂抹馬賽克效果實現(xiàn)以及“復(fù)原”的Demo螟凭,希望多多star支持~

操作像素點實現(xiàn)馬賽克

我們都知道圖片是一個一個像素點構(gòu)成的虚青,其實很早之前就有想過為什么所有的圖片都是矩形的?有沒有那種不規(guī)則的圖片螺男?計算機中的圖片為什么都是矩形的棒厘?顯示圓形也只能周圍透明?,估計只有非科班出身的我才會問這種問題吧~簡單來說下隧,其實就是為了統(tǒng)一奢人、更加方便的來處理圖片,所以圖片就是由像素矩陣構(gòu)成的淆院,平時我們看到的不規(guī)則的圖片沒有顏色的地方只是透明了而已何乎。然后我就會想能不能局部的改變圖片的顏色呢,比如把指定的不規(guī)則區(qū)域顏色改為別的顏色,以上都是自己以前胡亂想的支救,研究了之后發(fā)現(xiàn)其實都可以實現(xiàn)(不過沒那么精確)抢野,我們可以通過操作圖片的像素點來實現(xiàn),直接上代碼吧……

+ (NSArray *)getRGBsArrFromImage:(UIImage *)image{
    //1.get the image into your data buffer
    CGImageRef imageRef = [image CGImage];
    NSUInteger imageW = CGImageGetWidth(imageRef);
    NSUInteger imageH = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    NSUInteger bytesPerPixel = 4;//一個像素四個分量各墨,即ARGB
    NSUInteger bytesPerRow = bytesPerPixel * imageW;
    unsigned char *rawData = (unsigned char *)calloc(imageH*imageW*bytesPerPixel, sizeof(unsigned char));
    NSUInteger bitsPerComponent = 8;//每個分量8個字節(jié)
    /*
     參數(shù)1:數(shù)據(jù)源
     參數(shù)2:圖片寬
     參數(shù)3:圖片高
     參數(shù)4:表示每一個像素點指孤,每一個分量大小
     在我們圖像學(xué)中,像素點:ARGB組成 每一個表示一個分量(例如贬堵,A恃轩,R,G黎做,B)
     在我們計算機圖像學(xué)中每一個分量的大小是8個字節(jié)
     參數(shù)5:每一行大胁骢恕(其實圖片是由像素數(shù)組組成的)
     如何計算每一行的大小,所占用的內(nèi)存
     首先計算每一個像素點大姓舻睢(我們?nèi)∽畲笾担?ARGB是4個分量 = 每個分量8個字節(jié) * 4
     參數(shù)6:顏色空間
     參數(shù)7:是否需要透明度
     */
    CGContextRef context = CGBitmapContextCreate(rawData, imageW, imageH, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGContextDrawImage(context, CGRectMake(0, 0, imageW, imageH), imageRef);
    
    //2.Now your rawData contains the image data int the RGBA8888 pixel format
    NSUInteger blackPixel = 0;
    NSMutableArray *pixelsArr = [NSMutableArray array];
    for (int y = 0; y < imageH; y++) {
        for (int x = 0; x < imageW; x++) {
            NSUInteger byteIndex = bytesPerRow*y + bytesPerPixel*x;
            //rawData一維數(shù)組存儲方式RGBA(第一個像素)RGBA(第二個像素)
            NSUInteger red = rawData[byteIndex];
            NSUInteger green = rawData[byteIndex+1];
            NSUInteger blue = rawData[byteIndex+2];
            NSUInteger alpha = rawData[byteIndex+3];
            XPixelItem *pixelItem = [[XPixelItem alloc] init];
            pixelItem.color = [UIColor colorWithRed:red/255.0 green:green/255.0 blue:blue/255.0 alpha:alpha/255.0];
            pixelItem.location = CGPointMake(x, y);
            [pixelsArr addObject:pixelItem];
            if  (red+green+blue == 0 && (alpha/255.0 >= 0.5)){//計算黑色部分所占比例
                blackPixel++;
            }
        }
    }
    NSLog(@"黑色所占的面積--%f,%lu",blackPixel*1.0/(imageW*imageH),(unsigned long)pixelsArr.count);
    imageRef = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    free(rawData);
    return pixelsArr;
}

其中最主要的方法就是CGBitmapContextCreate筷厘,根據(jù)分配好的內(nèi)存創(chuàng)建一個Bitmap的上下文,其中rawData中存放的就是我們所需要的像素點的集合伟桅。每個像素點由ARGB四個分量組成敞掘,我們看到的不規(guī)則圖片沒有顏色的地方也就是A為0的像素點,像素在數(shù)組中存放的是方式也是一個分量一個分量的存進去的楣铁,這樣我們就可以通過修改數(shù)組中的數(shù)據(jù)來實現(xiàn)修改像素點玖雁。通過上述方法我們就可以把一個圖片的局部不規(guī)則區(qū)域修改顏色,效果如下圖:

修改圖片像素.jpeg
馬賽克算法

馬賽克核心算法的大概原理就是把某一個點的顏色賦值給它周圍的指定區(qū)域盖腕,這個區(qū)域大小可以我們自己來定義。

unsigned char *pixels[4] = {0};
if (i % sizeLevel == 0) {
   if (j % sizeLevel == 0) {
        memcpy(pixels, bitMapData+4*currentIndex, 4);
   }else{
        //將上一個像素點的值賦給第二個
        memcpy(bitMapData+4*currentIndex, pixels, 4);
   }
}else{
   preCurrentIndex = (i-1)*imageW+j;
   memcpy(bitMapData+4*currentIndex, bitMapData+4*preCurrentIndex, 4);
}

memcpy指的是c和c++使用的內(nèi)存拷貝函數(shù)溃列,memcpy函數(shù)的功能是從源src所指的內(nèi)存地址的起始位置開始拷貝n個字節(jié)到目標(biāo)dest所指的內(nèi)存地址的起始位置中。其實就是先把某個像素點的ARGB存到一個空數(shù)組pixels中听隐,然后遍歷像素點补鼻,如果是sizeLevel的整數(shù)倍就獲取新的像素信息雅任,不是的話就更換當(dāng)前像素點的信息,這樣就能實現(xiàn)sizeLevel大小區(qū)域的顏色是統(tǒng)一的了沪么,也就是我們看到的馬賽克中一個方塊區(qū)域。下邊這張圖片描述的就很貼切


馬賽克

其實這樣做的話還會有問題禽车,因為透明區(qū)域的像素點RGB信息也為0寇漫,0,0州胳,跟黑色一樣,這么一來后邊再跟根據(jù)bitmap去繪制圖片的時候陋葡,會把透明區(qū)域當(dāng)成黑色來處理亚亲,所以我在中間進行了一下過濾

if (red+green+blue == 0 && (alpha/255.0 <= 0.5)) {
     rawData[currentIndex*4] = 255;
     rawData[currentIndex*4+1] = 255;
     rawData[currentIndex*4+2] = 255;
     rawData[currentIndex*4+3] = 0;
     continue;
}

這種做法也不太嚴謹,但暫時想不到什么好的辦法腐缤。

涂抹實現(xiàn)馬賽克

馬賽克平時的運用更多的是跟用戶交互息息相關(guān)的,比如手指涂抹區(qū)域打上馬賽克肛响,其實這種實現(xiàn)也挺簡單岭粤。剛開始做的時候覺得還要去計算,但這樣顯然不易于實現(xiàn)特笋。其實用兩張圖片就可以搞定了剃浇,一張是原圖,用imageView來顯示猎物;一張是用馬賽克處理過的圖片虎囚,用CALayer來顯示;馬賽克處理過的圖片覆蓋在原圖上邊蔫磨,然后利用layer的mask屬性來控制CALayer指定區(qū)域的顯示與否淘讥。

self.imageLayer = [CALayer layer];
self.imageLayer.frame = self.bounds;
[self.layer addSublayer:self.imageLayer];
        
self.shapeLayer = [CAShapeLayer layer];
self.shapeLayer.frame = self.bounds;
self.shapeLayer.lineCap = kCALineCapRound;
self.shapeLayer.lineJoin = kCALineJoinRound;
self.shapeLayer.lineWidth = 20;
self.shapeLayer.strokeColor = [UIColor blueColor].CGColor;
self.shapeLayer.fillColor = nil;//此處必須設(shè)為nil,否則后邊添加addLine的時候會自動填充

self.imageLayer.mask = self.shapeLayer;
self.path = CGPathCreateMutable();

然后我們在touchMove方法中根據(jù)手指移動軌跡設(shè)置self.shapeLayer的path屬性就可以實現(xiàn)想要的效果了堤如。我把這些都封裝在XScratchView類中了蒲列,使用的時候只需要初始化并給圖片屬性賦值,

XScratchView *scratchView = [[XScratchView alloc] initWithFrame:CGRectMake(0, 100, kScreenWidth, 300)];
scratchView.surfaceImage = [UIImage imageNamed:@"smoke.jpeg"];
scratchView.mosaicImage = [XRGBTool getMosaicImageWith:[UIImage imageNamed:@"smoke.jpeg"] level:0];

復(fù)原時只需要調(diào)用recover方法搀罢,

[_scratchView recover];

需要保存的時候只需要截取圖片區(qū)域就可以獲取加碼后的圖片了蝗岖。效果如下

馬賽克.gif

具體實現(xiàn)代碼都在我的RGBTool這個Demo中,有什么問題還請大家多多指教榔至,共同進步抵赢!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市唧取,隨后出現(xiàn)的幾起案子铅鲤,更是在濱河造成了極大的恐慌,老刑警劉巖兵怯,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彩匕,死亡現(xiàn)場離奇詭異,居然都是意外死亡媒区,警方通過查閱死者的電腦和手機驼仪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門掸犬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绪爸,你說我怎么就攤上這事湾碎。” “怎么了奠货?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵介褥,是天一觀的道長。 經(jīng)常有香客問我递惋,道長柔滔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任睛廊,我火速辦了婚禮杉编,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘邓馒。我一直安慰自己嘶朱,他們只是感情好疏遏,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布挂疆。 她就那樣靜靜地躺著,像睡著了一般缤言。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胆萧,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天跌穗,我揣著相機與錄音,去河邊找鬼蚌吸。 笑死,一個胖子當(dāng)著我的面吹牛羹唠,可吹牛的內(nèi)容都是我干的娄昆。 我是一名探鬼主播缝彬,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼扒俯!你這毒婦竟也來了一疯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤墩邀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體只盹,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年站削,在試婚紗的時候發(fā)現(xiàn)自己被綠了孵稽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡菩鲜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出猛频,到底是詐尸還是另有隱情,我是刑警寧澤鹿寻,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布诽凌,位于F島的核電站,受9級特大地震影響侣诵,放射性物質(zhì)發(fā)生泄漏狱窘。R本人自食惡果不足惜疯暑,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望妇拯。 院中可真熱鬧,春花似錦仗嗦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至躲胳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坯苹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工恐仑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人裳仆。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓济赎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親司训。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

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