图像中的像素处理

图像的深度和通道

图像的深度

图像中像素点占得bit位数,就是图像的深度,比如以下图像的深度。

二值图像:

图像的像素点不是0 就是1 (图像不是黑色就是白色),图像像素点占的位数就是 1 位,图像的深度就是1,也称作位图。

灰度图像:

图像的像素点位于0-255之间,(0:全黑,255代表:全白,在0-255之间插入了255个等级的灰度)。最大值255的二进制表示为11111111,占有8个bit位,即2^8=256,图像的深度是8。

图像的通道

通道,是数字图像中存储不同类型信息的灰度图像。一个图像最多可以有数十个通道,常用的RGB和Lab图像默认有三个通道,而CMYK图像则默认有四个通道。 一张RGB图像含有三个通道:红(Red)、绿(Green)、蓝(Blue)。 一张CMYK图像含有四个通道:青色(Cyan)、品红(Magenta)、黄色、黑色。

所以想灰度图就只有一个通道,占有8个bit位,也就是8位图。所以RGB图像占有三个通道,3*8=24,所以RGB图像就是24位图。

图像在内存中的存储

image
image

图像像素点的存储就是对应的原图从左到右,从上到下,依次排列,每个点的值就是就是像素点的值,每个点的地址就是像素像素点的地址。

如第一幅图就是灰度图的存储,只有单通道。在内存中的存储即可用一个一维数组来表示,根据顺序从左到右,从上到下,依次按顺序存入数组。

图二则为RGB图像的存储模型,每一个像素有3个通道,所以需要一个二维数组来表示,顺序也是从左到右,从上到下,如[[234,200,0],[234,0,0],[255,55,0],....]这样,当然其中的数子,在内存中需要用对应的二进制来表示。

python输出图像数据

我们来用python来输出一个图片的像素数据,来验证看看上面所说的存储模型。

import sys
import tensorflow as tf
from PIL import Image, ImageFilter
import numpy as np

def imageprepare(argv):
    testImage=Image.open(argv).convert('L')
    testImage = testImage.resize((6, 4))
    test_input=np.array(testImage)
    print(test_input)


def main(argv):
    """
    Main function.
    """
    imvalue = imageprepare(argv)
    
if __name__ == "__main__":
    main(sys.argv[1])

我这里传进去一张图片,然后转换成L模型(L表示灰度图),设置宽高位(6,4),输出如下所示,这里np.array把图片数据转换成了一个二维数组,方便根据(x,y)来读取:

[[254 255 254  97 255 248]
 [246 255  15 180 255 255]
 [252 227 227 246  44 252]
 [244 254 229 151 243 248]]

如我们之前所说根据从左到右,从上到下存储的话,则可以方便的用以下方法来读取:

width = testImage.size[0]
height = testImage.size[1]
y = 0
while y<height:
    x = 0
    while x<width:
        print(test_input[y,x])
        x += 1
    y += 1

对应的RGB图片,我们转换模型改一下testImage=Image.open(argv).convert('RGB'),转换为array之后,就变成了一个rowscolschannels的三维矩阵,输出读取如下所示:

[[[254 254 254]
  [255 255 255]
  [254 254 254]
  [ 97  97  97]
  [255 255 255]
  [248 248 248]]

 [[246 246 246]
  [255 255 255]
  [ 15  15  15]
  [180 180 180]
  [255 255 255]
  [255 255 255]]

 [[252 252 252]
  [227 227 227]
  [227 227 227]
  [246 246 246]
  [ 44  44  44]
  [252 252 252]]

 [[244 244 244]
  [254 254 254]
  [229 229 229]
  [151 151 151]
  [243 243 243]
  [248 248 248]]]
width = testImage.size[0]
height = testImage.size[1]
y = 0
while y<height:
    x = 0
    while x<width:
        # 像素的3通道值
        print(test_input[y,x])
        print('R: ' + str(test_input[y,x,0]))
        print('G: ' + str(test_input[y,x,1]))
        print('B: ' + str(test_input[y,x,2]))
        x += 1
    y += 1

仿python图像处理库PIL

python代码

以下为python中PIL把图片转换为像素数据数组的代码,我们先把图片转化为RGBA格式,然后输出对应位置的像素数据。当然RGBA一个像素有4个通道,所以我们可以依次输出每个通道的值,如R通道:test_input[y,x,0]

