python彩色图像处理

2020-03-05 23:33:28 qq_41514794 阅读数 803

     本文为武汉理工大学数字图像处理课程设计:彩色图像复原的实现过程,本人初学Python,对数字图像处理的理解也仅限于课堂教学范围,如有不足之处欢迎指出。

       先上效果图,对于jpg图片,在一分钟内处理完成,以下分别是原始图片,彩色图像,自动裁剪等优化后的图像。

                                                                           

 

       对于tif图片,在三分钟内处理完成,以下分别是原始图片,彩色图像,裁剪等优化后的图像。(原图为60MB,此处上传图片大小约为2MB,仅作效果参考)

 

 

 

  • 问题内容描述

1. 问题背景

        Sergei Mikhailovich Prokudin-Gorskii (1863-1944)是一位超越其所在时代的人,早在1907年,他就坚信彩色摄影将成为未来的发展趋势。在当时,由于沙皇的特别许可,他可以周游辽阔的沙俄帝国并拍摄他所看到的事物,包括列夫·托尔斯泰唯一的彩色肖像。他用简单原始的彩色摄影法拍下了很多东西:人、建筑、风景、铁路、桥梁……!

其所采用的彩色摄影法很简单:分别用红、绿、蓝滤光片把每个场景的三次曝光记录到一个玻璃底板上,并设想通过特殊的投影装置将三种颜色的底片叠加显示,从而让观众能够通过彩色照片了解这个幅员辽阔的国家。可惜,他的计划从未实现:他在1918年的十月革命后离开了俄罗斯,再也没有回来。幸运的是,他所拍摄的沙俄帝国最后几年的RGB玻璃板底片保存了下来,并于1948年被美国国会图书馆(LoC)买下。LoC最近将底片数字化,并可以通过网络供公众下载。

 

2.设计目标

      本次课程设计的目的是利用图像处理技术,基于数字化存储的玻璃底板图像自动生成尽量非虚化的彩色图像。为完成本次课程设计,你需要从原始图像文件中分割提取三个彩色通道图像,将它们对齐并彼此叠加在一起,最终形成一张RGB彩色图像。美国国会图书馆在其网站上详细说明了他们对这批照片进行复原并创建彩色图像的过程,可以参考

      http://www.loc.gov/exhibits/empire/making.html

1.设计思路

         Prokudin Gorskii将一块3英寸宽9英寸长的窄玻璃板垂直放置在相机中。然后,他用红色滤镜、绿色滤镜和蓝色滤镜以相当快的顺序拍摄了同一场景三次。通过Prokudin Gorskii的相机观看时,被拍摄的场景会出现颠倒,并与实际方向相反。对于数字处理,原始的三部分玻璃负片在灰度模式下用高架数码相机扫描。图像编辑软件将整个板的扫描从负片转换成正片。扫描被反转以表示原始物理方向。然后将整个板还原为8位灰度模式。在放大倍数下,检查板上每个图像的对比度、分色程度、乳剂损坏程度以及可能影响最终颜色合成的任何其他细节,将整个板的扫描对齐,并裁剪外侧边缘可以得到修复的彩色图片。

        类似的,我们要通过数字图像处理进行彩色图片复原,需要首先获得图片不同颜色的三个图层,在图层处于灰度模式时,将红色(R)、蓝色(B)和绿色(G)层对齐,形成“RGB”颜色组合。对齐之后将得到一副带有杂乱边框的图片,可以通过裁剪将其剪去,裁剪后的颜色组合整体调整以创建适当的对比度、适当的高光和阴影细节以及最佳的颜色平衡,最终可以完成彩色图片的恢复。

2.简易流程图

 

 

  • 实现方案

(1)读入图片:

思路描述:读入文件,若为tif文件将其转换为8位无符号整数格式,之后转换成灰度图,并保存原始图片。

关键代码:

pic_name = '../turkmen.tif'  #图片名

im =  cv.imread(pic_name)  #将图像转换为8位无符号整数格式

if '.tif' in pic_name:

    im=skimage.util.img_as_ubyte(im)

im = cv.cvtColor(im,cv.COLOR_BGR2GRAY)

#之后会利用图片构造高斯金字塔,保留原始图片

im_old = im.copy()

(2)构造高斯金字塔。

思路描述:对于jpg文件我们可以轻松地对图像进行遍历操作,但是tif文件太大非常耗时,所以通过构造高斯金字塔的方法解决。先对图片进行高斯滤波处理以使图片即时缩小也能保留较多细节,然后再每次删除一半的行和列达到减小图片大小的效果,如图所示。

                                                        

 

关键代码:

#由于初始图像已存至变量im_old,只需保留满足大小要求的最后一层图像即可

#计算高斯核函数

    def gausskernel(size):  

    sigma=0.8

    gausskernel=np.zeros((size,size),np.float32)

    k = int(size/2)

    print(k)

    for i in range (size):

        for j in range (size):

            norm=math.pow(i-k,2)+pow(j-k,2)

            gausskernel[i,j]=math.exp(-norm/(2*math.pow(sigma,2)))    

    sum=np.sum(gausskernel)   # 求和

    kernel=gausskernel/sum   # 归一化

return kernel



def pyramid_Gauss(image):   

    kernel=gausskernel(3)        #阶数取3

    temp = image.copy() 

    (length,width) = temp.shape[:2]

    length = length / 3.0

    cnt = 0  

#当图片过大对其构造高斯金字塔使其大小小于一定限度

    while length * width > 250000 :

        cnt = cnt + 1

        temp = pyramid_Down(temp,kernel)

        (length,width) = temp.shape[:2]     

    cv.imshow("pyramid_down_" , temp)       

    return temp,cnt



def pyramid_Down(image,kernel):

    (length,width) = image.shape[:2]

    img1 = np.zeros((length,width),np.uint8)

