Python/OpenCV : Laser curve analysis from camera video flow - python

Good morning,
I'm currently trying to study real-time liquid surface deformations by sending a laser sheet on the surface and gathering its reflection. What I obtain is typically a bright curve at each timestep, and I wish to analyze its coordinates.
I thus brought myself to write a Python script, which is displayed right below (The analysis part is retaken from laser curved line detection using opencv and python, as it represents nearly exactly what I'm trying to do, except that I'm working with a video flow) :
import cv2
from PIL import Image
import cv2.cv as cv
import numpy as np
import time
myfile = open("hauteur.txt","w")
#Import camera flow
class Target:
def __init__(self):
self.capture = cv.CaptureFromCAM(0)
cv.namedWindow("Target", 1)
cv.SetCaptureProperty(self.capture,cv.CV_CAP_PROP_FRAME_WIDTH, 150)
cv.SetCaptureProperty(self.capture,cv.CV_CAP_PROP_FRAME_HEIGHT, 980)
cv.SetCaptureProperty(self.capture,cv.CV_CAP_PROP_FPS, 60 )
def run(self):
frame = cv.QueryFrame(self.capture)
frame_size = cv.GetSize(frame)
color_image_cv = cv.CreateImage(cv.GetSize(frame), 8, 3)
color_image = np.array(color_image_cv)
grey_image = cv.CreateImage(cv.GetSize(frame), cv.IPL_DEPTH_8U, 1)
first = True
t = time.clock()
# Frame analysis
while True:
ret, bw = cv2.threshold(color_image, 0, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
curves = np.zeros((img.shape[0], img.shape[1], 3), np.uint8)
for i in range(len(contours)):
for col in range(draw.shape[1]):
M = cv2.moments(draw[:, col])
if M['m00'] != 0:
x = col
y = int (M['m01']/M['m00'])
curves[y, x, :] = (0, 0, 255)
res = {'X' : x, 'Y' : y, 't' : t}
print res
myfile.write('{X}\t{Y}\t{t}'.format(**res))
myfile.write("\n")
cv2.ShowImage("Target", color_image)
# Listen for ESC key
c = cv2.WaitKey(7) % 0x100
if c == 27:
break
if __name__=="__main__":
t = Target()
t.run()
However, the use of cv and cv2 functions within the same code seems to bring a nice mess and I get the error
src data type = 17 is not supported
from line
ret, bw = cv2.threshold(color_image, 0, 255, cv2.THRESH_BINARY)
I understand this arises from the way cv and cv2 functions create and store images, but any conversion process I try doesn't seem to work, and I didn't find equivalent cv2 functions to insert in my video flow importing part (but, as you may understand, I'm clearly not a programming pro and I may have skipped what I'd need in the documentation). Is there then a way to conciliate these cv and cv2 functions, or get a equivalent camera flow with cv2 functions ?
Bonus question : How fast can an script like this run (considering that I'd eventually need this to run at 300-400 fps, I'm not even sure this is actually feasible) ?
Thanks for your attention

ok, cv2 video code:
def __init__(self):
self.capture = cv2.VideoCapture(0)
cv2.namedWindow("Target", 1)
self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, 150)
self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 980)
self.capture.set(cv2.CAP_PROP_FPS, 60 )
def run(self):
ok, frame = self.capture.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY);
...
Bonus question : ofc, it can only run as fast, as the capture delivers. 300fps seems absurd, 30fps, more likely.

Related

Increase smoothness of video

