弗洛伊德-斯坦伯格抖動(dòng)算法
這是一個(gè)真實(shí)的魔法技術(shù)。它愚弄了你的眼睛和大腦,讓你以為自己看到的顏色要比實(shí)際的多纽绍。
image.png
一般來說,抖動(dòng)是通過增加人工噪聲去減少一個(gè)圖像的顏色空間势似,主旨在于拌夏,一個(gè)區(qū)域的光量應(yīng)該保持一致。
image.png
弗洛伊德-斯坦伯格抖動(dòng)算法對(duì)周圍的像素使用非均勻分布的量化誤差達(dá)到抖動(dòng)的目的履因。這就意味著要先將中心像素四舍五入為0或1障簿,而后將殘差加入其周圍的像素中。
image.png
以上你看到的三張圖片都是灰階抖動(dòng)的栅迄,它們?nèi)慷际侵挥蓛煞N顏色的噪音組成站故,而其余的信息,當(dāng)然是因?yàn)槟愕拇竽X在轉(zhuǎn)嘍毅舆。
算法實(shí)現(xiàn)
#pragma mark - 分配內(nèi)存
uint32_t* oldImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);
uint32_t* newImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);
#pragma mark - 創(chuàng)建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 -遍歷像素計(jì)算殘差
//殘差
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分之 7西篓、5、3憋活、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 - 設(shè)置像素
- (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;
}