Gamma校正(幂律变换)
在电视和图形监视器中,显像管发生的电子束及其生成的图像亮度并不是随显像管的输入电压线性变化,电子流与输入电压相比是按照指数曲线变化的,输入电压的指数要大于电子束的指数。这说明暗区的信号要比实际情况更暗,而亮区要比实际情况更高。所以,要重现摄像机拍摄的画面,电视和监视器必须进行伽玛补偿。这种伽玛校正也可以由摄像机完成。我们对整个电视系统进行伽玛补偿的目的,是使摄像机根据入射光亮度与显像管的亮度对称而产生的输出信号,所以应对图像信号引入一个相反的非线性失真,即与电视系统的伽玛曲线对应的摄像机伽玛曲线,它的值应为1/γ,我们称为摄像机的伽玛值。电视系统的伽玛值约为2.2,所以电视系统的摄像机非线性补偿伽玛值为0.45。彩色显像管的伽玛值为2.8,它的图像信号校正指数应为1/2.8=0.35,但由于显像管内外杂散光的影响,重现图像的对比度和饱和度均有所降低,所以彩色摄像机的伽玛值仍多采用0.45。在实际应用中,我们可以根据实际情况在一定范围内调整伽玛值,以获得最佳效果。
(Gamma Correction,伽玛校正):所谓伽玛校正就是对图像的伽玛曲线进行编辑,以对图像进行非线性色调编辑的方法,检出图像信号中的深色部分和浅色部分,并使两者比例增大,从而提高图像对比度效果。计算机绘图领域惯以此屏幕输出电压与对应亮度的转换关系曲线,称为伽玛曲线(Gamma Curve)。
以传统CRT(Cathode Ray Tube)屏幕的特性而言,该曲线通常是一个乘幂函数,Y=(X+e)γ,其中,Y为亮度、X为输出电压、e为补偿系数、乘幂值(γ)为伽玛值,改变乘幂 值(γ)的大小,就能改变CRT的伽玛曲线。典型的Gamma值是0.45,它会使CRT的影像亮度呈现线性。使用CRT的电视机等显示器屏幕,由于对于 输入信号的发光灰度,不是线性函数,而是指数函数,因此必需校正。
假设图像中有一个像素,值是 200 ,那么对这个像素进行校正必须执行如下步骤:
1. 归一化 :将像素值转换为 0 ~ 1 之间的实数。 算法如下 : ( i + 0. 5)/256 这里包含 1 个除法和 1 个加法操作。对于像素 A 而言 , 其对应的归一化值为 0. 783203 。
2. 预补偿 :根据公式 , 求出像素归一化后的 数据以 1 /gamma 为指数的对应值。这一步包含一个 求指数运算。若 gamma 值为 2. 2 , 则 1 /gamma 为 0. 454545 , 对归一化后的 A 值进行预补偿的结果就 是 0. 783203 ^0. 454545 = 0. 894872 。
3. 反归一化 :将经过预补偿的实数值反变换为 0 ~ 255 之间的整数值。具体算法为 : f*256 - 0. 5 此步骤包含一个乘法和一个减法运算。续前 例 , 将 A 的预补偿结果 0. 894872 代入上式 , 得到 A 预补偿后对应的像素值为 228 , 这个 228 就是最后送 入显示器的数据。
如上所述如果直接按公式编程的话,假设图像的分辨率为 800*600 ,对它进行 gamma 校正,需要执行 48 万个浮点数乘法、除法和指数运算。效率太低,根本达不到实时的效果。
针对上述情况,提出了一种快速算法,如果能够确知图像的像素取值范围 , 例如 , 0 ~ 255 之间的整数 , 则图像中任何一个像素值只能 是 0 到 255 这 256 个整数中的某一个 ; 在 gamma 值 已知的情况下 ,0 ~ 255 之间的任一整数 , 经过“归一 化、预补偿、反归一化”操作后 , 所对应的结果是唯一的 , 并且也落在 0 ~ 255 这个范围内。
如前例 , 已知 gamma 值为 2. 2 , 像素 A 的原始值是 200 , 就可求得 经 gamma 校正后 A 对应的预补偿值为 228 。基于上述原理 , 我们只需为 0 ~ 255 之间的每个整数执行一次预补偿操作 , 将其对应的预补偿值存入一个预先建立的 gamma 校正查找表 (LUT:Look Up Table) , 就可以使用该表对任何像素值在 0 ~ 255 之 间的图像进行 gamma 校正。
import cv2 import numpy as np import matplotlib.pyplot as plt # gamma correction # **幂运算 def gamma_correction(img, c=1, g=2.2): out = img.copy() out /= 255. out = (1/c * out) ** (1/g) out *= 255 out = out.astype(np.uint8) return out # Read image img = cv2.imread("imori_gamma.jpg").astype(np.float) # Gammma correction out = gamma_correction(img) # Save result cv2.imshow("result", out) cv2.waitKey(0) cv2.imwrite("out.jpg", out)
参考网址:
https://www.cnblogs.com/qiqibaby/p/5325193.html
https://blog.csdn.net/lxy201700/article/details/24929013
https://blog.csdn.net/chenjuan0530/article/details/78970791
减色算法
将图像的值由256^3压缩至4^3,即将{RGB}的值只取{32, 96, 160, 224}。这被称作色彩量化。
import cv2 import numpy as np # Dicrease color def dicrease_color(img): out = img.copy() out = out // 64 * 64 + 32 return out # Read image img = cv2.imread("imori.jpg") # Dicrease color out = dicrease_color(img) cv2.imwrite("out.jpg", out) cv2.imshow("result", out) cv2.waitKey(0) cv2.destroyAllWindows()平均池化
将图片按照固定大小网格分割,网格内的像素值取网格内所有像素的平均值。我们将这种把图片使用均等大小网格分割,并求网格内代表值的操作称为池化(Pooling)。池化操作是**卷积神经网络(Convolutional Neural Network)**中重要的图像处理方式。在CNN里池化是为了降低特征的维度,同时提取更好的、具有更强烈的语义信息的特征。
import cv2 import numpy as np # average pooling def average_pooling(img, G=8): out = img.copy() H, W, C = img.shape Nh = int(H / G) Nw = int(W / G) for y in range(Nh): for x in range(Nw): for c in range(C): out[G*y:G*(y+1), G*x:G*(x+1), c] = np.mean(out[G*y:G*(y+1), G*x:G*(x+1), c]).astype(np.int) return out # Read image img = cv2.imread("imori.jpg") # Average Pooling out = average_pooling(img) # Save result cv2.imwrite("out.jpg", out) cv2.imshow("result", out) cv2.waitKey(0) cv2.destroyAllWindows()最大池化
import cv2 import numpy as np # max pooling def max_pooling(img, G=8): # Max Pooling out = img.copy() H, W, C = img.shape Nh = int(H / G) Nw = int(W / G) for y in range(Nh): for x in range(Nw): for c in range(C): out[G*y:G*(y+1), G*x:G*(x+1), c] = np.max(out[G*y:G*(y+1), G*x:G*(x+1), c]) return out # Read image img = cv2.imread("imori.jpg") # Max pooling out = max_pooling(img) # Save result cv2.imwrite("out.jpg", out) cv2.imshow("result", out) cv2.waitKey(0) cv2.destroyAllWindows()高斯滤波
import cv2 import numpy as np # Gaussian filter def gaussian_filter(img, K_size=3, sigma=1.3): if len(img.shape) == 3: H, W, C = img.shape else: img = np.expand_dims(img, axis=-1) H, W, C = img.shape ## Zero padding pad = K_size // 2 out = np.zeros((H + pad * 2, W + pad * 2, C), dtype=np.float) out[pad: pad + H, pad: pad + W] = img.copy().astype(np.float) ## prepare Kernel K = np.zeros((K_size, K_size), dtype=np.float) for x in range(-pad, -pad + K_size): for y in range(-pad, -pad + K_size): K[y + pad, x + pad] = np.exp( -(x ** 2 + y ** 2) / (2 * (sigma ** 2))) K /= (2 * np.pi * sigma * sigma) K /= K.sum() tmp = out.copy() # filtering for y in range(H): for x in range(W): for c in range(C): out[pad + y, pad + x, c] = np.sum(K * tmp[y: y + K_size, x: x + K_size, c]) out = np.clip(out, 0, 255) out = out[pad: pad + H, pad: pad + W].astype(np.uint8) return out # Read image img = cv2.imread("imori_noise.jpg") # Gaussian Filter out = gaussian_filter(img, K_size=3, sigma=1.3) # Save result cv2.imwrite("out.jpg", out) cv2.imshow("result", out) cv2.waitKey(0) cv2.destroyAllWindows()中值滤波
import cv2 import numpy as np # Median filter def median_filter(img, K_size=3): H, W, C = img.shape ## Zero padding pad = K_size // 2 out = np.zeros((H + pad*2, W + pad*2, C), dtype=np.float) out[pad:pad+H, pad:pad+W] = img.copy().astype(np.float) tmp = out.copy() # filtering for y in range(H): for x in range(W): for c in range(C): out[pad+y, pad+x, c] = np.median(tmp[y:y+K_size, x:x+K_size, c]) out = out[pad:pad+H, pad:pad+W].astype(np.uint8) return out # Read image img = cv2.imread("imori_noise.jpg") # Median Filter out = median_filter(img, K_size=3) # Save result cv2.imwrite("out.jpg", out) cv2.imshow("result", out) cv2.waitKey(0) cv2.destroyAllWindows()均值滤波
import cv2 import numpy as np # mean filter def mean_filter(img, K_size=3): H, W, C = img.shape # zero padding pad = K_size // 2 out = np.zeros((H + pad * 2, W + pad * 2, C), dtype=np.float) out[pad: pad + H, pad: pad + W] = img.copy().astype(np.float) tmp = out.copy() # filtering for y in range(H): for x in range(W): for c in range(C): out[pad + y, pad + x, c] = np.mean(tmp[y: y + K_size, x: x + K_size, c]) out = out[pad: pad + H, pad: pad + W].astype(np.uint8) return out # Read image img = cv2.imread("imori.jpg") # Mean Filter out = mean_filter(img, K_size=3) # Save result cv2.imwrite("out.jpg", out) cv2.imshow("result", out) cv2.waitKey(0) cv2.destroyAllWindows()运动模糊算子Motion Filter
# Motion Filter取对角线方向的像素的平均值 import cv2 import numpy as np # motion filter def motion_filter(img, K_size=3): H, W, C = img.shape # Kernel K = np.diag( [1] * K_size ).astype(np.float) K /= K_size # zero padding pad = K_size // 2 out = np.zeros((H + pad * 2, W + pad * 2, C), dtype=np.float) out[pad: pad + H, pad: pad + W] = img.copy().astype(np.float) tmp = out.copy() # filtering for y in range(H): for x in range(W): for c in range(C): out[pad + y, pad + x, c] = np.sum(K * tmp[y: y + K_size, x: x + K_size, c]) out = out[pad: pad + H, pad: pad + W].astype(np.uint8) return out # Read image img = cv2.imread("imori.jpg") # motion filtering out = motion_filter(img, K_size=3) # Save result cv2.imwrite("out.jpg", out) cv2.imshow("result", out) cv2.waitKey(0) cv2.destroyAllWindows()MAX-MIN滤波器
# MAX-MIN滤波器使用网格内像素的最大值和最小值的差值对网格内像素重新赋值。通常用于边缘检测。 import cv2 import numpy as np # Gray scale def BGR2GRAY(img): b = img[:, :, 0].copy() g = img[:, :, 1].copy() r = img[:, :, 2].copy() # Gray scale out = 0.2126 * r + 0.7152 * g + 0.0722 * b out = out.astype(np.uint8) return out # max-min filter def max_min_filter(img, K_size=3): H, W = img.shape # Zero padding pad = K_size // 2 out = np.zeros((H + pad * 2, W + pad * 2), dtype=np.float) out[pad: pad + H, pad: pad + W] = gray.copy().astype(np.float) tmp = out.copy() # filtering for y in range(H): for x in range(W): out[pad + y, pad + x] = np.max(tmp[y: y + K_size, x: x + K_size]) - \ np.min(tmp[y: y + K_size, x: x + K_size]) out = out[pad: pad + H, pad: pad + W].astype(np.uint8) return out # Read image img = cv2.imread("../imori.jpg").astype(np.float) # grayscale gray = BGR2GRAY(img) # Max-Min filtering out = max_min_filter(gray, K_size=3) # Save result cv2.imwrite("out.jpg", out) cv2.imshow("result", out) cv2.waitKey(0) cv2.destroyAllWindows()差分滤波器
# 差分滤波器对图像亮度急剧变化的边缘有提取效果,可以获得邻接像素的差值。 import cv2 import numpy as np # Gray scale def BGR2GRAY(img): b = img[:, :, 0].copy() g = img[:, :, 1].copy() r = img[:, :, 2].copy() # Gray scale out = 0.2126 * r + 0.7152 * g + 0.0722 * b out = out.astype(np.uint8) return out # different filter def different_filter(img, K_size=3): H, W, C = img.shape # Zero padding pad = K_size // 2 out = np.zeros((H + pad * 2, W + pad * 2), dtype=np.float) out[pad: pad + H, pad: pad + W] = gray.copy().astype(np.float) tmp = out.copy() out_v = out.copy() out_h = out.copy() # vertical kernel Kv = [[0., -1., 0.],[0., 1., 0.],[0., 0., 0.]] # horizontal kernel Kh = [[0., 0., 0.],[-1., 1., 0.], [0., 0., 0.]] # filtering for y in range(H): for x in range(W): out_v[pad + y, pad + x] = np.sum(Kv * (tmp[y: y + K_size, x: x + K_size])) out_h[pad + y, pad + x] = np.sum(Kh * (tmp[y: y + K_size, x: x + K_size])) out_v = np.clip(out_v, 0, 255) out_h = np.clip(out_h, 0, 255) out_v = out_v[pad: pad + H, pad: pad + W].astype(np.uint8) out_h = out_h[pad: pad + H, pad: pad + W].astype(np.uint8) return out_v, out_h # Read image img = cv2.imread("imori.jpg").astype(np.float) # grayscale gray = BGR2GRAY(img) # different filtering out_v, out_h = different_filter(gray, K_size=3) # Save result cv2.imwrite("out_v.jpg", out_v) cv2.imshow("result_v", out_v) while cv2.waitKey(100) != 27:# loop if not get ESC if cv2.getWindowProperty('result_v',cv2.WND_PROP_VISIBLE) <= 0: break cv2.destroyWindow('result_v') cv2.imwrite("out_h.jpg", out_h) cv2.imshow("result_h", out_h) # loop if not get ESC or click x while cv2.waitKey(100) != 27: if cv2.getWindowProperty('result_h',cv2.WND_PROP_VISIBLE) <= 0: break cv2.destroyWindow('result_h') cv2.destroyAllWindows()