Unable to adjust resolution by opencv under python environment - python

I am currently try to adjust the resolution of the video by opencv, according to my research, cap.set is the method to do so. thus, I write the code below:
cap = cv2.VideoCapture('IMG_2957.MOV')
for i in range(19):
print (""+str(i)+" "+str(cap.get(i)))
cap.set(3,160) #Width
cap.set(4,120) #Height
for i in range(19):
print (""+str(i)+" "+str(cap.get(i)))
very surprise, the value of "CV_CAP_PROP_FRAME_WIDTH" and "CV_CAP_PROP_FRAME_HEIGHT" remain unchanged
Before the cv.set
3 1920.0
4 1080.0
And after the setting, the frame was not set as what I want
After the cv.set
3 1920.0
4 1080.0
Is that we are not able to change the resolution by setting its property? Can anyone advice me the usage of .set of cv2.videocapture?
More information about the version:
Python 3.6.0 [MSC v.1900 64 bit (AMD64)] on win32
>>> import cv2
>>> cv2.__version__
'3.4.0'

First, it is important to understand that VideoCapture is more than just a class to playback a video file. It can also connect to a stream or a camera for example. Then it may have properties that will work only for specific cases and that is why they have a set / get generic function. With a webcam, if your webcam/drivers allow it you may set the resolution for example with the code snippet you supply.
The set function returns a bool value in C++ (and in python) not sure if this means that it is set or not, but you can check it out, probably yours returns false. I am not sure, since I had not checked that and the documentation does not provide an explanation.
In a video, it will read each frame, but it may not change the properties of the video. What can you do then? Depends on what you are planning to do with it.
You may use the resize, for example:
retval, frame = cap.read()
res = cv2.resize(frame,(160, 120), interpolation = cv2.INTER_CUBIC)
Then you may save the video with VideoWritter or do any image manipulation to it.

Related

Fast screenshot of a small part of the screen in Python