# 高斯滤波过滤

    for i in range (1,length-1):

        for j in range (1,width-1):

            print(i,j)

            s = 0

            for k in range(-1,2):

                for l in range(-1,2):

                    s = s + image[i+k,j+l]*kernel[k+1,l+1]  

            img1[i,j] = s

   #删去一半的行和列,构造高斯金字塔1

    cnt = 0

    for i in range(length):

        if i % 2 == 1:

            img1 = np.delete(img1, i - cnt, axis=0)

            cnt = cnt + 1

    cnt = 0

    for j in range(width):    #删列操作与上文类似,在此省略

        ……

    #主函数调用上述函数完成操作

im,count = pyramid_Gauss(im)

print('pyramid finished!')

(3)计算图层相似性及平移距离

思路描述:由公式:

SSD(u,v) =  Sum{[Ima_1(u+x,v+y) – Ima_2(x,y)] ^2} 

可计算并比较图层间的相似程度。

关键代码:

def find_min_ssd(img_1,img_2,r_lim,d_lim):

      #寻找两幅图片的最小ssd所需平移的坐标

      #img_1固定,img_2平移的空间为[-r_lim,-d_lim] -> [r_lim,d_lim]

      length,width = img_1.shape[:2]



      #最小ssd初始化为无穷大

      min_ssd = float("inf")

      min_r = 0

      min_d = 0

      sum_ssd = 0

      for r_dis in range(-r_lim,r_lim+1):

          for d_dis in range(-d_lim,d_lim+1):

              sum_ssd = 0

              for i in range(length):

                  for j in range(width):

                      x = i + d_dis

                      y = j + r_dis

                      if(x >= 0 and x < length and y >= 0 and y < width):

                          sum_ssd = sum_ssd + (int(img_1[x,y])-int(img_2[i,j])) * (int(img_1[x,y])-int(img_2[i,j]))

              sum_ssd = sum_ssd

              if sum_ssd < min_ssd:

                  min_r = r_dis

                  min_d = d_dis

                  min_ssd = sum_ssd

return min_r,min_d

#在主函数中调用上述函数

height = np.floor(im.shape[0]/3.0).astype(np.int)

b = im[:height]

g = im[height:2 * height]

r = im[2 * height:3 *height]

(a1,b1) = find_min_ssd(r,b,15,15)

(a2,b2) = find_min_ssd(r,g,15,15)

#每一层高斯金字塔图片的长宽各缩小一倍,共建了count层,需要使平移参数乘上2的count次方

k = math.pow(2,count)

a1 = int(a1 * k)

b1 = int(b1 * k)

a2 = int(a2 * k)

b2 = int(b2 * k)

 

(4)平移图层,将三个图层重叠得到彩色图片。

思路描述:通过调用translate函数对图像进行仿射变化,达到平移的效果。

关键代码:

def translate(img,tx,ty):

    #图像平移

    length,width = img.shape[:2]

    m = np.float32([[1,0,tx],[0,1,ty]])

#仿射变换

    res = cv.warpAffine(img,m,(width,length))

    return res



#在主函数中调用上述函数

b = translate(b,a1,b1)

g = translate(g,a2,b2)

im_out = cv.merge((b,g,r))

print(a1,b1,a2,b2)

cv.imshow("aligned image",im_out)

cv.imwrite('../turkmen_2.tif',im_out)



 

 

  • 优化方案

(1)图像微调。

思路:在得到平移距离后,因为对于.tif文件,该平移距离是通过比较压缩后的图像所得,而非原始图像,假设构建了四层金字塔,则需要对原距离乘以16,那么所得平移距离必然是16的整数倍,对于原始图像,这些点往往不是平移的最优点,在对原始图片平移前,应该对该平移距离进行微调。

关键代码:

#im_old为原始灰度图像

height = np.floor(im_old.shape[0]/3.0).astype(np.int)

b = im_old[:height]

g = im_old[height:2 * height]

r = im_old[2 * height:3 *height]

print(b.shape)

#find_min_ssd_adj逻辑与find_min_ssd相同,只是搜索空间为原始图像,且平移距离仅为之前所得平移距离的领域,l1,l2,l3,l4为常数可根据实际情况修改

(a1,b1) = find_min_ssd_adj(r,b,a1-l1,a1+l1,b1-l2,b1+l2)

(a2,b2) = find_min_ssd_adj(r,g,a2-l3,a2+l3,b2-l4,b2+l4)

(2)改进相似度比较策略。

思路: 在使用上文的算法进行处理后,即使对jpg图片,对齐效果依然不能算完美,如cathedral.jpg图片放大后蓝色图层存在一定偏移,需要人工对距离进行一定微调,于是我输出了人工调整后的SSD总和与之前计算的最优SSD总和进行比较,发现调整后的SSD和反而增加了,证明我的算法没有错误,但调整后的对齐效果确实更好。

      经过仔细思考,我发现原始图片四周存在黑边,当对某一个图层进行平移后,可能会使得某一图层的非黑边部分与另一图层黑边部分产生更多重叠,因为黑边的亮度接近0,在这部分计算相似度可能会产生较大的SSD和,使得全局比较并累加得到的平移距离往往比真实应该平移的距离偏小。

      于是对比较策略进行调整,如果当待比较的点距离上、下、左、右边界较近(本课程设计中设为距离小于1/6长或宽),则其值不计入SSD总和,否则计入总和,即人为设定一条阈值,只计算图片中间部分的点进行比较相似度,虽然一定程度上减少了比较的面积,但通过尝试对不同图像进行实验,得到的效果非常好,且算法运行速度大大提高。

       具体代码即为在之前的for循环加一个条件判断即可,在此不再赘述。

 

(3)提高对比度。

思路:在合成彩色图片前,对图像的亮度进行重新标定是常用的处理手法。本课程设计中,在平均亮度最暗的颜色通道上,其接近最黑的像素(取与最黑像素值之差小于10的点)被标定为0,在平均亮度最亮的颜色通道上,其接近最亮的像素(同上)被标定为255

关键代码:

def contrast(r1,b1,g1):

    (length1,width1) = r1.shape[:2]

    sum_b = 0

    sum_g = 0

    sum_r = 0

    for i in range(length1):

        for j in range(width1):

            sum_b += r1[i,j]

            sum_g += b1[i,j]

            sum_r += g1[i,j]

    if(sum_b > sum_g and sum_g > sum_r):

        i_max = b1

        i_min = r1

    elif(sum_b > sum_g and sum_r > sum_g):