def imageprepare(argv):
    testImage=Image.open(argv).convert('RGBA')
    testImage = testImage.resize((28, 28))
    test_input=np.array(testImage)
    print(test_input)
    width = testImage.size[0]
    height = testImage.size[1]
    y = 0
    while y<height:
        x = 0
        while x<width:
            print(test_input[y,x])
            # print('R: ' + str(test_input[y,x,0]))
            x += 1
        y += 1

iOS代码

在iOS中图片转化为图片数据格式相对于python和Android中来讲相对麻烦一些,所以我这里封装了一个iOS图片转图片数据的类。输出的格式跟python中类似,但是python支持多种编码格式,分别为1,L,P,RGB,RGBA,CMYK,YCbCr,I,F。这里iOS开发中只支持RGBA,CMYK。

在iOS中我们会先根据图片的编码格式来生成一个CGContextRef(画布),以下代码是对RGBA格式图片处理生成的CGContextRef。

- (CGContextRef) newBitmapRGBA8ContextFromImage:(CGImageRef) image {
    
    CGContextRef context = NULL;
    CGColorSpaceRef colorSpace;
    uint32_t *bitmapData;
    
    size_t bitsPerPixel = 32;
    size_t bitsPerComponent = 8;
    size_t bytesPerPixel = bitsPerPixel / bitsPerComponent;
    
    size_t width = CGImageGetWidth(image);
    size_t height = CGImageGetHeight(image);
    
    size_t bytesPerRow = width * bytesPerPixel;
    size_t bufferLength = bytesPerRow * height;
    
    colorSpace = CGColorSpaceCreateDeviceRGB();
    
    if(!colorSpace) {
        NSLog(@"Error allocating color space Gray\n");
        return NULL;
    }
    
    // Allocate memory for image data
    bitmapData = (uint32_t *)malloc(bufferLength);
    
    if(!bitmapData) {
        NSLog(@"Error allocating memory for bitmap\n");
        CGColorSpaceRelease(colorSpace);
        return NULL;
    }
    
    //Create bitmap context
    
    context = CGBitmapContextCreate(bitmapData,
                                    width,
                                    height,
                                    bitsPerComponent,
                                    bytesPerRow,
                                    colorSpace,
                                    kCGImageAlphaPremultipliedLast); 
    if(!context) {
        free(bitmapData);
        NSLog(@"Bitmap context not created");
    }
    
    CGColorSpaceRelease(colorSpace);
    
    return context;
}

要理解以上代码,首先要知道什么是像素格式:

位图其实就是一个像素数组,而像素格式则是用来描述每个像素的组成格式,它包括以下信息:

Bits per component :一个像素中每个独立的颜色分量使用的 bit 数;
Bits per pixel : 一个像素使用的总 bit 数;
Bytes per row : 位图中的每一行使用的字节数。

有一点需要注意的是,对于位图来说,像素格式并不是随意组合的,目前iOS、Mac OS X开发只支持以下有限的 17 种特定组合: 官方文档

源码

DSImageBitmaps这是我iOS源码的地址,其中包含了python的代码,我iOS里面的图片直接使用的是python裁剪过大小的图片,然后能发现数据的数据是一样的。

但是我用iOS里面直接裁剪大小后的图片就跟python处理过大小的图片输出的数据就不一样了,说是python的image.resize用到了滤波器,具体是什么我也不太清楚。反正就是iOS和python处理图片大小内部的算法有些许差异,但是你能发现每一个像素上的数据差异不大,具体到一张图显示的话人眼是识别不出来的。

还有就算要注意iOS中处理的图片大小问题,也就是iOS中像素和image.size的关系:

test.png (像素 20*20) test@2x.png(像素40*40) test@3x.png(像素 60*60)

UIImage *image = [UIImageimageNamed:@"test.png"];

image.size输出大小为(20,20);


UIImage *image = [UIImage imageNamed:@"test@2x.png"];

image.size输出大小为(20,20);


UIImage *image = [UIImage imageNamed:@"test@3x.png"];

image.size输出大小为(20,20);


image.size输出的大小会自动识别图片是几倍的,如果是3倍的输出的结果就是像素除以3,2倍的像素除以2。

参考

Converting UIImage to RGBA8 Bitmaps and Back
一张图片引发的深思
谈谈 iOS 中图片的解压缩

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容