'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.

Effect: enter image description here enter image description here

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