#判断哪个图层平均亮度最大和最小,与上述代码类似,在此省略

    maxn = -1    

    minn = 256

    for i in range(length1):

        for j in range(width1):

            if(i_max[i,j] > maxn):

                maxn = i_max[i,j]

            if(i_min[i,j] < minn):

                minn = i_min[i,j]

    for i in range(length1):

        for j in range(width1):              

            if(i_max[i,j] >= maxn - 10):    

                i_max[i,j] = 255

    for i in range(length1):

        for j in range(width1):

            if(i_min[i,j] <= minn + 10):

                i_min[i,j] = 0

    return r1,b1,g1

 

(4)自动裁剪边框。

思路:在进行自动裁边前,我观察到边框的颜色很怪异,可能导致在边框不同图层像素值之间差很大,于是我将图片每一行(列)的不同图层像素值最大的差输出,观察到在边框附近这个差值远大于图片内部,即可以通过设定一个阈值将差值大于阈值的行(列)删除,达到自动裁边的效果。

关键代码:
 

#删除列的代码与删除行的逻辑相同,以下代码省略删除列的部分代码

up_record = 0     #上边界,小于其的行将被删除,以下类似

down_record = 999999999    #下边界

for i in range(length):    #计算上下边界

    sum_t = 0

    for j in range(width):

        sum_t += max(b[i,j],g[i,j],r[i,j]) - min(b[i,j],g[i,j],r[i,j])

    if(sum_t/width > 65 and i <length/4):

        up_record = i

    elif(sum_t/width > 65 and i > 3 * length / 4 and down_record > i ):

        down_record = i

    #删除行,删除列代码已省略

for i in range(length - down_record):  

    b = np.delete(b,-1,axis = 0)

    g = np.delete(g,-1,axis = 0)

    r = np.delete(r,-1,axis = 0)

for i in range(up_record):

    b = np.delete(b,0,axis = 0)

    g = np.delete(g,0,axis = 0)

    r = np.delete(r,0,axis = 0) 

im_out = cv.merge((b,g,r))
  • 其他改进

特征点匹配

         基于对课程设计任务的理解,采用了模板匹配的方式进行特征匹配,模板匹配是一种最原始、最基本的模式识别方法,研究某一特定对象物的图案位于图像的什么地方,进而识别对象物。它是图像处理中最基本、最常用的匹配方法。模板匹配具有自身的局限性,因为它只能进行平行移动,但是对于本次课程设计来说非常适合。

 

关键代码:

b = im[:height]

g = im[height:2 * height]

r = im[2 * height:3 *height]

raw = g



#选取某图层部分区域充当模板

template = b

(l1,l2,w1,w2)=(50,150,200,300)

template = template[l1:l2,:]

template = template[:,w1:w2]

cv.imshow("test",template)

height, width = template.shape[:2]



#使用归一化平方差匹配法进行模板匹配

result = cv.matchTemplate(raw,template,cv.TM_SQDIFF_NORMED)



#进行归一化

cv.normalize( result, result, 0, 1, cv.NORM_MINMAX, -1 )

min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)



a1 = min_loc[0]-w1

b1 = min_loc[1]-l1

#得到该图层平移距离,再对另外一个图层做相同操作后调用前文中的代码进行平移,并将平移后的图层重叠即可恢复彩色图像。

 

  • 问题与影响因素讨论

 

1.结果描述

        程序经过调试与修改,对所给的每张图片都能恢复成效果较好的彩色图片,对jpg图片能够在2分钟左右完成,对于tif图片能够在5分钟左右完成,且能对不同颜色的边框进行自动裁剪。

2.综合分析

         要对彩色图像进行恢复,首先需要获得图片不同颜色的三个图层,在图层处于灰度模式时,将红色(R)、蓝色(B)和绿色(G)层对齐,形成“RGB”颜色组合。对齐之后将得到一副带有杂乱边框的图片,可以通过裁剪将其剪去,裁剪后的颜色组合整体调整以创建适当的对比度、适当的高光和阴影细节以及最佳的颜色平衡,最终可以完成彩色图片的恢复。

         本次课程设计所实现的程序,对jpg图片处理较快,大约在一分钟之内可以完成,对tif图片,因为对tif图片构造高斯金字塔的图片进行对齐后,为了对原始图片进行对齐,还对原始的三个图层进行了小范围的微调,所以处理时间大约在三分钟,考虑到图片的大小,这个时间我认为还在可以接受的范围内,后续可以考虑对循环体进行结构的优化,以减少运行的时间。    

  • 参考文献

 

[1]Brooks, L.E.. The empire that was Russia: the Prokudin-Gorskii photographic record re-created[P]. Applied Imagery Pattern Recognition Workshop, 2002. Proceedings. 31st,2002.

[2]Rafael C.Gonzalez Richard E.woods.数字图像处理(第三版)[M].电子工业出版社:北京,2013:1.

 

 

 

 

 

2020-06-01 12:30:41 IT_charge 阅读数 249

本文主要运用用Python代码实现了5种彩色空间之间的转换!

具体而言,包括:

  • 1)RGB → CMY;
  • 2)  CMY → RGB;
  • 3)  RGB → HSI;
  • 4)  HSI → RGB;
  • 5)  RGB → YIQ;
  • 6)  YIQ → RGB;
  • 7)  RGB → YUV;
  • 8)  YUV → RGB;
  • 9)  RGB → YCbCr;
  • 10) YCbCr → RGB;

文末还附有两方面的扩展:

  • 1)对“多图”的处理
  • 2)对“视频”的处理

快来一起交流学习吧!

目录

1 RGB → CMY

1.1 转换公式

1.2 代码实现 

1.3 运行结果 

2 CMY → RGB

2.1 公式转换

2.2 代码实现

2.3 运行效果

3 RGB → HSI

3.1 公式转换

3.2 代码实现

3.3 运行效果

4 HSI → RGB

4.1 公式转换

4.2 代码实现

