'Python capture a specific part of an image after zooming in OpenCV
So I'm trying to zoom in and out using my webcam and capture the image of a specific part in the live webcam after zooming. However, I'm able to zoom in and out but by using "cam.read()" I am only able to capture the entire image and not the specific part of the image that I have zoomed. I have defined the ROI but the issue is now that the ROI is working but i can't get the image to zoom in or out. I can't seem to find the problem.
import cv2
import time
import os
import datetime
from threading import Thread
from queue import Queue
import numpy as np
import imutils
from PIL import Image
class Camera:
def __init__(self, mirror=False):
self.data = None
self.cam = cv2.VideoCapture(0, cv2.CAP_DSHOW)
self.WIDTH = 640
self.HEIGHT = 480
self.center_x = self.WIDTH / 2
self.center_y = self.HEIGHT / 2
self.touched_zoom = False
self.image_queue = Queue()
self.video_queue = Queue()
self.scale = 1
self.__setup()
self.recording = False
self.image_coordinates = []
self.extract = False
self.selected_ROI = False
self.select_roi()
self.mirror = mirror
def __setup(self):
self.cam.set(cv2.CAP_PROP_FRAME_WIDTH, self.WIDTH)
self.cam.set(cv2.CAP_PROP_FRAME_HEIGHT, self.HEIGHT)
time.sleep(2)
def get_location(self, x, y):
self.center_x = x
self.center_y = y
self.touched_zoom = True
def stream(self):
def streaming():
self.ret = True
while self.ret:
self.ret, np_image = self.cam.read()
if np_image is None:
continue
if self.mirror:
np_image = cv2.flip(np_image, 1)
if self.touched_zoom:
np_image = self.__zoom(np_image, (self.center_x, self.center_y))
else:
if not self.scale == 1:
np_image = self.__zoom(np_image)
self.data = np_image
k = cv2.waitKey(1)
if k == ord('q'):
self.release()
break
Thread(target=streaming).start()
def __zoom(self, img, center=None):
height, width = img.shape[:2]
if center is None:
center_x = int(width / 2)
center_y = int(height / 2)
radius_x, radius_y = int(width / 2), int(height / 2)
else:
rate = height / width
center_x, center_y = center
if center_x < width * (1-rate):
center_x = width * (1-rate)
elif center_x > width * rate:
center_x = width * rate
if center_y < height * (1-rate):
center_y = height * (1-rate)
elif center_y > height * rate:
center_y = height * rate
center_x, center_y = int(center_x), int(center_y)
left_x, right_x = center_x, int(width - center_x)
up_y, down_y = int(height - center_y), center_y
radius_x = min(left_x, right_x)
radius_y = min(up_y, down_y)
radius_x, radius_y = int(self.scale * radius_x), int(self.scale * radius_y)
min_x, max_x = center_x - radius_x, center_x + radius_x
min_y, max_y = center_y - radius_y, center_y + radius_y
cropped = img[min_y:max_y, min_x:max_x]
new_cropped = cv2.resize(cropped, (width, height), interpolation=cv2.INTER_CUBIC)
return new_cropped
def touch_init(self):
self.center_x = self.WIDTH / 2
self.center_y = self.HEIGHT / 2
self.touched_zoom = False
self.scale = 1
def zoom_out(self):
if self.scale < 1:
self.scale += 0.1
if self.scale == 1:
self.center_x = self.WIDTH
self.center_y = self.HEIGHT
self.touched_zoom = False
def zoom_in(self):
if self.scale > 0.2:
self.scale -= 0.1
def zoom(self, num):
if num == 0:
self.zoom_in()
elif num == 1:
self.zoom_out()
elif num == 2:
self.touch_init()
def save_picture(self):
ret, img = self.cam.read()
if ret:
now1 = datetime.datetime.now()
now_str1 = now1.strftime("%Y-%m-%d-%H-%M-%S")
outfilename1 = 'Img-{}.jpg'.format(now_str1)
path = 'C:/Users/dashi/Documents/MyRiV/espeyh_images'
cv2.imwrite(os.path.join(path, outfilename1), img)
path1 = ('C:/Users/dashi/Documents/MyRiV/espeyh_images/' + outfilename1)
imag = cv2.imread(path1)
lower = np.array([140, 140, 140])
upper = np.array([255, 255, 255])
thresh = cv2.inRange(imag, lower, upper)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (26, 30))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
mask = 255 - morph
result = cv2.bitwise_and(imag, imag, mask=mask)
cv2.imshow('Background Removed', result)
outfilename2 = 'Img-{}.jpg'.format(now_str1)
path2 = ('C:/Users/dashi/Documents/MyRiV/modified_images')
cv2.imwrite(os.path.join(path2, outfilename2), result)
def record_video(self):
fc = 20.0
record_start_time = time.time()
now = datetime.datetime.now()
date = now.strftime('%Y%m%d')
t = now.strftime('%H')
num = 1
filename = 'C:/Users/dashi/Documents/MyRiV/espeyh_videos/captured_{}_{}.avi'.format(date, t, num)
while os.path.exists(filename):
num += 1
filename = 'videos/captured_{}_{}_{}.avi'.format(date, t, num)
codec = cv2.VideoWriter_fourcc('D', 'I', 'V', 'X')
out = cv2.VideoWriter(filename, codec, fc, (int(self.cam.get(3)), int(self.cam.get(4))))
while self.recording:
if time.time() - record_start_time >= 600:
self.record_video()
break
ret, frame = self.cam.read()
if ret:
if len(os.listdir('C:/Users/dashi/Documents/MyRiV/espeyh_videos')) >= 100:
name = self.video_queue.get()
if os.path.exists(name):
os.remove(name)
out.write(frame)
self.video_queue.put_nowait(filename)
k = cv2.waitKey(1)
if k == ord('q'):
break
def crop_ROI(self):
if self.selected_ROI:
self.cropped_image = self.frame.copy()
x1 = self.image_coordinates[0][0]
y1 = self.image_coordinates[0][1]
x2 = self.image_coordinates[1][0]
y2 = self.image_coordinates[1][1]
self.cropped_image = self.cropped_image[y1:y2, x1:x2]
print('Cropped image: {} {}'.format(self.image_coordinates[0], self.image_coordinates[1]))
else:
print('Select ROI to crop before cropping')
def show_cropped_ROI(self):
cv2.imshow('cropped image', self.cropped_image)
def select_roi(self):
while True:
if self.cam.isOpened():
# Read frame
(self.status, self.frame) = self.cam.read()
cv2.imshow('image', self.frame)
key = cv2.waitKey(2)
# Crop image
if key == ord('c'):
self.clone = self.frame.copy()
cv2.namedWindow('image')
cv2.setMouseCallback('image', self.mouse_callback)
while True:
key = cv2.waitKey(2)
cv2.imshow('image', self.clone)
# Crop and display cropped image
if key == ord('c'):
self.crop_ROI()
self.show_cropped_ROI()
# Resume video
if key == ord('r'):
break
# Close program with keyboard 'q'
if key == ord('q'):
cv2.destroyAllWindows()
exit(1)
else:
pass
def show(self):
while True:
frame = self.data
if frame is not None:
cv2.imshow('Zooming', frame)
cv2.setMouseCallback('Zooming', self.mouse_callback)
key = cv2.waitKey(1)
if key == ord('f'):
if key == ord('q'):
self.release()
cv2.destroyAllWindows()
break
elif key == ord('z'):
self.zoom_in()
elif key == ord('x'):
self.zoom_out()
elif key == ord('p'):
self.save_picture()
elif key == ord('v'):
self.touch_init()
elif key == ord('r'):
self.recording = not self.recording
if self.recording:
t = Thread(target=cam.record_video)
t.start()
elif key == ord('k'):
self.select_roi()
def release(self):
self.cam.release()
cv2.destroyAllWindows()
def mouse_callback(self, event, x, y, flag, parameters):
if event == cv2.EVENT_LBUTTONDBLCLK:
self.get_location(x, y)
self.zoom_in()
elif event == cv2.EVENT_RBUTTONDOWN:
self.zoom_out()
elif event == cv2.EVENT_LBUTTONDOWN:
self.image_coordinates = [(x,y)]
self.extract = True
elif event == cv2.EVENT_LBUTTONUP:
self.image_coordinates.append((x,y))
self.extract = False
self.selected_ROI = True
# Draw rectangle around ROI
cv2.rectangle(self.clone, self.image_coordinates[0], self.image_coordinates[1], (0,255,0), 2)
elif event == cv2.EVENT_RBUTTONDOWN:
self.clone = self.frame.copy()
self.selected_ROI = False
if __name__ == '__main__':
cam = Camera(mirror=True)
cam.stream()
cam.show()
Solution 1:[1]
You should crop the images you are recording the same way you did for the display.
Also, you are reading from the VideoCapture from 2 threads. I don't think that's a good idea : you are probably displaying and recording one image over 2. (the first is displayed, the second is recorded, the third is displayed, ...).
You should call self.cam.read() at one place only, crop the image if needed, and make it available for both display and recording.
Solution 2:[2]
The basic idea is deciding the scale changed every time on mouse wheel. After you get the current scale (v.s. origin image) and correct region of image you want to show on screen, you can get the position and length of rectangle on scaled image. So you can draw this rectangle on scaled image.
In my github?checking OnMouseWheel () and RefreshSrcView () in Fastest_Image_Pattern_Matching/ELCVMatchTool/ELCVMatchToolDlg.cpp may give what you want.
Although it's c++ code, only imshow () and resize () are used, meaning that transforming to python code is easy for you. Focusing on how I change the scale and how the new rectangle to be draw in scaled image will be enough.
Part of the code:
BOOL CELCVMatchToolDlg::OnMouseWheel (UINT nFlags, short zDelta, CPoint pt)
{
POINT pointCursor;
GetCursorPos (&pointCursor);
ScreenToClient (&pointCursor);
// TODO: ???????????????? (?) ?????
if (zDelta > 0)
{
if (m_iScaleTimes == MAX_SCALE_TIMES)
return TRUE;
else
m_iScaleTimes++;
}
if (zDelta < 0)
{
if (m_iScaleTimes == MIN_SCALE_TIMES)
return TRUE;
else
m_iScaleTimes--;
}
CRect rect;
//GetWindowRect (rect);
GetDlgItem (IDC_STATIC_SRC_VIEW)->GetWindowRect (rect);//??
if (m_iScaleTimes == 0)
g_dCompensationX = g_dCompensationY = 0;
int iMouseOffsetX = pt.x - (rect.left + 1);
int iMouseOffsetY = pt.y - (rect.top + 1);
double dPixelX = (m_hScrollBar.GetScrollPos () + iMouseOffsetX + g_dCompensationX) / m_dNewScale;
double dPixelY = (m_vScrollBar.GetScrollPos () + iMouseOffsetY + g_dCompensationY) / m_dNewScale;
m_dNewScale = m_dSrcScale * pow (SCALE_RATIO, m_iScaleTimes);
if (m_iScaleTimes != 0)
{
int iWidth = m_matSrc.cols;
int iHeight = m_matSrc.rows;
m_hScrollBar.SetScrollRange (0, int (m_dNewScale * iWidth - m_dSrcScale * iWidth) - 1 + BAR_SIZE);
m_vScrollBar.SetScrollRange (0, int (m_dNewScale * iHeight - m_dSrcScale * iHeight) - 1 + BAR_SIZE);
int iBarPosX = int (dPixelX * m_dNewScale - iMouseOffsetX + 0.5);
m_hScrollBar.SetScrollPos (iBarPosX);
m_hScrollBar.ShowWindow (SW_SHOW);
g_dCompensationX = -iBarPosX + (dPixelX * m_dNewScale - iMouseOffsetX);
int iBarPosY = int (dPixelY * m_dNewScale - iMouseOffsetY + 0.5);
m_vScrollBar.SetScrollPos (iBarPosY);
m_vScrollBar.ShowWindow (SW_SHOW);
g_dCompensationY = -iBarPosY + (dPixelY * m_dNewScale - iMouseOffsetY);
//????
SCROLLINFO infoH;
infoH.cbSize = sizeof (SCROLLINFO);
infoH.fMask = SIF_PAGE;
infoH.nPage = BAR_SIZE;
m_hScrollBar.SetScrollInfo (&infoH);
SCROLLINFO infoV;
infoV.cbSize = sizeof (SCROLLINFO);
infoV.fMask = SIF_PAGE;
infoV.nPage = BAR_SIZE;
m_vScrollBar.SetScrollInfo (&infoV);
//????
}
else
{
m_hScrollBar.SetScrollPos (0);
m_hScrollBar.ShowWindow (SW_HIDE);
m_vScrollBar.SetScrollPos (0);
m_vScrollBar.ShowWindow (SW_HIDE);
}
RefreshSrcView ();
return CDialogEx::OnMouseWheel (nFlags, zDelta, pt);
}
Solution 3:[3]
You can do in this way:
var wantShow = true;
void changeState() {
setState(() {
wantShow = !wantShow;
});
}
bottomNavigationBar: BottomNavigationBar(
elevation: 8.0,
items: [
wantShow ? BottomNavigationBarItem(
icon: Padding(
padding: EdgeInsets.all(2.0),
child: SvgPicture.asset("${IMAGE_PATH}fi-rr-home.svg"),
),
activeIcon: Padding(
padding: EdgeInsets.all(2.0),
child: SvgPicture.asset("${IMAGE_PATH}fi-sr-home.svg"),
) : SizedBox(),
],
),
Solution 4:[4]
var wantShow = true;
void changeState() {
setState(() {
wantShow = !wantShow;
});
}
bottomNavigationBar: BottomNavigationBar(
elevation: 8.0,
items: [
if(wantShow) BottomNavigationBarItem(
icon: Padding(
padding: EdgeInsets.all(2.0),
child: SvgPicture.asset("${IMAGE_PATH}fi-rr-home.svg"),
),
activeIcon: Padding(
padding: EdgeInsets.all(2.0),
child: SvgPicture.asset("${IMAGE_PATH}fi-sr-home.svg"),
),
],
),
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Fab-B |
| Solution 2 | |
| Solution 3 | Amirsalar Salehi |
| Solution 4 | Edie Kamau |


