Print frame size above frame - opencv

I found the size of the frame, the information is displayed in the console and now I need the size to be displayed above the frame, it doesn’t work through putText.
OpenCV 4.7.0.68
please describe the full function of the code
import numpy as np
import cv2
cap=cv2.VideoCapture(1)
kernel=np.ones((2,2),np.uint8)
while (True):
# Capture frame-by-frame
ret,frame=cap.read()
# Our operations on the frame come here
gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
gray=cv2.GaussianBlur(gray,(7,7),0)
gray=cv2.medianBlur(gray,5) # to remove salt and paper noise
# to binary
ret,thresh=cv2.threshold(gray,128,128,128) # to detect white objects
# to get outer boundery only
thresh=cv2.morphologyEx(thresh,cv2.MORPH_GRADIENT,kernel)
# to strength week pixels
thresh=cv2.dilate(thresh,kernel,iterations=1)
im2=contours,hierarchy=cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
if len(contours)>0:
cv2.drawContours(frame,contours,-5,(0,255,0),3)
# find the biggest countour (c) by the area
c=max(contours,key=cv2.contourArea)
x,y,w,h=cv2.boundingRect(c)
# draw the biggest contour (c) in green
cv2.rectangle(frame,(x,y),(x+w,y+h),(255,255,0),2)
cv2.rectangle (frame,(x,y),(x+w*1,y+h//2),(255,255,0),2)
frameWidth=cap.get (cv2.CAP_PROP_FRAME_WIDTH)
frameHeight=cap.get (cv2.CAP_PROP_FRAME_HEIGHT)
print (frameWidth / 43 / 3.8)
print (frameHeight / 43 / 3.8)
# Display the resulting frame
cv2.imshow('frame',frame,)
if cv2.waitKey(27)&0xFF==ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()

Related

What is the purpose of decimation when calibrating a camera with Charuco?

I have been working on performing camera calibration using ChAruCo boards.
Following the code here (my commented version is shown below), it appears that only every other image is used when performing the camera calibration - due to the decimator.
What could be a reason for this? Other than to save processing power, which seems unnecessary since this step is only performed once.
def read_chessboards(chessboard_images):
# Charuco base pose estimation.
print("POSE ESTIMATION STARTS:")
# Declare lists to store corner locations and IDs
allCorners = []
allIds = []
decimator = 0
# SUB PIXEL CORNER DETECTION CRITERION
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.00001)
# for each of the chessboard images
for im in chessboard_images:
print("=> Processing image {0}".format(im))
frame = cv2.imread(im) # read current image into frame variable
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # convert to grayscale
corners, ids, rejectedImgPoints = cv2.aruco.detectMarkers(gray, ARUCO_DICT) # detect markers present in image
# if there are any markers detected
if len(corners) > 0:
# SUB PIXEL DETECTION
for corner in corners:
# refine corner locations
# TODO: check if this works
cv2.cornerSubPix(gray, corner,
winSize=(3, 3),
zeroZone=(-1, -1),
criteria=criteria)
# interpolate position of ChArUco board corners.
res2 = cv2.aruco.interpolateCornersCharuco(corners, ids, gray, board)
print(f'Charuco corners at: {res2}')
# if 3+ corners are detected, add to allCorners list for every other image
if res2[1] is not None and res2[2] is not None and len(res2[1]) > 3 and decimator % 1 == 0:
allCorners.append(res2[1])
allIds.append(res2[2])
# why only every other chessboard image?
decimator += 1
imsize = gray.shape
return allCorners, allIds, imsize
Just realized that x % 1 always evaluates to 0, so it doesn't actually do anything. I guess it was included as an optional feature - if you change 1 to some other number.

frame rate not correct in videos

I have a 12 sec video in 25 fps. When I play the video in 25 fps in opencv the video becomes 16 sec. I get the fps by using
fps = get(cv2.CAP_PROP_FPS) and then I set waitKey(1000/fps) but the video plays to slow...
import numpy as np
import cv2
import time
start = time.time()
cap = cv2.VideoCapture("hackerman.mp4")
fps = cap.get(cv2.CAP_PROP_FPS)
print(fps)
while True:
# Capture frame-by-frame
ret, frame = cap.read()
if ret == True:
frame_new = frame
else:
end = time.time()
# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # turn video gray
# Display the resulting frame
cv2.namedWindow('frame', cv2.WINDOW_NORMAL)
cv2.imshow('frame', frame_new)
k = cv2.waitKey(int(round(1000/fps))) & 0xFF
if k == 27: # wait for ESC key to exit
break
elif cv2.getWindowProperty("frame", 0) == -1:
break
print(end-start)
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
It appears that not unreasonably cap.read() is reading faster than the frame rate, which would be highly desirable if you were processing the frames rather than displaying them - so in your application you need to add a delay using e.g. time.sleep() or in your case waitKey() and this must be calculated to hit the frame rate 25fps.
For most precise 25fps, base the time for the end of the next frame on the overall starting time, like this (untested):
frameref_ms = int(time.time()*1000)
frametime_ms = int(1000/fps)
while True:
# update frameref to end frame period from now (regardless of how long reading and displaying the frame takes)
frameref_ms += frametime_ms
# Capture frame-by-frame
ret, frame = cap.read()
if ret == True:
frame_new = frame
else:
end = time.time()
# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # turn video gray
# Display the resulting frame
cv2.namedWindow('frame', cv2.WINDOW_NORMAL)
cv2.imshow('frame', frame_new)
# wait for a keypress or the time needed to finish the frame duration
k = cv2.waitKey(frameref_ms-int(time.time()*1000)) & 0xFF
if k == 27: # wait for ESC key to exit
break
elif cv2.getWindowProperty("frame", 0) == -1:
break
This technique of having an absolute time to finish the display/read the next frame means that the frame rate will be exact, automatically compensating for multi-tasking other OS tasks as long as those other tasks don't take so much CPU that your code hardly runs, if you hit that problem I guess you'll have to increase the priority of Python which will slow the other programs down. I've used this method in timing sampling for temperature/vibration measurements and it works very well. See the same technique in one of my answers Inaccurate while loop timing in Python
If you were being really careful/pessimistic you'd also check that waitKey() isn't being given a negative delay in the case where the frame read+display took longer than the frame period.

Mouse.position from pynput not working [python2, opencv, mac, jupyter]

I'm new to this so it may just be a syntax problem, but can anyone figure out why line 77 mouse.position = (x,y) isn't moving my mouse? It should be mapped to a dot drawn by holding a green object up to my webcam.
Furthermore, when I introduce the while mouse.position!=(x,y): pass the camera freezes when a green object is introduced.
Code here (no errors shown when freezing):
import cv2
import numpy as np
from pynput.mouse import Button, Controller
import wx
mouse=Controller()
#get monitor size
app=wx.App(False)
(sx,sy)=wx.GetDisplaySize()
print sx, sy
#output window size
(camx,camy)=(320,240)
#set filter limits
lowerBound=np.array([33,80,40])
upperBound=np.array([102,255,255])
#initialise cam
cam= cv2.VideoCapture(0)
cam.set(3,camx)
cam.set(4,camy)
#mask parameters
kernelOpen=np.ones((5,5))
kernelClose=np.ones((20,20))
while True:
ret, img=cam.read()
img=cv2.resize(img,(340,220))
#convert BGR to HSV
imgHSV= cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
#create the Mask
mask=cv2.inRange(imgHSV,lowerBound,upperBound)
#morphology
maskOpen=cv2.morphologyEx(mask,cv2.MORPH_OPEN,kernelOpen)
maskClose=cv2.morphologyEx(maskOpen,cv2.MORPH_CLOSE,kernelClose)
maskFinal=maskClose
#Find contours
conts,h=cv2.findContours(maskFinal.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
#For two contours (open):
if(len(conts)==2):
x1,y1,w1,h1=cv2.boundingRect(conts[0])
x2,y2,w2,h2=cv2.boundingRect(conts[1])
cv2.rectangle(img,(x1,y1),(x1+w1,y1+h1),(255,0,0),2)
cv2.rectangle(img,(x2,y2),(x2+w2,y2+h2),(255,0,0),2)
#finding points in middle of rectangles
cx1=x1+w1/2
cy1=y1+h1/2
cx2=x2+w2/2
cy2=y2+h2/2
#find centre of line
cx=(cx1+cx2)/2
cy=(cy1+cy2)/2
#create line between centres of contours
cv2.line(img,(cx1,cy1),(cx2,cy2),(255,0,0),2)
#create dot in middle of line
cv2.circle(img, (cx,cy),2,(0,0,255),2)
#to check values
print (cx*sx/camx)
print (cy*sy/camy)
#move mouse to dot cx,cy (scaled for monitor)
mouse.position = (cx*sx/camx,cy*sy/camy)
while mouse.position != (cx*sx/camx,cy*sy/camy):
pass
#For one contour (closed):
elif (len(conts)==1):
x,y,w,h= cv2.boundingRect(conts[0])
#draw single rectangle around 'clicked' object
cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
#show when clicked with large circle
cx=x+w/2
cy=y+h/2
cv2.circle(img,(cx,cy),(w+h)/4,(0,0,255),2)
#set mouse to dot cx,cy
mouse.position = (cx*sx/camx,cy*sy/camy)
while mouse.position != (cx*sx/camx,cy*sy/camy):
pass
#cv2.imshow("maskClose",maskClose)
#cv2.imshow("mask",mask)
cv2.imshow("cam",img)
cv2.waitKey(5)
I appreciate any help!
My problem was that I needed to allow permission for terminal and jupyter to access mouse control (Mac Mojave 10.14.2).
The reason for the freeze is that it was stuck in the while loop - but with the mouse.position now working its all good!

Get Depth image in grayscale in ROS with imgmsg_to_cv2 [python]

I am using Kinect v1 and I want to get the depth image in greyscale mode from the channel "/camera/depth_registered/image" in ROS. As I found here, I can do it by using the function imgmsg_to_cv2. The default desired_encoding for my depth messages is "32FC1", which I keep. The problem is that when I use the cv2.imshow() function to show it, I get the image in binary... When I do the same for the RGB image everything is being shown just fine...
Any help appreciated!
So after all, I found a solution, which you can see here:
def Depthcallback(self,msg_depth): # TODO still too noisy!
try:
# The depth image is a single-channel float32 image
# the values is the distance in mm in z axis
cv_image = self.bridge.imgmsg_to_cv2(msg_depth, "32FC1")
# Convert the depth image to a Numpy array since most cv2 functions
# require Numpy arrays.
cv_image_array = np.array(cv_image, dtype = np.dtype('f8'))
# Normalize the depth image to fall between 0 (black) and 1 (white)
# http://docs.ros.org/electric/api/rosbag_video/html/bag__to__video_8cpp_source.html lines 95-125
cv_image_norm = cv2.normalize(cv_image_array, cv_image_array, 0, 1, cv2.NORM_MINMAX)
# Resize to the desired size
cv_image_resized = cv2.resize(cv_image_norm, self.desired_shape, interpolation = cv2.INTER_CUBIC)
self.depthimg = cv_image_resized
cv2.imshow("Image from my node", self.depthimg)
cv2.waitKey(1)
except CvBridgeError as e:
print(e)
However, the result is not that perfect as the one I get from the image_view node of ROS, but it is still pretty acceptable!

Rolling ball background subtraction algorithm for OpenCV

Is there an OpenCV (android) implementation of "rolling ball" background subtraction algorithm found in ImageJ: Process->Subtract Background?
OpenCV has a BackgroundSubtractorMOG class, but it is used for video streams not single, independent images.
This is an example of what this method does:
http://imgur.com/8SN2CFz
Here is a documentation of the process: http://imagejdocu.tudor.lu/doku.php?id=gui:process:subtract_background
There's no implementation in the OpenCV C libraries that I know of and the Android JNI wrappers are just that - wrappers around the main libraries.
Having said that the source code for the ImageJ implementation is available online here and so you should be able to incorporate this directly into your Android image processing pipeline.
There is some discussion about the relative merits of rolling ball vs. e.g. using a disk structuring element (which is available in OpenCV) here.
If you absolutely require Rolling Ball and OpenCV then unfortunately it's not available 'out of the box'.
There is a recent rolling-ball implementation in opencv that you can find here
https://pypi.org/project/opencv-rolling-ball/
In short
Install pip install opencv-rolling-ball
Example
import cv2
from cv2_rolling_ball import subtract_background_rolling_ball
img = cv2.imread(f'path/to/img.tif', 0)
img, background = subtract_background_rolling_ball(img, 30, light_background=True, use_paraboloid=False, do_presmooth=True)
Building on #Xenthor's answer this is what I came up with:
import numpy as np
import scipy.ndimage as ndi
from scipy.ndimage._ni_support import _normalize_sequence
def rolling_ball_filter(data, ball_radius, spacing=None, top=False, **kwargs):
"""Rolling ball filter implemented with morphology operations
This implenetation is very similar to that in ImageJ and uses a top hat transform
with a ball shaped structuring element
https://en.wikipedia.org/wiki/Top-hat_transform
Parameters
----------
data : ndarray
image data (assumed to be on a regular grid)
ball_radius : float
the radius of the ball to roll
spacing : int or sequence
the spacing of the image data
top : bool
whether to roll the ball on the top or bottom of the data
kwargs : key word arguments
these are passed to the ndimage morphological operations
Returns
-------
data_nb : ndarray
data with background subtracted
bg : ndarray
background that was subtracted from the data
"""
ndim = data.ndim
if spacing is None:
spacing = 1
spacing = _normalize_sequence(spacing, ndim)
radius = np.asarray(_normalize_sequence(ball_radius, ndim))
mesh = np.array(np.meshgrid(*[np.arange(-r, r + s, s) for r, s in zip(radius, spacing)], indexing="ij"))
structure = 2 * np.sqrt(1 - ((mesh / radius.reshape(-1, *((1,) * ndim)))**2).sum(0))
structure[~np.isfinite(structure)] = 0
if not top:
# ndi.white_tophat(data, structure=structure, output=background)
background = ndi.grey_erosion(data, structure=structure, **kwargs)
background = ndi.grey_dilation(background, structure=structure, **kwargs)
else:
# ndi.black_tophat(data, structure=structure, output=background)
background = ndi.grey_dilation(data, structure=structure, **kwargs)
background = ndi.grey_erosion(background, structure=structure, **kwargs)
return data - background, background
Edit: Before using the method in this post read the comments below and also consider the answers of #renat and #David Hoffman.
In case someone is still looking for rolling ball background correction in python. For me, the following worked out very well.
Load the image and process each channel separately.
Create a weighted ball structuring element
Use white tophat transform
Here is some code for a monochrome image:
import scipy.ndimage as scim
from scipy.misc import imsave
from skimage.morphology import ball
# Read image
im = scim.imread("path")[:, :, 0].astype(int)
# Create 3D ball with radius of 50 and a diameter of 2*50+1
s = ball(50)
# Take only the upper half of the ball
h = s.shape[1] // 2 + 1 # 50 + 1
# Flatten the 3D ball to a weighted 2D disc
s = s[:h, :, :].sum(axis=0)
# Rescale weights into 0-255
s = (255 * (s - s.min())) / (s.max() - s.min())
# Use im-opening(im,ball) (i.e. white tophat transform) (see original publication)
im_corr = scim.white_tophat(im, structure=s)
# Save corrected image
imsave('outfile', im_corr)
This gives you not the exact same result as the imagej implementation but the results are quite similar. In my case there were both, better and worse corrected regions. Moreover the overall color intensity was higher.
The original algorithm that ImageJ implements comes from a 1983 paper https://www.computer.org/csdl/magazine/co/1983/01/01654163/13rRUwwJWBB. I took a look at it and it is actually a grayscale morphological white top-hat with a ball-shaped grayscale structuring element (see https://en.wikipedia.org/wiki/Top-hat_transform). In the ImageJ implementation (available here https://imagej.nih.gov/ij/developer/source/ij/plugin/filter/BackgroundSubtracter.java.html), the image is downsampled depending on the structuring elements' radius, then upsampled to the original resolution and, by default, a smoothing operation using a 3x3 mean filter is applied before computing the background to subtract. This likely explains the differences observed with the method proposed by Xenthor.
If you are working on Android, you have several options: 1) using the ImageJ library, since it is in Java, you will however need to implement an OpenCV-ImageJ image bridge; 2) if you work in C++ using the Android NDK and since OpenCV does not implement grayscale morphology for non-flat structuring elements, you can use ITK (https://itk.org/) instead to perform the graycale white top-hat; 3) still using the NDK, there is an OpenCV-based C++ port of the algorithm available here: https://github.com/folterj/BioImageOperation/tree/master/BioImageOperation, however it is still a work in progress.
I realize it's not opencv, but there is an implementation in scikit-image (version ≥ 0.18).
from skimage import data, restoration
image = data.coins()
background = restoration.rolling_ball(image, radius=100)
result = image - background
A more detailed walkthrough is provided in the documentation

Resources