4.3 运行效果

5 RGB → YIQ

5.1 公式转换

5.2 代码实现

5.3 运行效果

6 YIQ → RGB

6.1 公式转换

6.2 代码实现

6.3 运行效果

7 RGB → YUV

7.1 公式转换

7.2 代码实现

7.3 运行效果

8 YUV → RGB

8.1 公式转换

8.2 代码实现

8.3 运行效果

9 RGB → YCbCr

9.1 公式转换

9.2 代码实现

9.3 运行效果

10 YCbCr → RGB

10.1 公式转换

10.2 代码实现

10.3 运行效果

11 扩展1——多图处理

11.1 代码示例

11.2 运行效果

12 扩展2——视频处理

12.1 附:视频爬取代码

12.2 代码实现

12.3 运行效果


1 RGB → CMY

1.1 转换公式

1.2 代码实现 

'''-----------------RGB → CMY------------------------'''
import cv2
import imutils

def rgb_cmy(img):
    r, g, b = cv2.split(img)  # split the channels
    # normalization [0,1]
    r = r / 255.0
    g = g / 255.0
    b = b / 255.0
    c = 1 - r
    m = 1 - g
    y = 1 - b
    result = cv2.merge((c, m, y))  # merge the channels
    return result

if __name__ == '__main__':
    img = cv2.imread("E:/1.PNG")
    img_CMY = rgb_cmy(img)
    img_NEW = img_CMY * 255
    cv2.imwrite('F:/img_CMY.PNG', img_NEW)
    cv2.imshow("CMY image", imutils.resize(img_CMY, 666))
    cv2.imshow("original image", imutils.resize(img, 666))
    cv2.waitKey(0)
    cv2.destroyAllWindows()

1.3 运行结果 


2 CMY → RGB

2.1 公式转换

2.2 代码实现

'''-----------------CMY → RGB------------------------'''
import cv2
import imutils

def cmy_rgb(img):
    c, m, y = cv2.split(img)  # split the channels
    # normalization[0,1]
    c = c / 255.0
    m = m / 255.0
    y = y / 255.0
    r = 1 - c
    g = 1 - m
    b = 1 - y
    result = cv2.merge((r, g, b))  # merge the channels
    print(result)
    return result

if __name__ == '__main__':
    img = cv2.imread("F:/img_CMY.PNG")
    img_CMY = cmy_rgb(img)
    cv2.imshow("RGB image", imutils.resize(img_CMY, 666))
    cv2.imshow("original image", imutils.resize(img, 666))
    cv2.waitKey(0)
    cv2.destroyAllWindows()

2.3 运行效果


3 RGB → HSI

3.1 公式转换

3.2 代码实现

'''-----------------RGB → HSI------------------------'''
import cv2
import math
import imutils
import numpy as np

def rgb_hsi(rgb_Img):
    img_rows = int(rgb_Img.shape[0])
    img_cols = int(rgb_Img.shape[1])
    b, g, r = cv2.split(rgb_Img)
    # normalization[0,1]
    r = r / 255.0
    g = g / 255.0
    b = b / 255.0
    hsi_Img = rgb_Img.copy()
    H, S, I = cv2.split(hsi_Img)
    for i in range(img_rows):
        for j in range(img_cols):
            num = 0.5 * ((r[i, j]-g[i, j])+(r[i, j]-b[i, j]))
            den = np.sqrt((r[i, j]-g[i, j])**2+(r[i, j]-b[i, j])*(g[i, j]-b[i, j]))
            theta = float(np.arccos(num/den))

            if den == 0:
                H = 0
            elif b[i, j] <= g[i, j]:
                H = theta
            else:
                H = math.pi - theta

            min_RGB = min(min(b[i, j], g[i, j]), r[i, j])
            sum = b[i, j]+g[i, j]+r[i, j]
            if sum == 0:
                S = 0
            else:
                S = 1 - 3*min_RGB/sum

            H = H/(math.pi)
            I = sum/3.0
            # 输出HSI图像,扩充到255以方便显示,一般H分量在[0,2pi]之间,S和I在[0,1]之间
            hsi_Img[i, j, 0] = H*255
            hsi_Img[i, j, 1] = S*255
            hsi_Img[i, j, 2] = I*255
    return hsi_Img

if __name__ == '__main__':
    rgb_Img = cv2.imread("E:/1.PNG")
    hsi_Img = rgb_hsi(rgb_Img)

    cv2.imwrite('F:/img_HSI.PNG', hsi_Img)

    cv2.imshow('original image', imutils.resize(rgb_Img, 600))
    cv2.imshow('HSI image', imutils.resize(hsi_Img, 600))

    key = cv2.waitKey(0) & 0xFF
    if key == ord('q'):
        cv2.destroyAllWindows()

3.3 运行效果


4 HSI → RGB

4.1 公式转换

4.2 代码实现

'''-----------------HSI → RGB------------------------'''
import cv2
import math
import imutils

def hsi_rgb(hsi_img):
    img_rows = int(hsi_img.shape[0])
    img_cols = int(hsi_img.shape[1])
    H, S, I = cv2.split(hsi_img)
    # normalization[0,1]
    H = H / 255.0
    S = S / 255.0
    I = I / 255.0
    bgr_img = hsi_img.copy()
    B, G, R = cv2.split(bgr_img)
    for i in range(img_rows):
        for j in range(img_cols):
            if S[i, j] < 1e-6:
                R = I[i, j]
                G = I[i, j]
                B = I[i, j]
            else:
                H[i, j] *= 360
                if H[i, j] > 0 and H[i, j] <= 120:
                    B = I[i, j] * (1 - S[i, j])
                    R = I[i, j] * (1 + (S[i, j] * math.cos(H[i, j] * math.pi / 180)) / math.cos((60 - H[i, j]) * math.pi / 180))
                    G = 3 * I[i, j] - (R + B)
                elif H[i, j] > 120 and H[i, j] <= 240:
                    H[i, j] = H[i, j] - 120
                    R = I[i, j] * (1 - S[i, j])
                    G = I[i, j] * (1 + (S[i, j] * math.cos(H[i, j] * math.pi / 180)) / math.cos((60 - H[i, j]) * math.pi / 180))
                    B = 3 * I[i, j] - (R + G)
                elif H[i, j] > 240 and H[i, j] <= 360:
                    H[i, j] = H[i, j] - 240
                    G = I[i, j] * (1 - S[i, j])
                    B = I[i, j] * (1 + (S[i, j] * math.cos(H[i, j] * math.pi / 180)) / math.cos((60 - H[i, j]) * math.pi / 180))
                    R = 3 * I[i, j] - (G + B)
            bgr_img[i, j, 0] = B * 255
            bgr_img[i, j, 1] = G * 255
            bgr_img[i, j, 2] = R * 255
    return bgr_img

