在OpenCV显示比窗口更大的图片,可通过右侧的垂直滚动条和底部的水平滚动条移动图片来显示全图。
在OpenCV中,虽然有cv2.getTrackbarPos() 函数,但其作用主要是用作动态调节参数的控件,更准确名称应为滑动条,而非作为滚动条控件使用。经查询发现OpenCV并没有滚动条控件,其对于大于显示窗口的图片来说,OpenCv只能显示原比例图片的部分内容或者缩放后的图片,而无法显示原比例图片的全部内容。
1. 自己在OpenCv显示窗口画出滚动条,并通过鼠标事件实现其功能(在此详讲的,如果你一定要使用OpenCV界面的话);
2. 使用其它界面工具如QT等的滚动条控件来实现图片(此方法另谈)。
1. 在windows下已安装Python(必须的,我使用的是python3.7);
2. 安装PyCharm(方便编程调试)
3. 安装opencv-python(在python下安装配置OpenCV)
import cv2
#鼠标事件
def mouse(event, x, y, flags, param):
global flag, horizontal, vertical, flag_hor, flag_ver, dx, dy, sx, sy, dst, x1, y1, x2, y2, x3, y3, f1, f2
global zoom, scroll_har, scroll_var, img_w, img_h, img, dst1, win_w, win_h, show_w, show_h
if event == cv2.EVENT_LBUTTONDOWN: # 左键点击
if flag == 0:
if horizontal and 0 < x < win_w and win_h - scroll_w < y < win_h:
flag_hor = 1 # 鼠标在水平滚动条上
elif vertical and win_w - scroll_w < x < win_w and 0 < y < win_h:
flag_ver = 1 # 鼠标在垂直滚动条上
if flag_hor or flag_ver:
flag = 1 # 进行滚动条垂直
x1, y1, x2, y2, x3, y3 = x, y, dx, dy, sx, sy # 使鼠标移动距离都是相对于初始滚动条点击位置,而不是相对于上一位置
elif event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_LBUTTON): # 按住左键拖曳
if flag == 1:
if flag_hor:
w = (x - x1)/2 # 移动宽度
dx = x2 + w * f1 # 原图x
if dx < 0: # 位置矫正
dx = 0
elif dx > img_w - show_w:
dx = img_w - show_w
sx = x3 + w # 滚动条x
if sx < 0: # 位置矫正
sx = 0
elif sx > win_w - scroll_har:
sx = win_w - scroll_har
if flag_ver:
h = y - y1 # 移动高度
dy = y2 + h * f2 # 原图y
if dy < 0: # 位置矫正
dy = 0
elif dy > img_h - show_h:
dy = img_h - show_h
sy = y3 + h # 滚动条y
if sy < 0: # 位置矫正
sy = 0
elif sy > win_h - scroll_var:
sy = win_h - scroll_var
dx, dy = int(dx), int(dy)
img1 = img[dy:dy + show_h, dx:dx + show_w] # 截取显示图片
print(dy, dy + show_h, dx, dx + show_w)
dst = img1.copy()
elif event == cv2.EVENT_LBUTTONUP: # 左键释放
flag, flag_hor, flag_ver = 0, 0, 0
x1, y1, x2, y2, x3, y3 = 0, 0, 0, 0, 0, 0
elif event == cv2.EVENT_MOUSEWHEEL: # 滚轮
if flags > 0: # 滚轮上移
zoom += wheel_step
if zoom > 1 + wheel_step * 20: # 缩放倍数调整
zoom = 1 + wheel_step * 20
else: # 滚轮下移
zoom -= wheel_step
if zoom < wheel_step: # 缩放倍数调整
zoom = wheel_step
zoom = round(zoom, 2) # 取2位有效数字
img_w, img_h = int(img_original_w * zoom), int(img_original_h * zoom) # 缩放都是相对原图,而非迭代
img_zoom = cv2.resize(img_original, (img_w, img_h), interpolation=cv2.INTER_AREA)
horizontal, vertical = 0, 0
if img_h <= win_h and img_w <= win_w:
dst1 = img_zoom
cv2.resizeWindow("img", img_w, img_h)
scroll_har, scroll_var = 0, 0
f1, f2 = 0, 0
else:
if img_w > win_w and img_h > win_h:
horizontal, vertical = 1, 1
scroll_har, scroll_var = win_w * show_w / img_w, win_h * show_h / img_h
f1, f2 = (img_w - show_w) / (win_w - scroll_har), (img_h - show_h) / (win_h - scroll_var)
elif img_w > win_w and img_h <= win_h:
show_h = img_h
win_h = show_h + scroll_w
scroll_har, scroll_var = win_w * show_w / img_w, 0
f1, f2 = (img_w - show_w) / (win_w - scroll_har), 0
elif img_w <= win_w and img_h > win_h:
show_w = img_w
win_w = show_w + scroll_w
scroll_har, scroll_var = 0, win_h * show_h / img_h
f1, f2 = 0, (img_h - show_h) / (win_h - scroll_var)
dx, dy = dx * zoom, dy * zoom # 缩放后显示图片相对缩放图片的坐标
sx, sy = dx / img_w * (win_w - scroll_har), dy / img_h * (win_h - scroll_var)
img = img_zoom.copy() # 令缩放图片为原图
dx, dy = int(dx), int(dy)
img1 = img[dy:dy + show_h, dx:dx + show_w]
dst = img1.copy()
if horizontal and vertical:
sx, sy = int(sx), int(sy)
# 对dst1画图而非dst,避免鼠标事件不断刷新使显示图片不断进行填充
dst1 = cv2.copyMakeBorder(dst, 0, scroll_w, 0, scroll_w, cv2.BORDER_CONSTANT, value=[255, 255, 255])
cv2.rectangle(dst1, (sx, show_h), (int(sx + scroll_har), win_h), (181, 181, 181), -1) # 画水平滚动条
cv2.rectangle(dst1, (show_w, sy), (win_w, int(sy + scroll_var)), (181, 181, 181), -1) # 画垂直滚动条
elif horizontal == 0 and vertical:
sx, sy = int(sx), int(sy)
dst1 = cv2.copyMakeBorder(dst, 0, 0, 0, scroll_w, cv2.BORDER_CONSTANT, value=[255, 255, 255])
cv2.rectangle(dst1, (show_w, sy), (win_w, int(sy + scroll_var)), (181, 181, 181), -1) # 画垂直滚动条
elif horizontal and vertical == 0:
sx, sy = int(sx), int(sy)
dst1 = cv2.copyMakeBorder(dst, 0, scroll_w, 0, 0, cv2.BORDER_CONSTANT, value=[255, 255, 255])
cv2.rectangle(dst1, (sx, show_h), (int(sx + scroll_har), win_h), (181, 181, 181), -1) # 画水平滚动条
cv2.imshow("img", dst1)
cv2.waitKey(1)
img_original = cv2.imread("E:\\vs\\image\\2.png") # 此处需换成大于img_w * img_h的图片
img_original_h, img_original_w = img_original.shape[0:2] # 原图宽高
cv2.namedWindow('img', cv2.WINDOW_NORMAL)
cv2.moveWindow("img", 300, 100)
img = img_original.copy()
img_h, img_w = img.shape[0:2] # 原图宽高
show_h, show_w = 600, 800 # 显示图片宽高
horizontal, vertical = 0, 0 # 原图是否超出显示图片
dx, dy = 0, 0 # 显示图片相对于原图的坐标
scroll_w = 16 # 滚动条宽度
sx, sy = 0, 0 # 滚动块相对于滚动条的坐标
flag, flag_hor, flag_ver = 0, 0, 0 # 鼠标操作类型,鼠标是否在水平滚动条上,鼠标是否在垂直滚动条上
x1, y1, x2, y2, x3, y3 = 0, 0, 0, 0, 0, 0 # 中间变量
win_w, win_h = show_w + scroll_w, show_h + scroll_w # 窗口宽高
scroll_har, scroll_var = win_w * show_w / img_w, win_h * show_h / img_h # 滚动条水平垂直长度
wheel_step, zoom = 0.05, 1 # 缩放系数, 缩放值
zoom_w, zoom_h = img_w, img_h # 缩放图宽高
f1, f2 = (img_w - show_w) / (win_w - scroll_har), (img_h - show_h) / (win_h - scroll_var) # 原图可移动部分占滚动条可移动部分的比例
if img_h <= show_h and img_w <= show_w:
cv2.imshow("img", img)
else:
if img_w > show_w:
horizontal = 1
if img_h > show_h:
vertical = 1
i = img[dy:dy + show_h, dx:dx + show_w]
dst = i.copy()
cv2.resizeWindow("img", win_w, win_h)
cv2.setMouseCallback('img', mouse)
cv2.waitKey()
cv2.destroyAllWindows()
1、水平移动图片(鼠标左键点击水平滚动条区域,一直按住右移):
2、垂直移动图片(鼠标左键点击垂直滚动条区域,按住并下移):
3、鼠标滚轮后转缩小图片:
4、鼠标滚轮前转放大图片:
水平移动或垂直移动图片后,在放大图片会出现问题。