I am trying to create a python code that scans the barcode and retrieves the output. A library called pyzbar had already been created for the same purpose. Using that and OpenCV, I had created a code (attached below), for scanning and drawing bounding boxes on the barcode/QR code. The problem I'm facing is that when I input a pre-recorded video above 100 MB as input the output video that is displayed/saved is very slow, but with live stream, there is no such issue. I tried several methods to reduce the fps like PROP_FPS, but nothing worked. I even tried multithreading method and it seems to not have any effect. The code that I was referring to is attached below. Please help me out on the same.
import cv2
import numpy as np
from pyzbar.pyzbar import decode
cv2.namedWindow("Result", cv2.WINDOW_NORMAL)
cap = cv2.VideoCapture('2016_0806_040333_0081.mp4')
cap.set(3, 1280)
cap.set(4, 720)
#frame_width = int(cap.get(3))
#frame_height = int(cap.get(4))
#cap.set(cv2.CAP_PROP_FPS, 0.1)
#size = (frame_width, frame_height)
#result = cv2.VideoWriter('processed_video.avi', cv2.VideoWriter_fourcc(*'MJPG'),0.1, size)
while(True):
ret, frame = cap.read()
for barcode in decode(frame):
myData = barcode.data.decode('utf-8')
pts = np.array([barcode.polygon],np.int32)
pts = pts.reshape((-1,1,2))
cv2.polylines(frame, [pts], True, (255,0,255),5)
pts2 = barcode.rect
akash = []
akash.append(myData)
cv2.putText(frame, myData, (pts2[0], pts2[1]), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 0, 255), 2)
"""
f=open('output.csv','a+')
for ele in akash:
f.write(ele+'\n')
"""
result.write(frame)
cv2.imshow('Result',frame)
cv2.waitKey(1)
#video.release()
#result.release()
#cv2.destroyAllWindows()
print("The video was successfully saved")
You could change the frame rate of your project, but this won't add missing frames. Even if your editor can attempt to extrapolate the needed frames, it can result in glitches.
Best approach if you have the option is to shoot the video at a higher frame rate to begin with.

Opencv Python3 when attempting to switch from saved images to live video feed the program hangs