if __name__ == '__main__':
    hsi_Img = cv2.imread("F:/img_HSI.PNG")
    rgb_Img = hsi_rgb(hsi_Img)

    cv2.imshow('original image', imutils.resize(rgb_Img, 600))
    cv2.imshow('RGB image', imutils.resize(hsi_Img, 600))

    key = cv2.waitKey(0) & 0xFF
    if key == ord('q'):
        cv2.destroyAllWindows()

4.3 运行效果


5 RGB → YIQ

5.1 公式转换

5.2 代码实现

Python

'''-----------------RGB → YIQ------------------------'''
import cv2
import imutils
import numpy as np

def rgb_yiq(rgb_Img):
    img_rows = int(rgb_Img.shape[0])
    img_cols = int(rgb_Img.shape[1])
    yiq_image = rgb_Img.copy()
    R, G, B = cv2.split(yiq_image)

    for x in range(img_rows):
        for y in range(img_cols):
            right_matrix = np.array([[R[x,y]],
                                     [G[x,y]],
                                     [B[x,y]]])
            left_matrix = np.array([[0.299,0.587,0.114],
                                    [0.596,-0.275,-0.321],
                                    [0.212,-0.528,0.311]])
            matrix = np.dot(left_matrix,right_matrix)
            r = matrix[0][0]
            g = matrix[1][0]
            b = matrix[2][0]
            yiq_image[x, y] = (r, g, b)
    return yiq_image

if __name__ == '__main__':
    rgb_Img = cv2.imread("E:/1.PNG")
    yiq_Img = rgb_yiq(rgb_Img)
    cv2.imshow('original image', imutils.resize(rgb_Img, 600))
    cv2.imshow('YIQ image', imutils.resize(yiq_Img, 600))

    cv2.imwrite('F:/img_YIQ1.PNG', yiq_Img)

    key = cv2.waitKey(0) & 0xFF
    if key == ord('q'):
        cv2.destroyAllWindows()

MATLAB

% 清变量,关闭窗口
clear;
close all;
% 文件读取
img=imread('E:/1.PNG'); %获得256*256*3数组
imshow(img);title('原始RGB图像');

rgb=im2double(img); %将原图像转换到[0,1]空间
% figure;     %与原图像相同
% imshow(rgb);
r=rgb(:,:,1);
g=rgb(:,:,2);
b=rgb(:,:,3)

% rgb模型到yiq模型
y=0.299*r+0.587*g+0.114*b;
i=0.596*r-0.274*g-0.322*b;
q=0.211*r-0.523*g+0.312*b;

img_YIQ=cat(3,y,i,q);
figure;
imshow(img_YIQ);title('RGB2YIQ图像');

5.3 运行效果


6 YIQ → RGB

6.1 公式转换

6.2 代码实现

Python

'''-----------------YIQ → RGB------------------------'''
import cv2
import imutils
import numpy as np

def yiq_rgb(yiq_Img):
    img_rows = int(yiq_Img.shape[0])
    img_cols = int(yiq_Img.shape[1])
    rgb_image = yiq_Img.copy()
    Y, I, Q = cv2.split(rgb_image)

    for x in range(img_rows):
        for y in range(img_cols):
            right_matrix = np.array([[Y[x,y]],
                                     [I[x,y]],
                                     [Q[x,y]]])
            left_matrix = np.array([[1,0.956,0.620],
                                    [1,-0.272,-0.647],
                                    [1,-1.108,1.705]])
            matrix = np.dot(left_matrix,right_matrix)
            r = matrix[0][0]
            g = matrix[1][0]
            b = matrix[2][0]
            rgb_image[x, y] = (r, g, b)
    return rgb_image

if __name__ == '__main__':
    yiq_Img = cv2.imread("F:/img_YIQ1.PNG")
    rgb_Img = yiq_rgb(yiq_Img)
    cv2.imshow('original image', imutils.resize(yiq_Img, 600))
    cv2.imshow('RGB image', imutils.resize(rgb_Img, 600))

    key = cv2.waitKey(0) & 0xFF
    if key == ord('q'):
        cv2.destroyAllWindows()

MATLAB

% 清变量,关闭窗口
clear;
close all;
% 文件读取
img=imread('F:\img_YIQ.PNG'); %获得256*256*3数组
imshow(img);title('原始YIQ图像');

yiq=im2double(img); %将原图像转换到[0,1]空间
% figure;     %与原图像相同
% imshow(rgb);
y=yiq(:,:,1);
i=yiq(:,:,2);
q=yiq(:,:,3)

% rgb模型到yiq模型
r=1*y+0.956*i+0.620*q;
g=1*y-0.272*i-0.674*q;
b=1*y-1.108*i+1.705*q;

img_YIQ=cat(3,r,g,b);
figure;
imshow(img_YIQ);title('YIQ2RGB图像');

6.3 运行效果


7 RGB → YUV

7.1 公式转换

7.2 代码实现

'''-----------------RGB → YUV------------------------'''
import numpy as np
import cv2 as cv
import imutils


def rgb_yuv(rgb_img):
    W = np.array([
        [0.299, 0.587, 0.114],
        [-0.148, -0.289, 0.437],
        [0.615, -0.515, -0.100]
    ])
    rgb_Img = rgb_img.copy()
    rgb_Img = rgb_Img.astype(np.float)
    h, w, c = rgb_Img.shape
    for i in range(h):
        for j in range(w):
            rgb_Img[i, j] = np.dot(W, rgb_Img[i, j])
    imc = rgb_Img.astype(np.uint8)
    return imc