I am currently working on a project where I need to take a 30x40 pixels screenshot from a specific area of my screen. This is not very hard to do as there are plenty of methods that do that.
The issue I have is that I need to take about 10 to 15 screenshots/second of the size I mentioned. When I looked at some of these methods that capture the screen, I have seen that when you give them parameters for a smaller selection, there's cropping involved. So a full screenshot is being taken, then the method crops it to the given size. That seems like a waste of resources if I'm only going to use 30x40 image, especially considering I will take thousands of screenshots.
So my question is: Is there a method that ONLY captures a part of the screen without capturing the whole screen cutting the desired section out of the big screenshot? I'm currently using this command:
im = pyautogui.screenshot(region=(0,0, 30, 40)).
The Python mss module ( https://github.com/BoboTiG/python-mss , https://python-mss.readthedocs.io/examples.html ), an ultra fast cross-platform multiple screenshots module in pure Python using ctypes ( where MSS stands for Multiple Screen Shots ), is what you are looking for. The screenshots are fast enough to capture frames from a video and the smaller the part of the screen to grab the faster the capture (so there is apparently no cropping involved ). Check it out. mss.mss().grab() outperforms by far PIL.ImageGrab.grab(). Below a code example showing how to get the data of the screenshot pixels (allows to detect changes):
import mss
from time import perf_counter as T
left = 0
right = 2
top = 0
btm = 2
with mss.mss() as sct:
# parameter for sct.grab() can be:
monitor = sct.monitors[1] # entire screen
bbox = (left, top, right, btm) # screen part to capture
sT=T()
sct_im = sct.grab(bbox) # type: <class 'mss.screenshot.ScreenShot'>
eT=T();print(" >", eT-sT) # > 0.0003100260073551908
print(len(sct_im.raw), sct_im.raw)
# 16 bytearray(b'-12\xff\x02DU\xff-12\xff"S_\xff')
print(len(sct_im.rgb), sct_im.rgb)
# 12 b'21-UD\x0221-_S"'

Changing Screen Resolution of Computer using python

I'm creating a python program that is supposed to streamline the process of setting up a computer. I want this python program to change the screen resolution of the computer and scaling of it. I'm not sure what the best approach is however, or how to approach it.
I've tried using an example pywin32 program, but it only outputted an array of resolution sizes
I had a look how to change screen resolution using C++ and then translated it to Python:
import win32api
import win32con
import pywintypes
devmode = pywintypes.DEVMODEType()
devmode.PelsWidth = 1366
devmode.PelsHeight = 768
devmode.Fields = win32con.DM_PELSWIDTH | win32con.DM_PELSHEIGHT
win32api.ChangeDisplaySettings(devmode, 0)
We needed a DEVMODE object to pass to the ChangeDisplaySettings function. The pywintypes module which is also part of pywin32 has a function to create objects of type DEVMODE.
We then set the PelsWidth and PelsHeight fields and also the Fields field to tell the API which field's values we want to use.
To change back to the previous resolution, simply call:
win32api.ChangeDisplaySettings(None, 0)
Thanks for asking the question. I've learned something.

NameError: global name 'CV_GUI_NORMAL' is not defined

I'm coding using Python and OpenCV on Ubuntu 14.04. When I click on the right button of the mouse, the associated mouse event cv2.EVENT_RBUTTONDOWN does not work and I rather get context menu ("actions"). Is there a way to disable the context menu popup?
A user gave me a hint and I am sure the solution is somewhere there. He asked me to add CV_GUI_NORMAL as shown on here.
So I run: cv2.namedWindow("Window",CV_GUI_NORMAL) but I got this error:
NameError: global name 'CV_GUI_NORMAL' is not defined
When I try cv2.CV_GUI_NORMAL as on the below user's comment instead, I get this error:
AttributeError: 'module' object has no attribute 'CV_GUI_NORMAL'
Note that similar question was asked here but I do not want to change OpenCV code.
How to fix this ?
.
You can use cv2.WINDOW_GUI_NORMAL as follows:
cv2.namedWindow('desired_name_of_window', flags= cv2.WINDOW_GUI_NORMAL)
Allowed windows values in cv2 are:
WINDOW_AUTOSIZE = 1
WINDOW_FREERATIO = 256
WINDOW_FULLSCREEN = 1
WINDOW_GUI_EXPANDED = 0
WINDOW_GUI_NORMAL = 16
WINDOW_KEEPRATIO = 0
WINDOW_NORMAL = 0
WINDOW_OPENGL = 4096
WND_PROP_ASPECT_RATIO = 2
WND_PROP_AUTOSIZE = 1
WND_PROP_FULLSCREEN = 0
WND_PROP_OPENGL = 3
WND_PROP_VISIBLE = 4
The official documentation says:
Python:
cv.NamedWindow(name, flags=CV_WINDOW_AUTOSIZE) → None
Parameters:
name – Name of the window in the window caption that may be used as a window identifier.
flags –
Flags of the window. The supported flags are:
WINDOW_NORMAL If this is set, the user can resize the window (no constraint).
WINDOW_AUTOSIZE If this is set, the window size is automatically adjusted to fit the displayed image (see imshow() ), and you cannot change the window size manually.
WINDOW_OPENGL If this is set, the window will be created with OpenGL support.
Only some implementations with Qt backend support CV_GUI_NORMAL. It seems you have no choice than to install cv2 with Qt support or use other variables.
In that case you'll be using cv2.CV_WINDOW_NORMAL.
For a starter you could build without Qt support if you do not need it. It seems to do more harm than good in many cases. So it is better set the flag WINDOW_OPENGL: That way you disable the QT support and get the OpenGL one.

Python OpenCV access webcam maximum resolution

I am trying to acquire images from my webcam using a python code that imports OpenCV. The code is the following:
import sys
sys.path.append("C:\\opencv\\build\\python\\2.7")
import cv2
import cv2.cv as cv
import time
# Set resolution
cap = cv2.VideoCapture(0)
print "Frame default resolution: (" + str(cap.get(cv.CV_CAP_PROP_FRAME_WIDTH)) + "; " + str(cap.get(cv.CV_CAP_PROP_FRAME_HEIGHT)) + ")"
cap.set(cv.CV_CAP_PROP_FRAME_WIDTH, 800)
cap.set(cv.CV_CAP_PROP_FRAME_HEIGHT, 600)
print "Frame resolution set to: (" + str(cap.get(cv.CV_CAP_PROP_FRAME_WIDTH)) + "; " + str(cap.get(cv.CV_CAP_PROP_FRAME_HEIGHT)) + ")"
# Acquire frame
capture = cv.CreateCameraCapture(0)
img = cv.QueryFrame(capture)
The code works fine, except that the Camera default resolution is 640x480, and my code seems to be able to set only resolution values lower than that. For example, I can set the image size to 320x240, but I can't change it to 800x600. I have no error appearing: simply the resolution is set to the default one (640x480) as I try to set it to higher values.
The camera I am using (no other webcam is connected to the computer) is the QuickCam V-UBK45: with the software provided by Logitech, I am able to take pictures at full resolution (1280x960) and at all intermediate ones (e.g. 800x600).
Therefore, those frame sizes are supported from the hardware, but my code can't access them.
Does anyone know what I can do?
The problem as mentioned above is caused by the camera driver. I was able to fix it using Direct Show as a backend. I read (sorry, but I do not remember where) that almost all cameras provide a driver that allows their use from DirectShow. Therefore, I used DirectShow in Windows to interact with the cameras and I was able to configure the resolution as I wanted and also get the native aspect ratio of my camera (16:9).
You can try this code to see if this works for you:
import cv2
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) # this is the magic!
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
r, frame = cap.read()
...
print('Resolution: ' + str(frame.shape[0]) + ' x ' + str(frame.shape[1]))
In the OpenCV documentation, I found the following information for those who want to know more about OpenCV backends (OpenCV docs)
I hope this can help you!
I used the different resolutions to set image resolution from List of common resolutions by looping over
def set_res(cap, x,y):
cap.set(cv.CV_CAP_PROP_FRAME_WIDTH, int(x))
cap.set(cv.CV_CAP_PROP_FRAME_HEIGHT, int(y))
return str(cap.get(cv.CV_CAP_PROP_FRAME_WIDTH)),str(cap.get(cv.CV_CAP_PROP_FRAME_HEIGHT))
It seems that OpenCV or my camera allows only certain resolutions.
160.0 x 120.0
176.0 x 144.0
320.0 x 240.0
352.0 x 288.0
640.0 x 480.0
1024.0 x 768.0
1280.0 x 1024.0
I got it to work, so this post is for others experiencing the same problem:
I am running on the Logitech C270 as well. For some reason it would only show 640x480 even though the webcam supports 1280x720. Same issue persists with the built-in webcam in my laptop.
If I set it to 800x600 in the code it shows 640x480. However, if I set it to 1024x768 it becomes 800x600. And if I set it to something silly like 2000x2000 it becomes 1280x720.
This is in C++ on OpenCV 3.0, but perhaps it applies to Python as well.
Try the following code to obtain the maximum camera resolution, using this you can capture your photos or video using maximum resolution:
import cv2
HIGH_VALUE = 10000
WIDTH = HIGH_VALUE
HEIGHT = HIGH_VALUE
capture = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc(*'XVID')
capture.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)
width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(width,height)
For cv2 just change to this.
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 800)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 600)
OpenCV now only allows only these Resolutions.
'320.0x240.0': 'OK'
'640.0x480.0': 'OK'
'1280.0x720.0': 'OK'
Source: https://www.learnpythonwithrune.org/find-all-possible-webcam-resolutions-with-opencv-in-python/
These are the common resolutions. It may support some more resolutions, you can check.
If you can not find the supported resolution. You can also use:
frame = imutils(frame, width = 720)
This will set you to the nearer supported resolution.
Note: use you required value for the width and will set it to the nearer supported resolution and then you can check the supported resolution by using:
print(frame.shape)
imutils method is completely based on experience and testing around.