Hello there people of the internet,
The code in question is using python 3.8.5 and opencv 4 (I do not know how to check the exact version but I know its opencv 4). My team and I are attempting to take a live video feed from a usb webcam and determine the distance between the camera and the object in the video feed. We had some success in reading the distance with image stills taken from the same camera and read via the imutils library. But now we want to attempt to calculate that data live.
Our code is below.
from imutils import paths
import numpy as np
import imutils
import cv2
import time
import os
def find_marker(image):
#conver the image into grayscales, blurs it then detects edges
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 35, 125)
#find the contours in the edged image and keep the largest one;
#w'll assume that this our piece of paper in the image
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = max(cnts, key = cv2.contourArea)
#compute the bounding box of the paper region and return it
return cv2.minAreaRect(c)
def distance_to_camera(knownWidth, focalLength, perWidth):
#compute and return the distance from the marker to the camera
return (knownWidth * focalLength) / perWidth
#intialize the known distances from the camera to the object
KNOWN_DISTANCE = 22
#initialize the known object width, which in this case the piece of paper is 12 inches
KNOWN_WIDTH = 11
#load the first image that contains an object that is known to be 2 feet
#from our camera, the find the paper marker in the image and
#initialize the focal length
rootimage = cv2.imread("/Volumes/404/final_rov_code/Python/images/2ft.jpg")
marker1 = find_marker(rootimage)
marker2 = marker1[0][1] - marker1[1][1]
focalLength = (marker2 * KNOWN_DISTANCE) / KNOWN_WIDTH
print(marker1)
print(marker2)
image = cv2.VideoCapture(0)
#Loop over the image
while True:
#load the image, find the marker in the image then compute the
#distance to the marker from the camera
frame, ret = image.read()
marker = find_marker(ret)
inches = distance_to_camera(KNOWN_WIDTH, focalLength, marker[1][0])
print(inches)
#draw a bounding box around the image and display it
box = cv2.cv.BoxPoints(marker) if imutils.is_cv2() else cv2.boxPoints(marker)
box = np.int0(box)
cv2.drawContours(frame, [box], -1, (0, 255, 0), 2)
cv2.putText(ret, "%.2fin" % inches,
(ret.shape[1] - 200, ret.shape[0] - 20), cv2.FONT_HERSHEY_SIMPLEX,
2.0, (0, 255, 0), 3)
cv2.imshow("image", ret)
# if cv2.waitKey(33) == ord('q'):
# os.system('pause')
I understand that it should be as minimalistic as possible but since we have no idea what could be causing the program to hang upon reading the first frame of the video feed. Could it be the fact that the processing is taking too many resources from the single thread? (We're all newbies to the advanced sides of opencv and python 3)
There is no other errors that we are aware of at the moment so no leads in the terminal of where it could be coming from.
Thank you in advance.
Your problem is likely a result of not including the waitkey() statement at the end of your while loop. It takes time for openCV to load the image, so if the program doesn't pause for long enough for the image to be drawn, the display just doesn't update. Check out this other StackOverflow question for more details.
In addition, you have your ret and frame variables mixed up. ret should be the first one and frame should be the second. Right now, the drawContours() method isn't going to do anything because you're passing it a boolean instead of an image.
Making those changes fixed this for me using Python 3.9 and OpenCV 4.5.

Drawing a simple image, displaying it, and closing it

I am trying to do some simple drawings. I wanted to use opencv (cv2) because on a second project I have to display a small animation (rectangle, size depending on a variable; updated every X seconds). However, I do not have experience with image processing libraries and opencv.
I am running into a lot of problems, one of which is that I do not know how to display/close images. The image I am creating is a simple fixation cross, black; on a light gray background:
import numpy as np
import cv2
screen_width = 1024
screen_height = 768
img = np.zeros((screen_height, screen_width, 3), np.uint8) # Black image
img = img + 210 # light gray
screen_center = (screen_width//2, screen_height//2)
rect_width = int(0.2*screen_width)
rect_height = int(0.02*screen_height)
xP1 = screen_center[0] - rect_width//2
yP1 = screen_center[1] + rect_height//2
xP2 = screen_center[0] + rect_width//2
yP2 = screen_center[1] - rect_height//2
cv2.rectangle(img, (xP1, yP1), (xP2, yP2), (0, 0, 0), -1)
xP1 = screen_center[0] - rect_height//2
yP1 = screen_center[1] + rect_width//2
xP2 = screen_center[0] + rect_height//2
yP2 = screen_center[1] - rect_width//2
cv2.rectangle(img, (xP1, yP1), (xP2, yP2), (0, 0, 0), -1)
N.B: If there is a better way to create it, I am also interested :)
My goal is for this first project to do have the following code structure:
img = load_saved_img() # The created fixation cross
display_image()
add_text_to_image('texte to add')
# do stuff
# for several minutes
while something:
do_this()
remove_text_from_image() # Alternatively, go back to the initial image/change the image
# do stuff
# for several minutes
while something:
do_this()
close_image()
I know I can add text with cv2.putText() and that I can this way create a second image with the text. What I do not know is how can I manage the displaying of the different images; especially in a light-weight fashion while "doing stuff" on the background. Most people seems to use cv2.waitKey() which is not suited since I do not want to have any user input and since it seems to be something similar to a time.sleep() during which the program is basically paused.
Any tips welcome, even on other libraries and implementation :)
As proposed by #Miki, the combination of .imshow() and .waitKey(1) is working.
cv2.imshow(window, img)
cv2.waitKey(1)
However, those can not be used with time.sleep() to pause the program. Sometimes, the display will not be updated. For instance, on a 3 second countdown:
import time
import cv2
window = 'Name of the window'
def countdown(window, images):
"""
images = [image3, image2, image1]
"""
for img in images:
cv2.imshow(window, img)
cv2.waitKey(1)
time.sleep(1)
Sometimes one of the displays will be skipped. Instead, changing the parameter of cv2.waitKey() to 1000 (timer needed) and removing the use of the time module works best, if no keyboard input is expected during this time.

Cut and save an object recognized by color

