项目作者: harimkang

项目描述 :
This code adds camera streaming, zooming, image capturing, and video recording using openCV.
高级语言: Python
项目地址: git://github.com/harimkang/openCV-with-Zoom.git
创建时间: 2019-12-28T13:21:35Z
项目社区:https://github.com/harimkang/openCV-with-Zoom

开源协议:MIT License

下载


opencv-with-zoom

Hits License Stars

love and jo

Blog for Korean

The READ ME contents in Korean can be found at the address below.

https://davinci-ai.tistory.com/8

Environment

  • Python3
  • opencv-python

Installation

Install the dependency.

  1. $ pip install opencv-python

Clone Repository…

  1. $ mkdir project
  2. $ cd project
  3. $ git clone https://github.com/harimkang/opencv-with-zoom.git
  4. $ cd opencv-with-zoom

Start

  1. $ python Camera.py

And you can zoom through the double click.

You can exit through the q button.

Goal

The repository will bring the camera’s screen through openCV to show it, add zoom-in and zoom-out functions, and write code to use the zoom function using screen touch events. These codes are written in python code.

opencv%20with%20zoom/Untitled.png

The part related to the function of the repository in the Activity Diagram is as shown above.

Create openCV streaming screen

First of all, write code to show the live streaming screen using openCV.

  1. import cv2
  2. class Camera:
  3. def __init__(self, mirror=False):
  4. self.data = None
  5. self.cam = cv2.VideoCapture(0)
  6. self.WIDTH = 640
  7. self.HEIGHT = 480
  8. self.center_x = self.WIDTH / 2
  9. self.center_y = self.HEIGHT / 2
  10. self.touched_zoom = False
  11. self.scale = 1
  12. self.__setup()
  13. self.recording = False
  14. self.mirror = mirror
  15. def __setup(self):
  16. self.cam.set(cv2.CAP_PROP_FRAME_WIDTH, self.WIDTH)
  17. self.cam.set(cv2.CAP_PROP_FRAME_HEIGHT, self.HEIGHT)
  18. time.sleep(2)
  19. def stream(self):
  20. # streaming thread function
  21. def streaming():
  22. # The actual threaded function
  23. self.ret = True
  24. while self.ret:
  25. self.ret, np_image = self.cam.read()
  26. if np_image is None:
  27. continue
  28. if self.mirror:
  29. # Inverted left and right in mirror mode
  30. np_image = cv2.flip(np_image, 1)
  31. self.data = np_image
  32. k = cv2.waitKey(1)
  33. if k == ord('q'):
  34. self.release()
  35. break
  36. Thread(target=streaming).start()
  37. def show(self):
  38. while True:
  39. frame = self.data
  40. if frame is not None:
  41. cv2.imshow('Davinci AI', frame)
  42. key = cv2.waitKey(1)
  43. if key == ord('q'):
  44. # q : close
  45. self.release()
  46. cv2.destroyAllWindows()
  47. break
  48. def release(self):
  49. self.cam.release()
  50. cv2.destroyAllWindows()
  51. if __name__ == '__main__':
  52. cam = Camera(mirror=True)
  53. cam.stream()
  54. cam.show()

I wrote Camera class that sends streaming screen of personal camera through methods provided in openCV. When you run the code, it simply displays the video from the camera connected to your computer.

I want to add basic zooming, zooming through touch events.

Adding basic zoom features

First of all, before we start writing the code, let’s easily represent it with a picture. Since we are going to implement zoom function through touch event, we wrote this in consideration.

The circle means the touched position, and it looks at the size of the top and bottom and sides based on the position and considers the small part as half of the new frame to find and cut the frame of the new size.

opencv%20with%20zoom/Untitled%201.png