if __name__ == '__main__':
    img_rgb = cv.imread('E:/1.PNG')
    img_yuv1 = cv.cvtColor(img_rgb, cv.COLOR_RGB2YUV)
    img_yuv2 = rgb_yuv(img_rgb)

    cv.imwrite('F:/img_YUV.PNG', img_yuv1)
    # cv.imwrite('F:/img_YUV_self.PNG', img_yuv2)

    cv.imshow('original image', imutils.resize(img_rgb, 600))
    cv.imshow('OpenCV_YUV image', imutils.resize(img_yuv1, 600))
    cv.imshow('Self_YUV image', imutils.resize(img_yuv2, 600))

    cv.waitKey(0)

7.3 运行效果


8 YUV → RGB

8.1 公式转换

8.2 代码实现

'''-----------------YUV → RGB------------------------'''
import numpy as np
import cv2 as cv
import imutils


def yuv_rgb(yuv_img):
    W = np.array([
        [1, 0., 1.13983],
        [1, -0.39465, -0.58060],
        [1, 2.03211, 0.]
    ])
    rgb_img = yuv_img.copy()
    rgb_img = yuv_img.astype(np.float)
    h, w, c = rgb_img.shape
    for i in range(h):
        for j in range(w):
            rgb_img[i, j][0] -= 16  # Y
            rgb_img[i, j][1] -= 128  # U
            rgb_img[i, j][2] -= 128  # V
            rgb_img[i, j] = np.matmul(W, rgb_img[i, j])
    imc = rgb_img.astype(np.uint8)
    return imc

if __name__ == '__main__':
    img_rgb = cv.imread('F:/img_YUV.PNG')
    img_yuv1 = yuv_rgb(img_rgb)
    img_yuv2 = cv.cvtColor(img_rgb, cv.COLOR_YUV2RGB)

    cv.imshow('original image', imutils.resize(img_rgb, 600))
    cv.imshow('OpenCV_RGB image', imutils.resize(img_yuv2, 600))
    cv.imshow('Self_RGB image', imutils.resize(img_yuv1, 600))

    cv.waitKey(0)

8.3 运行效果


9 RGB → YCbCr

9.1 公式转换

9.2 代码实现

'''-----------------RGB → YCbCr------------------------'''
import numpy as np
import cv2 as cv
import imutils

def rgb2ycbcr(rgb_image):
    """convert rgb into ycbcr"""
    if len(rgb_image.shape)!=3 or rgb_image.shape[2]!=3:
        raise ValueError("input image is not a rgb image")
    rgb_image = rgb_image.astype(np.float32)
    # 1:创建变换矩阵,和偏移量
    transform_matrix = np.array([[0.257, 0.564, 0.098],
                                 [-0.148, -0.291, 0.439],
                                 [0.439, -0.368, -0.071]])
    shift_matrix = np.array([16, 128, 128])
    ycbcr_image = np.zeros(shape=rgb_image.shape)
    w, h, _ = rgb_image.shape
    # 2:遍历每个像素点的三个通道进行变换
    for i in range(w):
        for j in range(h):
            ycbcr_image[i, j, :] = np.dot(transform_matrix, rgb_image[i, j, :]) + shift_matrix
    return ycbcr_image

if __name__ == '__main__':
    img_rgb = cv.imread('E:/1.PNG')
    img_ycbcr = rgb2ycbcr(img_rgb)
    img_NEW = img_ycbcr / 255

    cv.imwrite('F:/img_YCbCr.PNG', img_ycbcr)

    cv.imshow('original image', imutils.resize(img_rgb, 600))
    cv.imshow('Self_YCbCr image', imutils.resize(img_NEW, 600))

    cv.waitKey(0)

9.3 运行效果


10 YCbCr → RGB

10.1 公式转换

10.2 代码实现

'''-----------------YCbCr → RGB------------------------'''
import numpy as np
import cv2 as cv
import imutils

def ycbcr2rgb(ycbcr_image):
    """convert ycbcr into rgb"""
    if len(ycbcr_image.shape)!=3 or ycbcr_image.shape[2]!=3:
        raise ValueError("input image is not a rgb image")
    ycbcr_image = ycbcr_image.astype(np.float32)
    transform_matrix = np.array([[0.257, 0.564, 0.098],
                                 [-0.148, -0.291, 0.439],
                                 [0.439, -0.368, -0.071]])
    transform_matrix_inv = np.linalg.inv(transform_matrix)
    shift_matrix = np.array([16, 128, 128])
    rgb_image = np.zeros(shape=ycbcr_image.shape)
    w, h, _ = ycbcr_image.shape
    for i in range(w):
        for j in range(h):
            rgb_image[i, j, :] = np.dot(transform_matrix_inv, ycbcr_image[i, j, :]) - np.dot(transform_matrix_inv, shift_matrix)
    return rgb_image.astype(np.uint8)

if __name__ == '__main__':
    img_ycbcr = cv.imread('F:/img_YCbCr.PNG')
    img_rgb = ycbcr2rgb(img_ycbcr)
    img_NEW = img_rgb / 255

    cv.imshow('original image', imutils.resize(img_ycbcr, 600))
    cv.imshow('Self_RGB image', imutils.resize(img_NEW, 600))

    cv.waitKey(0)

10.3 运行效果

 

11 扩展1——多图处理

11.1 代码示例

'''-----------------RGB → CMY------------------------'''
import cv2
import imutils

def rgb_cmy(img):
    r, g, b = cv2.split(img)  # split the channels
    # normalization [0,1]
    r = r / 255.0
    g = g / 255.0
    b = b / 255.0
    c = 1 - r
    m = 1 - g
    y = 1 - b
    result = cv2.merge((c, m, y))  # merge the channels
    return result