So i would like to make a program which can detect an object by color, position and sharpness.
Now I am there that I could detect the object by color and draw its contour and bounding box.
My problem is that i really dont know how to cut out the object from the picture and save it as picture file when the program recognise its contour or bounding box.
here's a picture of what my camera is seeing
input
output
I would like to cut out what is inside of the green colored boundig box as many times as fps in the video and as long as you can see it in the video. So if the video is 30 fps and the object is visible for 10 seconds it needs to take 300 pictures.
Here is the code:
i know it looks bad, im just trying to figure out what to use to make it work
import cv2 as cv
import numpy as np
import os
import uuid
cap = cv.VideoCapture(1)
font = cv.FONT_HERSHEY_COMPLEX
path = os.getcwd()
print(path)
def createFolder(directory):
try:
if not os.path.exists(directory):
os.makedirs(directory)
except OSError:
print('Error: Creating directory. ' + directory)
createFolder("./data")
# folderName = '%s' % (str(uuid.uuid4()))
while cap.isOpened():
_, frame = cap.read()
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
# blue is the chosen one for now
lower_color = np.array([82, 33, 39])
upper_color = np.array([135, 206, 194])
mask = cv.inRange(hsv, lower_color, upper_color)
kernel = np.ones((5, 5), np.uint8)
mask = cv.erode(mask, kernel)
contours, hierarchy = cv.findContours(mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# find contour
for contour in contours:
area = cv.contourArea(contour)
x, y, h, w = cv.boundingRect(contour)
if area > 100:
# bounding box
# cv.rectangle(frame, (x - 40, y - 30), (x + h * 3, y + w * 3), (0, 255, 0), 1)
# cutting and saving
ext_left = tuple(contour[contour[:, :, 0].argmin()][0] - 20)
ext_right = tuple(contour[contour[:, :, 0].argmax()][0] + 20)
ext_top = tuple(contour[contour[:, :, 1].argmin()][0] - 20)
ext_bot = tuple(contour[contour[:, :, 1].argmax()][0] + 20)
outfile = '%s.jpg' % (str(uuid.uuid4()))
cropped_image = frame[ext_top[1]:ext_bot[1], ext_left[0]:ext_right[0]]
# write images to a specified folder
cv.imwrite(os.path.join(path, "/data/", outfile), cropped_image)
# outputs
cv.imshow("Frame", frame)
cv.imshow("Mask", mask)
key = cv.waitKey(1)
if key == 27:
break
cap.release()
cv.destroyAllWindows()
Focusing on the question and ignoring the code style, I can say you are close to achieving your goal :)
For cropping the object, you can use the Mat copyTo method. Here is the official OpenCV documentation and here is an example from the OpenCV forums.
Now, for creating the mask from the contours, you can use the same drawCountours method you already use, but provide a negative value for the thickness parameters (for example, thickness=CV_FILLED). You can see a code snippet in this stackoverflow post and check details in the official documentation.
For saving the image to disk you can use imwrite.
So, in a nutshell, draw filled contours to a mask and use that mask to copy only the object pixels from the video frame to another mat that you can save the disk.
Instead of posting code, I will share this very similar question with an accepted answer that may have the code snippet you are looking for.

How to detect multiple colors other than a particular color in image processing with Python using openCV?

I was trying to detect multiple colors simultaneously in an video, So I made a code including trackbar to adjust hsv values. Here is the code:
import numpy as np
import cv2
def nothing(x):
pass
cap = cv2.VideoCapture(0)
cv2.namedWindow('image')
cv2.createTrackbar('lh','image',0,180,nothing)
cv2.createTrackbar('ls','image',0,255,nothing)
cv2.createTrackbar('lv','image',0,255,nothing)
cv2.createTrackbar('hh','image',0,180,nothing)
cv2.createTrackbar('hs','image',0,255,nothing)
cv2.createTrackbar('hv','image',0,255,nothing)
while(True):
ret, img = cap.read()
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
lh = cv2.getTrackbarPos('lh','image')
ls = cv2.getTrackbarPos('ls','image')
lv = cv2.getTrackbarPos('lv','image')
hh = cv2.getTrackbarPos('hh','image')
hs = cv2.getTrackbarPos('hs','image')
hv = cv2.getTrackbarPos('lv','image')
lr = np.array([lh,ls,lv])
hr = np.array([hh,hs,hv])
mask = cv2.inRange(hsv,lr,hr)
res = cv2.bitwise_and(img,img,mask = mask)
cv2.imshow('image',res)
if(cv2.waitKey(1)&0xFF==ord(' ')):
break
cap.release()
cv2.destroyAllWindows()
The problem I encountered here is that when I run this program all I can see is a black screen. Even though I moved the trackbars, it was still showing black screen. But when I tried running this program removing the trackbars for higher range, by specifying the range in the code itself for a particular color it was working.
For example I changed the code :
hr = np.array([120,255,255])
mask = cv2.inRange(hsv,lr,hr)
How can I make this code work for all colors?

Categories

Resources