iOS 圖片抖動算法

弗洛伊德-斯坦伯格抖動算法

這是一個真實的魔法技術。它愚弄了你的眼睛和大腦,讓你覺得本身看到的顏色要比實際的多。

image.png

通常來講,抖動是經過增長人工噪聲去減小一個圖像的顏色空間,主旨在於,一個區域的光量應該保持一致。

image.png

弗洛伊德-斯坦伯格抖動算法對周圍的像素使用非均勻分佈的量化偏差達到抖動的目的。這就意味着要先將中心像素四捨五入爲0或1,然後將殘差加入其周圍的像素中。

image.png

以上你看到的三張圖片都是灰階抖動的,它們所有都是隻由兩種顏色的噪音組成,而其他的信息,固然是由於你的大腦在轉嘍。

算法實現

#pragma mark - 分配內存
 uint32_t* oldImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);
 uint32_t* newImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);
#pragma mark - 建立context
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();// 色彩範圍的容器
CGContextRef oldContext = CGBitmapContextCreate(oldImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
CGContextDrawImage(oldContext, CGRectMake(0, 0, imageWidth, imageHeight), self.CGImage);
CGContextRef newContext = CGBitmapContextCreate(newImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
CGContextDrawImage(newContext, CGRectMake(0, 0, imageWidth, imageHeight), self.CGImage);
複製代碼
#pragma mark -遍歷像素計算殘差
 //殘差
                    int eRgb[3];
                    if (nearColor == 0) {
                        newptr[3] = 0;
                        newptr[2] = 0;
                        newptr[1] = 0;
                        newptr[0] = 255;
                        eRgb[0] = r;
                        eRgb[1] = g;
                        eRgb[2] = b;
                    } else {
                        newptr[3] = 255;
                        newptr[2] = 255;
                        newptr[1] = 255;
                        newptr[0] = 255;
                        eRgb[0] = r-255;
                        eRgb[1] = g-255;
                        eRgb[2] = b-255;
                    }
                    //殘差 16分之 七、五、三、1
                    float rate1 = 0.4375;
                    float rate2 = 0.3125;
                    float rate3 = 0.1875;
                    float rate4 = 0.0625;
                    uint32_t rgb1 = [self getPixel:oldImageBuf width:imageWidth height:imageHeight row:row column:column+1 rate:rate1 eRgb:eRgb];
                    uint32_t rgb2 = [self getPixel:oldImageBuf width:imageWidth height:imageHeight row:row+1 column:column rate:rate2 eRgb:eRgb];
                    uint32_t rgb3 = [self getPixel:oldImageBuf width:imageWidth height:imageHeight row:row+1 column:column-1 rate:rate3 eRgb:eRgb];
                    uint32_t rgb4 = [self getPixel:oldImageBuf width:imageWidth height:imageHeight row:row+1 column:column+1 rate:rate4 eRgb:eRgb];
                    [self setPixel:oldImageBuf width:imageWidth height:imageHeight row:row column:column+1 value:rgb1];
                    [self setPixel:oldImageBuf width:imageWidth height:imageHeight row:row+1 column:column value:rgb2];
                    [self setPixel:oldImageBuf width:imageWidth height:imageHeight row:row+1 column:column-1 value:rgb3];
                    [self setPixel:oldImageBuf width:imageWidth height:imageHeight row:row+1 column:column+1 value:rgb4];
複製代碼
#pragma mark - 獲取像素
- (uint32_t)getPixel:(uint32_t*)imageBuf width:(int)width height:(int)height   row:(int)row column:(int)column rate:(float)rate eRgb:(int *)eRgb {
    if (row < 0 || row >= height || column < 0 || column >= width) {
        return 0xFFFFFFFF;
    }
    int index = row * width + column;
    uint32_t *ptr = imageBuf + index;
    uint8_t* newptr = (uint8_t*)ptr;
    uint8_t r = newptr[3];
    uint8_t g = newptr[2];
    uint8_t b = newptr[1];
    uint8_t a = newptr[0];
    int er = eRgb[0];
    int eg = eRgb[1];
    int eb = eRgb[2];
    r = clamp(r + (int)(rate*er));
    g = clamp(g + (int)(rate*eg));
    b = clamp(b + (int)(rate*eb));
    return (r << 24) + (g << 16) + (b << 8) + a;
}

#pragma mark - 設置像素
- (void)setPixel:(uint32_t*)imageBuf width:(int)width height:(int)height   row:(int)row column:(int)column value:(uint32_t)value {
    if (row < 0 || row >= height || column < 0 || column >= width) {
        return;
    }
    int index = row * width + column;
    uint32_t *ptr = imageBuf + index;
    uint8_t* newptr = (uint8_t*)ptr;
    int r = (value & 0xFF000000) >> 24;
    int g = (value & 0x00FF0000) >> 16;
    int b = (value & 0x0000FF00) >> 8;
    int a = value & 0x000000FF;

    newptr[3] = r;
    newptr[2] = g;
    newptr[1] = b;
    newptr[0] = a;
}
複製代碼

總結

圖片抖動算法就是獲取當前點的像素與相鄰的點的像素比較,並進行相應處理的過程,是一個區域的光量保持一致

代碼傳送門
相關文章
相關標籤/搜索