if __name__ == '__main__':
    for i in range(3):
        img = cv2.imread("F:/{}.PNG".format(i))
        img_CMY = rgb_cmy(img)
        img_NEW = img_CMY * 255
        cv2.imwrite('F:/img_CMY.PNG', img_NEW)
        cv2.imshow("CMY image{}".format(i), imutils.resize(img_CMY, 666))
        cv2.imshow("original image{}".format(i), imutils.resize(img, 666))
    cv2.waitKey(0)
    cv2.destroyAllWindows()

11.2 运行效果

 

12 扩展2——视频处理

12.1 附:视频爬取代码

import requests
import json
import re

def change_title(title):
    # 替换非法字符
    pattern = re.compile(r"[\/\\\:\*\?\"\<\>\|]")
    new_title = re.sub(pattern, "_", title)
    return new_title

# 示例爬取3页数据
for page in range(1, 4):
    print('---------------正在爬取第{}页小姐姐视频-------------------'.format(page))
    # 获取 URL 地址
    url = 'https://v.6.cn/minivideo/getlist.php?act=recommend&page={}&pagesize=20'.format(page)
    # headers 参数确定
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36'
    }

    # 模拟浏览器发送 URL 地址请求
    response = requests.get(url, headers=headers)
    # 去除 response 响应对象中的文本数据
    response_data = response.text
    # print(response_data)


    # 转换数据类型
    dict_data = json.loads(response_data)    # 字典
    # print(dict_data)
    # 数据解析
    data_list = dict_data['content']['list']   # 列表
    # print(data_list)

    # 遍历
    for data in data_list:
        # print(data)
        video_title = data['title']      # 视频文件名
        video_alias = data['alias']       # 视频作者名
        video_playurl = data['playurl']   # 视频 url
        # print('视频:', video_title, '作者:', video_alias, 'url地址:', video_playurl)
        print('正在下载视频:', video_title)

        new_title = change_title(video_title)

        # 发送视频 URL 请求
        video = requests.get(video_playurl, headers=headers).content

        # 保存数据
        with open(r'F:\Beautiful Girl Video\\' + new_title + '_' + video_alias + '.mp4', 'wb') as video_file:
            video_file.write(video)

        print('视频下载成功…… \n')

    print('---------------第{}页小姐姐视频爬取完毕-------------------'.format(page))

12.2 代码实现

'''-----------------RGB → CMY------------------------'''
import cv2

def rgb_cmy(video):
    while True:
        ret, frame=video.read()
        if not ret:
            break
        else:
            r, g, b = cv2.split(frame)  # split the channels
            # normalization [0,1]
            r = r / 255.0
            g = g / 255.0
            b = b / 255.0
            c = 1 - r
            m = 1 - g
            y = 1 - b
            result = cv2.merge((c, m, y))  # merge the channels
            cv2.imshow('original video',frame)
            cv2.imshow('CMY video',result)
            cv2.waitKey(ret)

if __name__ == '__main__':
    img = cv2.VideoCapture(r"F:\Beautiful Girl Video\video.mp4")
    img_CMY = rgb_cmy(img)

12.3 运行效果


:本文所有内容的讲解视频已发布到:https://space.bilibili.com/386691571

版权声明:本专栏全部为CSDN博主「IT_change」的原创文章,遵循 CC 4.0 BY-SA 版权协议。
                  转载请附上原文出处链接及本声明。

感谢阅读 ! 感谢支持 !  感谢关注 !

希望本文能对读者学习和理解数字图像处理之彩色空间转换有所帮助,并请读者批评指正!

2020年6月初于山西大同

END
 

2020-04-11 13:08:45 weixin_43227526 阅读数 150

伪彩色图像处理

冈萨雷斯数字图像处理

伪彩色图像(也称假彩色图像)图像处理包含根据特定标准对灰度值赋予颜色。
伪彩色图像用于区分对单色图像赋予彩色的处理和与真彩色图像相关的处理。因为人眼对彩色图像更为敏感。(人眼可以辨别几千种色调和强度,相比之下却只能辨别20几种灰度。)可以说,伪彩色图像是对灰度图像灰度级的可视化和解释。

1 灰度分层

最简单的对于一张灰度图像,每一个像素点都对应一个灰度值。我们在灰度轴设置一个阈值,低于该阈值的像素点对应一种颜色,高于该阈值对应另一种颜色(理解为将图像二值化)。可以设置多个阈值,函数呈现阶梯式。
在这里插入图片描述
进一步,对于用于目标检测的灰度图像,我们只关注图像的某一物理特性时,将一种颜色赋给某一特定的灰度值,而将另一种颜色赋给所有其他灰度级。
举例:1.甲状腺模型的灰度图像和伪彩色图像2.焊缝孔隙的灰度图像和伪图像。
在这里插入图片描述

2 灰度到彩色的变换

彩色图像由三个通道(RGB),而灰度图像只有一个通道。
对于灰度图像,可以将灰度值执行三个独立的变换,将变换的结果作为三个通道合成一张彩色图片。变换函数可以根据特定的情况进行调制。上一节的灰度分层身成为彩色图像使用的是分段线性函数。而这种变换是以平滑的非线性函数为基础的。
在这里插入图片描述
选择合适的变换函数可以对伪彩色图像进行进一步的彩色增强。
在这里插入图片描述
书中举例将三个颜色通道的变换函数分别设置成具有不同相位的正弦函数。因为不同物体的灰度值有差别,而变换函数的纵坐标分别对应三个颜色的强度。由于正弦函数呈周期性变化,所以选择特定的灰度级区段表示不同的物体,是不同的物体呈现出不同的颜色。
1、在这里插入图片描述
2、
在这里插入图片描述
图1中爆炸物和背景有不同的灰度级,能够很容易识别到爆炸物。而图2中爆炸物和背景在三个变换函数的映射相差很小,区分效果不好。

