在OpenCV中,滑动条设计的主要目的是在视频播放帧中选择特定帧。在和父窗口使用时,需要给滑动条赋予一个特别的名字(通常是一个字符串),接下来直接通过那个名字进行引用。
创建滑动条的函数是createTrackbar,该函数声明如下:
CreateTrackbar(trackbarName, windowName, value, count, onChange) -> None
其中,参数trackbarName是滑动条的名称;windowName是滑动条将要添加到父窗口的名称,一旦滑动条创建好,它就将被添加到窗口的顶部或底部,滑动条不会挡住任何已经在窗口中的图像,只会让窗口变大,窗口的名称将作为一个窗口的标记,至于滑动条上滑动按钮的确切位置,则由操作系统决定,一般都是最左边;参数value是一个指向整数的指针,这个整数值会随着滑动按钮的移动而自动变化;参数count是滑动条可以滑动的最大值;参数onChange是一个指向回调函数的指针,当滑动按钮移动时,回调函数就会被自动调用。
回调函数类型TrackbarCallback的定义如下:
def TrackbarCallback(pos,userdata)
其中,参数pos表示滚动块的当前位置;userdata是传给回调函数的可选参数。这个回调函数不是必需的,如果赋值为NULL,就没有回调函数,移动滑动按钮的唯一响应就是createTrackbar的参数value指向的变量值的变化。
除了创建滑动条的函数外,OpenCV还提供了函数getTrackbarPos(用于获取滑动块的位置)和函数setTrackbarPos(用于设置滑动条的位置)。
getTrackbarPos函数的声明如下:
GetTrackbarPos(trackbarName, windowName) -> retval
其中,参数trackbarName是滑动条的名称;windowName是滑动条将要添加到父窗口的名称。函数返回滑动块的当前位置。
setTrackbarPos函数的声明如下:
SetTrackbarPos(trackbarName, windowName, pos) -> None
其中,参数trackName表示滚动条的名称;windowName是滑动条将要添加到父窗口的名称;pos表示要设置的滑动块位置。下面我们看一个专业的例子,利用滑动块调节参数。
【例3.12】 利用滑动块控制图片的亮度
import cv2 as cv import numpy as np import cv2 import numpy as np alpha = 0.3 beta = 80 img_path = "test.jpg" img = cv2.imread(img_path) img2 = cv2.imread(img_path) def updateAlpha(x): global alpha, img, img2 # 得到数值 alpha = cv2.getTrackbarPos('Alpha', 'image') alpha = alpha * 0.01 img = np.uint8(np.clip((alpha * img2 + beta), 0, 255)) def updateBeta(x): global beta, img, img2 beta = cv2.getTrackbarPos('Beta', 'image') img = np.uint8(np.clip((alpha * img2 + beta), 0, 255)) # 创建窗口 cv2.namedWindow('image') cv2.createTrackbar('Alpha', 'image', 0, 300, updateAlpha) cv2.createTrackbar('Beta', 'image', 0, 255, updateBeta) # 设置默认值 cv2.setTrackbarPos('Alpha', 'image', 100) cv2.setTrackbarPos('Beta', 'image', 10) while (True): cv2.imshow('image', img) if cv2.waitKey(1) == ord('q'): break cv2.destroyAllWindows()
在上述代码中,首先读取test.jpg,然后定义滑动块的两个回调函数updateAlpha和updateBeta,接着利用函数namedWindow创建1个窗口,并利用函数createTrackbar创建2个滑动条,这样窗口上就有2个滑动条。updateAlpha和updateBeta都是滑动条的回调函数,用于响应用户滑动滑块这个事件。最后一个while循环,等待用户按Q键退出。在回调函数中,np.uint8是专门用于存储各种图像的(包括RGB、灰度图像等),范围是0~255。该函数接收的参数是一个数组。需要注意的是,clip函数的返回值是uint8的参数,但是这个函数仅仅是对原数据和0xff相与(和最低2字节数据相与),这就容易导致如果原数据大于255,那么在直接使用np.uint8()后,比第8位大的数据都被截断了。clip函数将数组中的元素限制在a_min与a_max之间,大于a_max的就使得它等于a_max,小于a_min的就使得它等于a_min。它的原型是numpy.clip(a, a_min, a_max, out=None)。其中,a是一个数组,后面两个参数分别表示最小值和最大值。代码中的uint8和clip函数,在后面章节还会详细讲到。在上述代码中,首先读取test.jpg,然后定义滑动块的两个回调函数updateAlpha和updateBeta,接着利用函数namedWindow创建一个窗口,并利用函数createTrackbar创建两个滑动条,这样窗口上就有两个滑动条。updateAlpha和updateBeta都是滑动条的回调函数,用于响应用户滑动滑块这个事件。最后一个while循环,等待用户按Q键退出。
在回调函数中,函数np.uint8专门用于存储各种图像的数据(包括RGB、灰度图像等),它用于将输入数据转换为8位无符号整数类型,即将输入数据的值限制在0~255,并将其存储为8位无符号整数。NumPy中的np.uint8函数是将原数据和0xff做与运算,这就容易导致如果原数据是大于255的,那么在直接使用np.uint8函数后,比第8位更大的数据都被截断了。例如,数字2000在转换为np.uint8的时候,就会被转换成208(2000&0xff=208),这样就会导致数据不准确,区分不出原来的数据是大于255的还是本来就是208。为了解决这个问题,我们可以使用np.clip函数,用于将数组中的元素限制在一个指定的范围内,而且它可以给定一个范围,范围外的值将被剪裁到范围边界。np.clip函数原型如下:
numpy.clip(a, lower, upper)
其中,a是输入数组;lower表示元素的下限,所有小于下限的元素将被限制为下限值;upper表示元素的上限,所有大于上限的元素将被限制为上限值。该函数返回一个与输入数组形状相同的数组,其中元素值被限制在lower和upper之间。下面是一个简单的示例,展示如何使用clip()函数将数组中的元素限制在0~1:
import numpy as np arr = np.array([-2, -1, 0, 1, 2]) clipped_arr = np.clip(arr, 0, 1) print(clipped_arr) # 输出:[ 0. 0. 0. 1. 1.]
运行工程,结果如图3-12所示。
图3-12