How to make transparent background white instead of black when converting PDF to JPG using PythonMagick

I'm trying to convert from PDF to JPG using PythonMagick, but I can't find a way to set the background color, which by default is changed from transparent to black. I can get the desired result using os.system and the -flatten parameter as shown below.
import os
os.system('convert -flatten -background \#ffffff -density 400 -adaptive-resize 1900x infile.pdf outfile.jpg')
However, PythonMagick does not seem to have a flatten method and the following snippet produces an image with a black background.
import PythonMagick
import os
img = PythonMagick.Image("infile.pdf")
img.backgroundColor('#ffffff')
img.density('400')
img.resize('1900x')
img.magick('JPG')
img.quality(60)
img.write("outfile.jpg")
There is also a transparent() method that takes a color. I'm not quite sure what it's for, but img.transparent('#ffffff') did not help. Is there another way to achieve the same result? I'd rather not do it using os.system, since it seems to take quite alot longer.
If you look at the documentation for the -flatten command-line option, you will see it is an alias for -layers flatten.
The -layers flatten command is itself a combination command, which comprises creating a layer of the current background colour the size of the first images canvas, and then composing each layer in turn on top of it.
PythonMagick is essentially just a binding layer to the Magick++ C++ interface. The advanced commands that convert provides, are not necessarily replicated in the lower level libraries, as they are really a sequence of commands as described above. So whilst there is no single command for it in the PythonMagick library, the functionality can be replicated.
The method you are after is .composite(), the PythonMagick documentation is so limited ( or indeed non-existent), most people stay clear of the library. But I think the usage is something like this, if there was only one layer in the PDF (totally untested):
import PythonMagick
img = PythonMagick.Image("infile.pdf")
img.density('400')
bgColour = PythonMagick.ColorRGB(1.0, 1.0, 1.0)
size = "%sx%s" % (img.columns(), img.rows())
flattened = PythonMagick.Image(size, bgColour)
flattened.type = img.type
flattened.composite(img, 0, 0, PythonMagick.CompositeOperator.SrcOverCompositeOp)
flattened.resize('1900x')
flattened.magick('JPG')
flattened.quality(60)
flattened.write("outfile.jpg")
NB. The composition operator could be PythonMagick.CompositeOperator.DstOverCompositeOp, I'm not sure which way round it is handling that.
Though PDFs are a special case with ImageMagick, as they are usually passed off to ghostscript to rasterize. Which means you might need to give ghostscript (gs) some odd parameters to handle the alpha channel properly. Try adding verbose options to the command that works to see what delegate commands it issues and consider doing the PDF rasterisation yourself via an os.system('gs ...') command and then doing the resize. Though I doubt that would be faster than just calling convert.

Categories

Resources