本系列文章链接
像素是指在由一个数字序列表示的图像中的一个最小单位。
通常情况下,以图像左上角为原点建立以像素为单位的直接坐标系u-v。像素的横坐标u与纵坐标v分别是在其图像数组中所在的列数与所在行数。(在OpenCV中u对应x,v对应y)
二值图像是指:每个像素点均为黑色或者白色的图像。二值图像一般用来描述字符图像,其优点是占用空间少,缺点是,当表示人物,风景的图像时,二值图像只能展示其边缘信息,图像内部的纹理特征表现不明显。这时候要使用纹理特征更为丰富的灰度图像。
灰度图,又称灰阶图。把白色与黑色之间按对数关系分为若干等级,称为灰度。灰度分为256阶。用灰度表示的图像称作灰度图。除了常见的卫星图像、航空照片外,许多地球物理观测数据也以灰度表示。
索引图像是一种把像素值直接作为RGB调色板下标的图像。索引图像可把像素值“直接映射”为调色板数值。一幅索引图包含一个数据矩阵data和一个调色板矩阵map,数据矩阵可以是uint8,uint16或双精度类型的,而调色板矩阵则总是一个m×3的双精度矩阵。调色板通常与索引图像存储在一起,装载图像时,调色板将和图像一同自动装载。
彩色图形是指每个像素由R、G、B分量构成的图像,其中R、G、B是由不同的灰度级来描述的。
分辨率 = 画面水平方向的像素值 * 画面垂直方向的像素值
屏幕分辨率是屏幕图像的精密度,是指显示器所能显示的像素有多少。由于屏幕上的点、线和面都是由像素组成的,显示器可显示的像素越多,画面就越精细,同样的屏幕区域内能显示的信息也越多,所以分辨率是个非常重要的性能指标。可以把整个图像想象成是一个大型的棋盘,而分辨率的表示方式就是所有经线和纬线交叉点的数目。显示分辨率一定的情况下,显示屏越小图像越清晰,反之,显示屏大小固定时,显示分辨率越高图像越清晰。
图像分辨率是指在计算机中保存和显示一幅数字图像所具有的分辨率,它和图像的像素有直接的关系。例如,一张分辨率为640×480像素的图片,其分辨率就达到了307200像素,也就是常说的30万像素;而一张分辨率为1600×1200的图片,它的像素就是200万。
“色彩空间”一词源于西方的“Color Space”,又称作“色域”,色彩学中,人们建立了多种色彩模型,以一维、二维、三维甚至四维空间坐标来表示某一色彩,这种坐标系统所能定义的色彩范围即色彩空间。我们经常用到的色彩空间主要有RGB、CMYK、Lab等。
联合照片专家组(外语简称:JPEG外语全称:Joint Photographic Expert Group),文件后辍名为".jpg"或".jpeg",是最常用的图像文件格式,由一个软件开发联合会组织制定,是一种有损压缩格式,能够将图像压缩在很小的储存空间,图像中重复或不重要的资料会被丢失,因此容易造成图像数据的损伤。
便携式网络图形(外语简称PNG、外语全称:Portable Network Graphics),是网上接受的最新图像文件格式。PNG能够提供长度比GIF小30%的无损压缩图像文件。它同时提供 24位和48位真彩色图像支持以及其他诸多技术性支持。由于PNG非常新,所以目前并不是所有的程序都可以用它来存储图像文件,但Photoshop可以处理PNG图像文件,也可以用PNG图像文件格式存储。
图形交换格式(外语简称:GIF、外语全称:Graphics Interchange Format),是CompuServe公司在 1987年开发的图像文件格式。GIF文件的数据,是一种基于LZW算法的连续色调的无损压缩格式。其压缩率一般在50%左右,它不属于任何应用程序。目前几乎所有相关软件都支持它,公共领域有大量的软件在使用GIF图像文件。
PhotoShopDocument(PSD)这是Photoshop图像处理软件的专用文件格式,文件扩展名是.psd,可以支持图层、通道、蒙板和不同色彩模式的各种图像特征,是一种非压缩的原始文件保存格式。扫描仪不能直接生成该种格式的文件。PSD文件有时容量会很大,但由于可以保留所有原始信息,在图像处理中对于尚未制作完成的图像,选用 PSD格式保存是最佳的选择。
位图(外语简称:BMP、外语全称:BitMaP)BMP是一种与硬件设备无关的图像文件格式,使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选lbit、4bit、8bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。
import math
import random
import numpy as np
%matplotlib inline
import cv2
import matplotlib.pyplot as plt
ROI:Region of Interest.
截取ROI非常简单,指定图片的范围即可
# 创建一副图片
img = cv2.imread('cat.png')
# 转换颜色通道(改变顺序,由BGR变为RGB,使图片正常显示)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 截取猫脸ROI
face = img[0:740 , 400:1000]
plt.imshow(face)
彩色图的BGR三个通道是可以分开单独访问的,也可以将单独的三个通道合并成一副图像。分别使用
cv2.split()
和cv2.merge()
:
# 创建一副图片
img = cv2.imread('lena.jpg')
# 通道分割
b, g, r = cv2.split(img)
# 通道合并
RGB_Image = cv2.merge([b,g,r])
RGB_Image = cv2.cvtColor(RGB_Image, cv2.COLOR_BGR2RGB)
plt.figure(figsize = (12,12))
#显示各通道信息
plt.subplot(141)
plt.imshow(RGB_Image,'gray')
plt.title('RGB_Image')
plt.subplot(142)
plt.imshow(r,'gray')
plt.title('R_Channel')
plt.subplot(143)
plt.imshow(g,'gray')
plt.title('G_Channel')
plt.subplot(144)
plt.imshow(b,'gray')
plt.title('B_Channel')
常用的颜色空间转换如下:
- RGB或BGR到灰度(COLOR_RGB2GRAY,COLOR_BGR2GRAY)
- RGB或BGR到YcrCb(或YCC)(COLOR_RGB2YCrCb, COLOR_BGR2YCrCb)
- RGB或BGR到HSV(COLOR_RGB2HSV,COLOR_BGR2HSV)
- RGB或BGR到Luv(COLOR_RGB2Luv,COLOR_BGR2Luv)
- 灰度到RGB或BGR(COLOR_GRAY2RGB,COLOR_GRAY2BGR)
使用举例:
img = cv2.imread('lena.jpg')
# 转换为灰度图
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 保存灰度图
cv2.imwrite('img_gray.jpg', img_gray)
HSV是一个常用于颜色识别的模型,相比BGR更易区分颜色,转换模式用
COLOR_BGR2HSV
表示
下面以追踪蓝色为例:
# 加载一张有天空的图片
sky = cv2.imread('sky.jpg')
# 蓝色的范围,不同光照条件下不一样,可灵活调整
lower_blue = np.array([15, 60, 60])
upper_blue = np.array([130, 255, 255])
# 从BGR转换到HSV
hsv = cv2.cvtColor(sky, cv2.COLOR_BGR2HSV)
# inRange():介于lower/upper之间的为白色,其余黑色
mask = cv2.inRange(sky, lower_blue, upper_blue)
# 只保留原图中的蓝色部分
res = cv2.bitwise_and(sky, sky, mask=mask)
# 保存颜色分割结果
cv2.imwrite('res.jpg', res)
res = cv2.imread('res.jpg')
res = cv2.cvtColor(res, cv2.COLOR_BGR2RGB)
plt.imshow(res)
运行结果如下:
关于如何获取蓝色的HSV值的上下限lower和upper范围:
blue = np.uint8([[[255, 0, 0]]])
hsv_blue = cv2.cvtColor(blue, cv2.COLOR_BGR2HSV)
print(hsv_blue)
结果是[120, 255, 255]
import cv2
# 灰度图读入
img = cv2.imread('lena.jpg', 0)
# 颜色通道转换
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 阈值分割
ret, th = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
plt.imshow(th)
# 自适应阈值对比固定阈值
img = cv2.imread('lena.jpg', 0)
# 固定阈值
ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 自适应阈值, ADAPTIVE_THRESH_MEAN_C:小区域内取均值
th2 = cv2.adaptiveThreshold(
img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 4)
# 自适应阈值, ADAPTIVE_THRESH_GAUSSIAN_C:小区域内加权求和,权重是个高斯核
th3 = cv2.adaptiveThreshold(
img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 17, 6)
titles = ['Original', 'Global(v = 127)', 'Adaptive Mean', 'Adaptive Gaussian']
images = [img, th1, th2, th3]
plt.figure(figsize=(12,12))
for i in range(4):
plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i], fontsize=8)
plt.xticks([]), plt.yticks([])
img = cv2.imread('cat.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 按照指定的宽度、高度缩放图片
res = cv2.resize(img, (400, 500))
# 按照比例缩放,如x,y轴均放大一倍
res2 = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR)
plt.imshow(res)
plt.imshow(res2)
dst = cv2.flip(img, 1)
plt.imshow(dst)
# 平移图片
import numpy as np
# 获得图片的高、宽
rows, cols = img.shape[:2]
# 定义平移矩阵,需要是numpy的float32类型
# x轴平移200,y轴平移500
M = np.float32([[1, 0, 100], [0, 1, 500]])
# 用仿射变换实现平移
dst = cv2.warpAffine(img, M, (cols, rows))
plt.imshow(dst)
img = cv2.imread('lena.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 画一条线宽为5的红色直线,参数2:起点,参数3:终点
cv2.line(img, (0, 0), (800, 512), (255, 0, 0), 5)
plt.imshow(img)
# 画一个矩形,左上角坐标(40, 40),右下角坐标(80, 80),框颜色为绿色
img = cv2.rectangle(img, (40, 40), (80, 80), (0, 255, 0),2)
plt.imshow(img)
# 添加文字,加载字体
font = cv2.FONT_HERSHEY_SIMPLEX
# 添加文字hello
cv2.putText(img, 'hello', (10, 200), font, 4, (255, 255, 255), 2, lineType=cv2.LINE_AA)
plt.imshow(img)
x = np.uint8([250])
y = np.uint8([10])
print(cv2.add(x, y)) # 250+10 = 260 => 255
print(x + y) # 250+10 = 260 % 256 = 4
img1 = cv2.imread('lena.jpg')
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
img2 = cv2.imread('cat.png')
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)
img2 = cv2.resize(img2, (350, 350))
# 两张图片相加
res = cv2.addWeighted(img1, 0.6, img2, 0.4, 0)
plt.imshow(res)
img1 = cv2.imread('lena.jpg')
img2 = cv2.imread('logo.jpg')
img2 = cv2.resize(img2, (350, 350))
# 把logo放在左上角,所以我们只关心这一块区域
rows, cols = img2.shape[:2]
roi = img1[:rows, :cols]
# 创建掩膜
img2gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)
mask_inv = cv2.bitwise_not(mask)
# 保留除logo外的背景
img1_bg = cv2.bitwise_and(roi, roi, mask=mask_inv)
dst = cv2.add(img1_bg, img2) # 进行融合
img1[:rows, :cols] = dst # 融合后放在原图上
plt.imshow(dst)
img = cv2.imread('lena.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
blur = cv2.blur(img, (9, 9)) # 均值模糊
plt.imshow(blur)
blur = cv2.boxFilter(img, -1, (9, 9), normalize=True)
plt.imshow(blur)
gaussian = cv2.GaussianBlur(img, (9, 9), 1) # 高斯滤波
plt.imshow(gaussian)
median = cv2.medianBlur(img, 9) # 中值滤波
plt.imshow(median)
blur = cv2.bilateralFilter(img, 9, 75, 75) # 双边滤波
plt.imshow(blur)
kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32) #定义一个核
dst = cv2.filter2D(img, -1, kernel=kernel)
plt.imshow(dst)
img = cv2.imread('lena.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
edges = cv2.Canny(img, 30, 70) # canny边缘检测
plt.imshow(edges)
_, thresh = cv2.threshold(img, 124, 255, cv2.THRESH_BINARY)
edges = cv2.Canny(thresh, 30, 70)
plt.imshow(edges)
形态学操作一般作用于二值化图,来连接相邻的元素或分离成独立的元素,腐蚀和膨胀是针对图片中的白色部分。
腐蚀的效果是把图片"变瘦",其原理是在原图的小区域内取局部最小值。因为是二值化图,只有0和255,所以小区域内有一个是0该像素点就为0。OpenCV中用
cv2.erode()
函数进行腐蚀,只需要指定核的大小就行:
img = cv2.imread('lena.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
kernel = np.ones((5, 5), np.uint8)
erosion = cv2.erode(img, kernel) # 腐蚀
plt.imshow(erosion)
其中kernel可以是矩形/椭圆/十字形,可以用
cv2.getStructuringElement()
来生成不同形状的结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 矩形结构
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # 椭圆结构
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5)) # 十字形结构
膨胀与腐蚀相反,取的是局部最大值,效果是把图片"变胖":
dilation = cv2.dilate(img, kernel) # 膨胀
plt.imshow(dilation)
类别 | 开运算 | 闭运算 |
---|---|---|
操作 | 先腐蚀后膨胀 | 先膨胀后腐蚀 |
作用 | 消除小区域 | 填充小缝隙 |
实现 | cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) |
cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) |
开运算:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 定义结构元素
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) # 开运算
plt.imshow(opening)
闭运算:
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) # 闭运算
plt.imshow(closing)
import cv2
import numpy as np
from matplotlib import pyplot as plt
%matplotlib inline
filename = 'huaji.jpg'
img = cv2.imread(filename)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img)
class Resize:
def __init__(self, size):
self.size = size
def __call__(self, img):
res = cv2.resize(img, self.size)
return res
resize = Resize( (600, 600))
img2 = resize( img )
plt.imshow( img2 )
class Flip:
def __init__(self, mode):
self.mode = mode
def __call__(self, img):
fliptemp = cv2.flip(img, self.mode)
return fliptemp
flip = Flip( mode = 0 )
img2 = flip( img )
plt.imshow( img2 )
class Rotate:
def __init__(self, degree, size):
self.degree = degree
self.size = size
def __call__(self, img):
rows, cols, channel = img.shape
M = cv2.getRotationMatrix2D( (cols/2,rows/2), self.degree, self.size )
dst = cv2.warpAffine(img, M, (rows,cols))
return dst
rotate = Rotate( 45, 0.7)
img2 = rotate( img )
plt.imshow( img2 )
class Brightness:
def __init__(self, brightness_factor):
self.brightness_factor = brightness_factor
def __call__(self, img):
imgtemp = img.copy()
h, w, ch = imgtemp.shape
for xi in range(0,w):
for xj in range(0,h):
imgtemp[xj,xi,0] = int(imgtemp[xj,xi,0] * self.brightness_factor)
imgtemp[xj,xi,1] = int(imgtemp[xj,xi,1] * self.brightness_factor)
imgtemp[xj,xi,2] = int(imgtemp[xj,xi,2] * self.brightness_factor)
return imgtemp
brightness = Brightness(0.6)
img2 = brightness(img)
plt.imshow(img2)
import random
import math
class RandomErasing(object):
def __init__(self, EPSILON = 0.5, sl = 0.02, sh = 0.4, r1 = 0.3, mean = [0., 0., 0.]):
self.EPSILON = EPSILON
self.mean = mean
self.sl = sl
self.sh = sh
self.r1 = r1
def __call__(self, img):
imgtemp = img.copy()
if random.uniform(0, 1) > self.EPSILON:
return imgtemp
for attempt in range(100):
area = imgtemp.shape[0] * imgtemp.shape[1]
target_area = random.uniform(self.sl, self.sh) * area
aspect_ratio = random.uniform(self.r1, 1 / self.r1)
h = int(round(math.sqrt(target_area * aspect_ratio)))
w = int(round(math.sqrt(target_area / aspect_ratio)))
if w < imgtemp.shape[0] and h < imgtemp.shape[1]:
x1 = random.randint(0, imgtemp.shape[1] - h)
y1 = random.randint(0, imgtemp.shape[0] - w)
if imgtemp.shape[2] == 3:
imgtemp[ x1:x1 + h, y1:y1 + w, 0] = self.mean[0]
imgtemp[ x1:x1 + h, y1:y1 + w, 1] = self.mean[1]
imgtemp[ x1:x1 + h, y1:y1 + w, 2] = self.mean[2]
else:
imgtemp[x1:x1 + h, y1:y1 + w,0] = self.mean[0]
return imgtemp
return imgtemp
erase = RandomErasing()
img2 = erase(img)
plt.imshow(img2)