Now let’s write the code that handles the basic zoom functions. I know that openCV doesn’t provide a zoom-related method, so I wrote a code that cuts and resizes the screen to fit a variable that is a multiple of scale.

  1. def __zoom(self, img, center=None):
  2. # actual function to zoom
  3. height, width = img.shape[:2]
  4. if center is None:
  5. # Calculation when the center value is the initial value
  6. center_x = int(width / 2)
  7. center_y = int(height / 2)
  8. radius_x, radius_y = int(width / 2), int(height / 2)
  9. else:
  10. # Calculation at a specific location
  11. center_x, center_y = center
  12. center_x, center_y = int(center_x), int(center_y)
  13. left_x, right_x = center_x, int(width - center_x)
  14. up_y, down_y = int(height - center_y), center_y
  15. radius_x = min(left_x, right_x)
  16. radius_y = min(up_y, down_y)
  17. # Actual zoom code
  18. radius_x, radius_y = int(self.scale * radius_x), int(self.scale * radius_y)
  19. # size calculation
  20. min_x, max_x = center_x - radius_x, center_x + radius_x
  21. min_y, max_y = center_y - radius_y, center_y + radius_y
  22. # Crop image to size
  23. cropped = img[min_y:max_y, min_x:max_x]
  24. # Return to original size
  25. new_cropped = cv2.resize(cropped, (width, height))
  26. return new_cropped

The code is divided into two types when the center touched and the zoom function is used. It takes the size of the image, finds the center value, calculates the size according to the scale, crops it accordingly, and increases the size to the original size to return.

  1. def touch_init(self):
  2. self.center_x = self.WIDTH / 2
  3. self.center_y = self.HEIGHT / 2
  4. self.touched_zoom = False
  5. self.scale = 1
  6. def zoom_out(self):
  7. # scale 값을 조정하여 zoom-out
  8. if self.scale < 1:
  9. self.scale += 0.1
  10. if self.scale == 1:
  11. self.center_x = self.WIDTH
  12. self.center_y = self.HEIGHT
  13. self.touched_zoom = False
  14. def zoom_in(self):
  15. # scale 값을 조정하여 zoom-in
  16. if self.scale > 0.2:
  17. self.scale -= 0.1
  18. def zoom(self, num):
  19. if num == 0:
  20. self.zoom_in()
  21. elif num == 1:
  22. self.zoom_out()
  23. elif num == 2:
  24. self.touch_init()

The zoom in and out functions are structured to function by adjusting the scale value.

  1. if self.touched_zoom:
  2. np_image = self.__zoom(np_image, (self.center_x, self.center_y))
  3. else:
  4. if not self.scale == 1:
  5. np_image = self.__zoom(np_image)

Put the above code before the middle of the stream method (self.data = np_image) to check whether the zoom by touch function is already executed or the basic zoom function is executed.

Adding zoom function by Touch-Event

  1. def show(self):
  2. while True:
  3. frame = self.data
  4. if frame is not None:
  5. cv2.imshow('Davinci AI', frame)
  6. cv2.setMouseCallback('Davinci AI', self.mouse_callback)
  7. key = cv2.waitKey(1)
  8. if key == ord('q'):
  9. # q : close
  10. self.release()
  11. cv2.destroyAllWindows()
  12. break
  13. def mouse_callback(self, event, x, y, flag, param):
  14. if event == cv2.EVENT_LBUTTONDBLCLK:
  15. self.get_location(x, y)
  16. self.zoom_in()
  17. elif event == cv2.EVENT_RBUTTONDOWN:
  18. self.zoom_out()

In the code above, I added the red background cv2.setMouseCallback (‘Davinci AI’, self.mouse_callback) to the show function. That code means you assign a function called self.mouse_callback to a function that handles touch events on the screen.

The following mouse_callback function is a function that handles touch events. The code is configured to be zoom_in when double-clicking the left mouse and zoom_out when double-clicking the right mouse.

opencv%20with%20zoom/Untitled%202.png

Problem occurred!

Touch zoom at the edges tends to distort the screen or to zoom in too suddenly, so we want to fix it so that it’s proportional to touch zoom.

