英文原文:
- https://docs.opencv.org/master/d3/dc1/tutorial_basic_linear_transform.html
中文翻译:
- https://www.jb51.net/article/171029.htm
1 图像处理
图像变换就是找到一个函数,把原始图像矩阵经过函数处理后,转换为目标图像矩阵。
可以分为两种方式,即像素级别的变换和区域级别的变换
-
Point operators (pixel transforms)
-
Neighborhood (area-based) operators
像素级别的变换就相当于 p a f t e r ( i , j ) = f ( p b e f o r e ( i , j ) ) p_{after}(i,j) = f(p_{before}(i,j)) pafter(i,j)=f(pbefore(i,j)),即变换后的每个像素值都与变换前的同位置的像素值有个函数映射关系。
2 获取图片的亮度
import cv2
img = cv2.imread("2_1332.jpg")
# 法一:利用 mean
x = img.mean()
# 法二:三层循环
total = 0
h, w, c = img.shape
for i in range(h):
for j in range(w):
for k in range(c):
total += img[i][j][k] # 统计像素值总和
print(total/img.size) # 获取每个空间位置上的平均像素值
print(x)
output
133.61220920138888
133.61220920138888
3 对比度和亮度改变
3.1 线性变换
最常用的是线性变换,即
g ( i , j ) = α ⋅ f ( i , j ) + β g(i,j) = \alpha \cdot f(i,j) + \beta g(i,j)=α⋅f(i,j)+β
α \alpha α 调整对比度, β \beta β 调整亮度。有时也称之为 gain 和 bias 参数。
对比度是什么?不就是 “亮和暗的区别” 吗?也就是像素值的大小的区别。那我乘以一个 α \alpha α 系数,当 α \alpha α 很大的时候就是放大了这种亮度值的差异,也就是提高了对比度(1 和 2 的差距是 1,×1.2 倍后,1.2 和 2.4 的差距是 1.2);当 α \alpha α 很小时,也就是缩小了亮度的差异,也就是缩小了对比度。
β \beta β 就更好理解了,直接在像素的亮度值上加上一个数,正数就是提高亮度,负数降低亮度.
from __future__ import print_function
from builtins import input
import cv2 as cv
import numpy as np
import argparse
# Read image given by user
parser = argparse.ArgumentParser(description='Code for Changing the contrast and brightness of an image! tutorial.')
parser.add_argument('--input', help='Path to input image.', default='lena.jpg')
args = parser.parse_args()
image = cv.imread(cv.samples.findFile(args.input))
if image is None:
print('Could not open or find the image: ', args.input)
exit(0)
new_image = np.zeros(image.shape, image.dtype)
alpha = 1.0 # Simple contrast control
beta = 0 # Simple brightness control
# Initialize values
print(' Basic Linear Transforms ')
print('-------------------------')
try:
alpha = float(input('* Enter the alpha value [1.0-3.0]: '))
beta = int(input('* Enter the beta value [0-100]: '))
except ValueError:
print('Error, not a number')
# Do the operation new_image(i,j) = alpha*image(i,j) + beta
# Instead of these 'for' loops we could have used simply:
# new_image = cv.convertScaleAbs(image, alpha=alpha, beta=beta)
# but we wanted to show you how to access the pixels :)
for y in range(image.shape[0]):
for x in range(image.shape[1]):
for c in range(image.shape[2]):
new_image[y,x,c] = np.clip(alpha*image[y,x,c] + beta, 0, 255)
cv.imshow('Original Image', image)
cv.imshow('New Image', new_image)
# Wait until user press some key
cv.waitKey()
提示 module ‘cv2’ has no attribute ‘samples’ 的话要先安装 pip install opencv-python==4.0.0.21
执行:python change_brightness_contrast.py --input ./lights.jpeg
上图是 α = 2 \alpha=2 α=2, β = 20 \beta=20 β=20 的一个效果图。
线性变换有个问题,如上图, α = 1.3 \alpha=1.3 α=1.3 and β = 40 \beta=40 β=40,提高原图亮度的同时,导致云几乎看不见了。如果要看见云的话,建筑的亮度又不够,这个时候就引入了非线性变换。
3.2 非线性变换—Gamma 校正
理论可以参考 图像的Gamma变换,公式表示如下
O = ( I 255 ) γ × 255 O = \left( \frac{I}{255} \right)^{\gamma} \times 255 O=(255I)γ×255
在 γ < 1 \gamma<1 γ<1 的时候,会提高图片亮度. γ > 1 \gamma>1 γ>1 时,降低亮度。
与线性变换不同,对不同的原始亮度值,其改变强度是不同的,是非线性的。
上图横坐标是输入的像素值,纵坐标是输出的像素值,线性变换相当于改变了斜率和截距,会导致原图部分像素值被截断
下面看一个例子
如果查看不同变换下的灰度直方图的话可以看到
中间是原图的灰度直方图,可以看到低亮度值的像素点很多。
左边是做了线性变换的,整体直方图产生了右移,并且在 255 处出现峰值。因为每个像素点都增加了亮度嘛。导致了白云和蓝天过于明亮无法区分。
而右边做了gamma 校正的图像亮度分布比较均匀,即使得低亮度值的部分得以加强,又不至于过度曝光使得白云无法区分。
lookUpTable = np.empty((1,256), np.uint8)
for i in range(256):
lookUpTable[0,i] = np.clip(pow(i / 255.0, gamma) * 255.0, 0, 255)
res = cv.LUT(img_original, lookUpTable)
其中cv.LUT就是个变换函数.从lookUpTable里找到变换关系,生成新的图像矩阵。
https://docs.opencv.org/master/d2/de8/group__core__array.html