3 灰度图像转换成伪彩色图像的算法(来自博客https://blog.csdn.net/jizhidexiaoming/article/details/80255383

和第二部分的方法相似,将灰度图像通过三个变换函数(这里的变换函数就是分段的线性函数),转化成三个灰度值,作为彩色图像的三个通道。

matlab程序:

% 输入灰度级        输出彩色
% 0~63  1/4         蓝色
% 64~127 2/4        紫色
% 128~191 3/4       黄色
% 192~255  4/4      红色
clc;
clear;
grayImage=rgb2gray(imread('D:\Code\Image\girl.jpg'));
figure,imshow(grayImage);
[row,col]=size(grayImage);
range=255;%每个通道的最大灰度值

R = zeros(row,col);
G = zeros(row,col);
B = zeros(row,col);
for i=1:row
     for j=1:col
        if grayImage(i,j)<=range/4% [0,64]偏蓝
            R(i,j)=0;
            G(i,j)=4*grayImage(i,j);
            B(i,j)=range;
            else if grayImage(i,j)<=range/2% (64,128] 偏紫
                 R(i,j)=0;
                 G(i,j)=range;
                 B(i,j)=-4*grayImage(i,j)+2*range;
                else if grayImage(i,j)<=3*range/4% (128, 192]
                        R(i,j)=4*grayImage(i,j)-2*range;
                        G(i,j)=range;
                        B(i,j)=0;
                     else
                        R(i,j)=range;
                        G(i,j)=4*(range-grayImage(i,j));
                        B(i,j)=0;
                    end
                end
        end
    end
end

out = zeros(row,col);
for i=1:row
    for j=1:col
              out(i,j,1)=R(i,j);
              out(i,j,2)=G(i,j);
              out(i,j,3)=B(i,j);
    end
end

out=out/256;
figure,imshow(out);

python代码,还是用了opencv 待完善

import cv2
import numpy as np


def to_gray(path):
    img = cv2.imread(path)
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    return img_gray


'''
输入灰度级        输出彩色
 0~63  1/4         蓝色
 64~127 2/4        紫色
 128~191 3/4       黄色
 192~255  4/4      红色
'''


def show(img):
    cv2.imshow('img', img)
    cv2.waitKey()
    cv2.destroyAllWindows()


def color(img_gray):
    row, col = img_gray.shape[:]
    print(row, col)
    b = np.zeros((row, col))
    print('b', b, b.shape[:])
    g = np.zeros((row, col))
    r = np.zeros((row, col))
    for i in range(row):
        for j in range(col):
            if(img_gray[i, j]<255//4):
                b[i, j] = 255
                g[i, j] = 4 * img_gray[i, j]
                while (g[i, j]>255):
                    g[i, j] -= 255
                r[i, j] = 0
            elif(img_gray[i, j]<255//2):
                b[i, j] = -4 * img_gray[i, j]
                while (b[i, j]<0):
                    b[i, j]+=255
                g[i, j] = 255
                r[i, j] = 0
            elif(img_gray[i, j]<3*255//4):
                b[i, j] = 0
                g[i, j] = 255
                r[i, j] = 4*img_gray[i, j]-255*2
                while (r[i, j]>255):
                    r[i, j]-=255
            else:
                b[i, j] = 0
                g[i, j] = -4*img_gray[i, j]+0*255
                while (g[i, j]<0):
                    g[i, j] += 255
                r[i, j] = 255
    img_color = cv2.merge([b, g, r])
    return img_color


img_gray = to_gray('a.jpg')
img_color = color(img_gray)
show(img_gray)
show(img_color)

显示结果:
在这里插入图片描述
在这里插入图片描述

感谢
https://blog.csdn.net/weixin_41004352/article/details/90711499
https://blog.csdn.net/jizhidexiaoming/article/details/80255383

冈萨雷斯《数字图像处理》第三版

2016-03-27 17:15:05 chongshangyunxiao321 阅读数 7955

人的生理视觉系统特征对微小的灰度变化感觉不敏感,而对彩色的微小差别极为敏感,利用这一特点就可以把人眼不敏感的灰度信号映射为人眼灵敏的彩色信号,以增强人对图像中细微变换的分辨率。

在图像处理技术中,彩色增强应用十分广泛且效果显著

常见的彩色增强技术主要有假彩色增强和伪彩色增强两大类

(1)假彩色增强

思路是将灰度分层几级,比如我们这里将灰度分为16级,然后每一级灰度对应一种彩色。在查看原图中某像素,找出它所属的灰度级,用相应的彩色代替就行了

(2)伪彩色处理

由灰度值根据一定的映射关系求出R,G,B的值,组成该点的彩色值

典型的映射关系图为:
这里写图片描述

Python测试代码如下:

__author__ = 'Administrator'
import cv

def Color(image):
    w = image.width
    h = image.height
    size = (w,h)
    iColor = cv.CreateImage(size,8,3)
    for i in range(h):
        for j in range(w):
            r = GetR(image[i,j])
            g = GetG(image[i,j])
            b = GetB(image[i,j])
            iColor[i,j] = (r,g,b)
    return iColor

def GetR(gray):
    if gray < 127:
        return 0
    elif gray > 191:
        return 255
    else:
        return (gray-127)*4-1


def GetG(gray):
    if gray < 64:
        return 4*gray
    elif gray > 191:
        return 256-(gray-191)*4
    else:
        return 255

def GetB(gray):
    if gray < 64:
        return 255
    elif gray > 127:
        return 0
    else:
        return 256-(gray-63)*4

def FColor(image,array):
    w = image.width
    h = image.height
    size = (w,h)
    iColor = cv.CreateImage(size,8,3)
    for i in range(h):
        for j in range(w):
            iColor[i,j] = array[int(image[i,j]/16)]
    return iColor

FCArray = [(0,51,0),(0,51,102),(51,51,102),(51,102,51),\
            (51,51,153),(102,51,102),(153,153,0),(51,102,153),\
            (153,102,51),(153,204,102),(204,153,102),(102,204,102),\
            (153,204,153),(204,204,102),(204,255,204),(255,255,204)]
image = cv.LoadImage('18.jpg',0)
iColor = Color(image)
iFColor = FColor(image,FCArray)
cv.ShowImage('image',image)
cv.ShowImage('iColor',iColor)
cv.ShowImage('iFColor',iFColor)
cv.WaitKey(0)

我们来看看运行结果:
这里写图片描述

python 图像处理

阅读数 3160