As shown in the figure below, during touch event of the edge part, the touch position is adjusted according to a certain ratio to prevent distortion. Enable magnification while maintaining a reasonable ratio.

opencv%20with%20zoom/Untitled%203.png

The code above is shown below.

  1. def __zoom(self, img, center=None):
  2. # actual function to zoom
  3. height, width = img.shape[:2]
  4. if center is None:
  5. # Calculation when the center value is the initial value
  6. center_x = int(width / 2)
  7. center_y = int(height / 2)
  8. radius_x, radius_y = int(width / 2), int(height / 2)
  9. else:
  10. # Calculation at a specific location
  11. rate = height / width
  12. center_x, center_y = center
  13. # Calculate centroids for ratio range
  14. if center_x < width * (1-rate):
  15. center_x = width * (1-rate)
  16. elif center_x > width * rate:
  17. center_x = width * rate
  18. if center_y < height * (1-rate):
  19. center_y = height * (1-rate)
  20. elif center_y > height * rate:
  21. center_y = height * rate
  22. center_x, center_y = int(center_x), int(center_y)
  23. left_x, right_x = center_x, int(width - center_x)
  24. up_y, down_y = int(height - center_y), center_y
  25. radius_x = min(left_x, right_x)
  26. radius_y = min(up_y, down_y)
  27. # Actual zoom code
  28. radius_x, radius_y = int(self.scale * radius_x), int(self.scale * radius_y)
  29. # size calculation
  30. min_x, max_x = center_x - radius_x, center_x + radius_x
  31. min_y, max_y = center_y - radius_y, center_y + radius_y
  32. # Crop image to size
  33. cropped = img[min_y:max_y, min_x:max_x]
  34. # Return to original size
  35. new_cropped = cv2.resize(cropped, (width, height))
  36. return new_cropped

Added a red background code to the __zoom function responsible for the zoom function.

opencv%20with%20zoom/Untitled%204.png

Additional features

In addition, we want to add a function to capture and save the current image and to record a video.

  1. def save_picture(self):
  2. # Function to save image
  3. ret, img = self.cam.read()
  4. if ret:
  5. now = datetime.datetime.now()
  6. date = now.strftime('%Y%m%d')
  7. hour = now.strftime('%H%M%S')
  8. user_id = '00001'
  9. filename = './images/cvui_{}_{}_{}.png'.format(date, hour, user_id)
  10. cv2.imwrite(filename, img)
  11. self.image_queue.put_nowait(filename)

First is the image capture function. You need to create a folder called images first.

  1. def record_video(self):
  2. # Video recording function
  3. fc = 20.0
  4. record_start_time = time.time()
  5. now = datetime.datetime.now()
  6. date = now.strftime('%Y%m%d')
  7. t = now.strftime('%H')
  8. num = 1
  9. filename = 'videos/cvui_{}_{}_{}.avi'.format(date, t, num)
  10. while os.path.exists(filename):
  11. num += 1
  12. filename = 'videos/cvui_{}_{}_{}.avi'.format(date, t, num)
  13. codec = cv2.VideoWriter_fourcc('D', 'I', 'V', 'X')
  14. out = cv2.VideoWriter(filename, codec, fc, (int(self.cam.get(3)), int(self.cam.get(4))))
  15. while self.recording:
  16. if time.time() - record_start_time >= 600:
  17. self.record_video()
  18. break
  19. ret, frame = self.cam.read()
  20. if ret:
  21. if len(os.listdir('./videos')) >= 100:
  22. name = self.video_queue.get()
  23. if os.path.exists(name):
  24. os.remove(name)
  25. out.write(frame)
  26. self.video_queue.put_nowait(filename)
  27. k = cv2.waitKey(1)
  28. if k == ord('q'):
  29. break

Next is the video recording function. The code is configured to be saved as another file when recording for more than 10 minutes. It also queues to delete over 100 files. (Method to adjust file size not too big)