Showing web camera through web interface (Python, CherryPy, OpenCV) - python

I want to show OpenCV processed image with web interface (made with CherryPy). Code below works fine:
import cherrypy
import cv2
class Picture(object):
def __init__(self):
self.cam = cv2.VideoCapture(0)
#cherrypy.expose
def index(self):
_, image = self.cam.read()
_, data = cv2.imencode('.jpg', image)
cherrypy.response.headers['Content-Type'] = 'image/jpeg'
return data.tostring()
if __name__ == '__main__':
cherrypy.config.update({'server.socket_host': '127.0.0.1', 'server.socket_port': 80, })
cherrypy.quickstart(Picture())
However: I would like to embed the image in html so I can (for example) add another image or other data to the same page.
I have tried the following code:
import cherrypy
import cv2
class Picture(object):
def __init__(self):
self.cam = cv2.VideoCapture(0)
#cherrypy.expose
def index(self):
_, image = self.cam.read()
_, data = cv2.imencode('.jpeg', image)
return """ <html>
<head>
<title>CherryPy static imagl</title>
</head>
<html>
<body>
<img src=" """ + data + """:image/jpeg" />
</body>
</html>"""
if __name__ == '__main__':
cherrypy.config.update({'server.socket_host': '127.0.0.1', 'server.socket_port': 80, })
cherrypy.quickstart(Picture())
But this gives the following error:
<img src=" """ + data + """:image/jpeg" />
TypeError: cannot concatenate 'str' and 'numpy.ndarray' objects
Converting the numpy arry to a string using the following code also does not work (it gives no error but displays only characters):
<img src=" """ + data.tostring() + """:image/jpeg" />
Anyone who can give me some more insight? Thanks in advance!

The following code does the trick :)
import cherrypy
import cv2
import base64
class Picture(object):
def __init__(self):
self.cam = cv2.VideoCapture(0)
#cherrypy.expose
def index(self):
_, image = self.cam.read()
_, data = cv2.imencode('.jpg', image)
jpeg_base64 = base64.b64encode(data.tostring())
return """
<html>
<head>
<meta http-equiv="refresh" content="1" />
<title>Cherrypy webcam</title>
</head>
<html>
<body>
<img src='data:image/jpeg;base64,%s' />
<img src='data:image/jpeg;base64,%s' />
</body>
</html>
""" % (jpeg_base64, jpeg_base64)
if __name__ == '__main__':
cherrypy.config.update({'server.socket_host': '127.0.0.1', 'server.socket_port': 80, })
cherrypy.quickstart(Picture())
This code displays the same picture two times. and the:
<meta http-equiv="refresh" content="1" />
refreshes the code every second.

Related

How to return video frame and text to HTML page using FastAPI?

With the current code, I can only send the video frame to the webpage. How can I also send some text along with each frame and have it displayed.
FastAPI code
def generate_frames(cap,i):
while True:
success,frame = cap.read()
if not success:
break
else:
# Reshape image
im_arr = cv2.imencode('.jpg', frame)[1]
cv2.waitKey(50)
print(loa[i]) //text to be displayed along with image
i = i + 1
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + bytearray(im_arr) + b'\r\n')
#app.get('/video')
def video():
i = 0
cap = cv2.VideoCapture('C:\\Users\\ryanv_k78mbsh\\Desktop\\FINAL\\MovenetTest\\animation.gif')
return StreamingResponse(generate_frames(cap,i),media_type = 'multipart/x-mixed-replace; boundary=frame')
HTML code that receives and displays the video frame
<div style= "height:50px"></div>
<img src ="{{ url_for('video') }}" width="50%" />
</div>
You could use WebSockets instead, as described in this answer (Option 2), and send both the text and image bytes to the frontend. Example below:
app.py
from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
from fastapi.templating import Jinja2Templates
import uvicorn
import cv2
app = FastAPI()
camera = cv2.VideoCapture(0,cv2.CAP_DSHOW)
templates = Jinja2Templates(directory="templates")
#app.get('/')
def index(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
#app.websocket("/ws")
async def get_stream(websocket: WebSocket):
await websocket.accept()
try:
while True:
success, frame = camera.read()
if not success:
break
else:
ret, buffer = cv2.imencode('.jpg', frame)
await websocket.send_text("some text")
await websocket.send_bytes(buffer.tobytes())
except WebSocketDisconnect:
print("Client disconnected")
if __name__ == '__main__':
uvicorn.run(app, host='127.0.0.1', port=8000)
templates/index.html
<!DOCTYPE html>
<html>
<head>
<title>Live Streaming</title>
</head>
<body>
<img id="frame" src="">
<div id="textArea"></div>
<script>
let ws = new WebSocket("ws://localhost:8000/ws");
let image = document.getElementById("frame");
image.onload = function(){
URL.revokeObjectURL(this.src); // release the blob URL once the image is loaded
}
ws.onmessage = function(event) {
if (typeof event.data === 'string')
document.getElementById("textArea").innerHTML = event.data;
else
image.src = URL.createObjectURL(event.data);
};
</script>
</body>
</html>

Can't execute python script server-side inside php shell_exec

I got a strange situation and i can't figure out what's the problem.
I want to use ajax to make a request on the server (Raspberry Pi 3) that use php.
The php script handle the request, run a python script with shell_exec and return the result of the shell_exec to the client.
The python script execute a prediction of Tensorflow Lite model and print the result.
The problem is that my shell_exec() is not executed and the php return a response with status 200 and response = 0.
I thought it was some timeout on server because python script need 10 seconds to print a result, but changing python code to only a "sleep(180)" make the ajax request waiting for 3 minutes.
Change the python script back.
So, I think it's some about the python script.
Running
php server.php
on raspberry I got the output of the python script as I exepct.
So, maybe it's something about apache user who run php script (www-data, etc...). I changed permission of all files to 777 but the error still persist.
ajax.html
<!DOCTYPE html>
<html lang="it">
<head>
<title>Ajax Example</title>
<meta charset="utf-8">
<script>
function reply() {
$.ajax({
url: 'risposta.php',
type: 'POST',
contentType: false,
processData: false,
async: true,
timeout: 0,
success: function(response){
if(response != 0){
document.getElementById("result").innerHTML = response;
}else{
alert('Error');
}
},
});
}
</script>
</head>
<body>
<form id="formesempio">
<input type="text" id="nome">
<input type="button" value="Play" onclick="javascript:reply()">
</form>
<p>Result: <span id="result"></span></p>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</body>
</html>
server.php
<?php
$command = "python3 /var/www/html/DBR/predict.py --fn /var/www/html/img.jpg";
$out = shell_exec($command);
echo $out;
?>
predict.py
from tflite_runtime.interpreter import Interpreter
from PIL import Image
import numpy as np
import time
import argparse
import pandas as pd
from DBR_utils import print_human_readable_dog_deploy, get_dog_from_predictions
# Variabili globali
model_path = "DBR/DBR.tflite"
label_path = "DBR/IT_breeds.txt"
def set_input_tensor(interpreter, image):
tensor_index = interpreter.get_input_details()[0]['index']
input_tensor = interpreter.tensor(tensor_index)()[0]
input_tensor[:, :] = image
def classify_image(interpreter, image, top_k=1):
set_input_tensor(interpreter, image)
interpreter.invoke()
output_details = interpreter.get_output_details()[0]
output = interpreter.get_tensor(output_details['index'])
return output
# -- Parser --
parser = argparse.ArgumentParser(description='DBR')
parser.add_argument('--fn', type=str, required=True, help='filename')
args = parser.parse_args()
interpreter = Interpreter(model_path)
#print("Model Loaded Successfully.")
interpreter.allocate_tensors()
_, height, width, _ = interpreter.get_input_details()[0]['shape']
#print("Image Shape (", width, ",", height, ")")
# Load an image to be classified.
image = Image.open(args.fn).convert('RGB').resize((width, height))
# Classify the image.
time1 = time.time()
output = classify_image(interpreter, image)
time2 = time.time()
classification_time = np.round(time2-time1, 3)
#print("Classificaiton Time =", classification_time, "seconds.")
labels = []
with open(label_path, 'r') as f:
tmp = f.readlines()
for l in tmp:
labels.append(l.rstrip())
labels = pd.Series(labels)
dog = get_dog_from_predictions(np.squeeze(output), labels, args.fn)
res = print_human_readable_dog_deploy(dog)
print(res)

QWebEnginePage interactive with javascript not working?

I am not familiar with javascript and QWebEnginePage.
When i set self.content.setText('text') content, the QWebEngineView content not changed?
The python code
class Document(QObject):
textChanged = pyqtSignal(str)
def __init__(self):
super().__init__()
self.text = ''
def setText(self, text):
self.text = text
self.textChanged.emit(text)
print('emit')
class Demo(QWebEngineView):
def __init__(self):
super().__init__()
self.content = Document()
page = self.page()
channel = QWebChannel()
channel.registerObject('content', self.content)
page.setWebChannel(channel)
with open('index.html') as f:
self.setHtml(f.read())
self.content.setText('text')
app = QApplication([])
demo = Demo()
demo.resize(500, 400)
demo.show()
app.exec()
The index html:
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<head>
<script src="qwebchannel.js"></script>
</head>
<body>
<div id="placeholder">22</div>
<script>
'use strict';
var placeholder = document.getElementById('placeholder');
var updateText = function (text) {
placeholder.innerHTML = text;
console.log(text);
}
new QWebChannel(qt.webChannelTransport,
function (channel) {
var content = channel.objects.content;
updateText(content.text);
content.textChanged.connect(updateText);
}
);
</script>
</body>
</html>
You have the following errors:
channel is a local variable that will be removed as soon as the "Demo" constructor is finished, and that is the intermediary between Python and Javascript communication. The solution is to extend the life cycle by passing it a parent (Qt style) or make it an attribute of the class.
In the .html you are trying to include qwebchannel.js but in general you should use <script src="qrc:///qtwebchannel/qwebchannel.js"></script>.
If you export a QObject to javascript then only the Q-Properties, QSlot and QSignals will be exported since QWebChannel uses the QMetaObject instropection, but "text" is neither of them so it will be undefined in javascript. The solution is to expose it as a pyqtProperty.
import os
from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject, QUrl
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWebChannel import QWebChannel
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class Document(QObject):
textChanged = pyqtSignal(str)
def __init__(self):
super().__init__()
self._text = ""
def text(self):
return self._text
def setText(self, text):
self._text = text
self.textChanged.emit(text)
print("emit")
text = pyqtProperty(str, fget=text, fset=setText, notify=textChanged)
class Demo(QWebEngineView):
def __init__(self):
super().__init__()
self.content = Document()
channel = QWebChannel(self)
channel.registerObject("content", self.content)
self.page().setWebChannel(channel)
filename = os.path.join(CURRENT_DIR, "index.html")
self.load(QUrl.fromLocalFile(filename))
self.content.setText("text")
def main():
app = QApplication([])
demo = Demo()
demo.resize(500, 400)
demo.show()
app.exec()
if __name__ == "__main__":
main()
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<head>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
</head>
<body>
<div id="placeholder">22</div>
<script>
'use strict';
var placeholder = document.getElementById('placeholder');
var updateText = function (text) {
placeholder.innerHTML = text;
console.log(text);
}
new QWebChannel(qt.webChannelTransport,
function (channel) {
var content = channel.objects.content;
updateText(content.text);
content.textChanged.connect(updateText);
}
);
</script>
</body>
</html>

Using Pil to store image in a list and retrieve those image in a browser

This question is not a duplicate because I have stored the image address in a dataset and i need to retrieve those images that are present in my folder using a machine learning model, store it in a list and display those images in a browser I am creating a movie recommendation system in flask. I need to display poster image along with the name, but the browser doesn't seem to view the image. Here is my code:
app.py:
from flask import Flask,render_template
from flask import request
from sample import get_title_from_index
from sample import get_poster_from_index
from sample import similar_movies
from flask import jsonify
from PIL import Image
import array as arr
app = Flask(__name__)
#app.route('/', methods=['GET','POST'])
def predict():
if 'movie_input' in request.form:
movies = similar_movies(request.form['movie_input'])
i=0
print("Top 5 similar movies are:")
e1= []
p1=[]
for element in movies:
e1.insert(0,get_title_from_index(element[0]))
image= Image.open(get_poster_from_index(element[0]))
p1.insert(0,image)
i=i+1
if i>5:
break
else:
e1 = "No movies selected. Please, select something!"
p1 = ""
return render_template('predictor.html', movie=e1,poster=p1)
if __name__ == '__main__':
app.run(debug=True)
predictor.html:
<!doctype html>
<html>
<head>
</head>
<body>
<form method="POST">
<input type="text" name="movie_input" maxlength="500" >
<input type="submit" value="Submit" method="get" >
</form>
<p>
{{movie[0]}}
{{poster[0]}}
</p>
</body>
</html>
sample.py
import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics.pairwise import cosine_similarity
df = pd.read_csv("movies.csv")
def get_title_from_index(index):
return df[df.index == index]["title"].values[0]
def get_index_from_title(title):
return df[df.title == title]["index"].values[0]
def get_poster_from_index(index):
return df[df.index == index]["poster"].values[0]
def combine_features(row):
return row["keywords"]+"" +row["cast"]+""+row["genres"]+""+row["director"]
def similar_movies(movie_input):
features = ['keywords','cast','genres','director']
df["poster"] = df["poster"].fillna("")
for feature in features:
df[feature] = df[feature].fillna("") #filling all NaNs with blank string
df["combined_features"] = df.apply(combine_features,axis=1)
cv = CountVectorizer() #creating new CountVectorizer() object
count_matrix = cv.fit_transform(df["combined_features"])
cosine_sim = cosine_similarity(count_matrix)
movie_index = get_index_from_title(movie_input)
similar_movies = list(enumerate(cosine_sim[movie_index]))
sorted_similar_movies = sorted(similar_movies,key=lambda x:x[1],reverse=True)[1:]
return sorted_similar_movies
The browser view:
browser view
The browser view shows that the image is present but doesn't seems to view it.
dataset view
movie dataset view
folder view
flask folder
Please review my code and let me know the necessary changes.
You can't just output an image file into html. You have you have encode it an put it in an html IMG tag.
If you encode the image data into base64, you can do something like:
{{movie[0]}}
<img src="data:image/png;base64, {{poster[0]}}" />
For encoding the image, see Encoding an image file with base64

Image won't load on python-based webserver

I've built a simple web server that gets a request and send a response. So when the server gets an invalid request, like "localhost/not-a-page", the server will send a response with the content of the HTML file "404.html" the webpage should display an image. So far, so good.
But when the 404 page loads up, the page can't find the image. The HTML part is correct and works offline. I've tried to move the image to serval locations, relative to the Python script, relative to the HTML. But it just can't find it. Hi I'm trying to make the server as low-level as I can, I want to learn how servers work. So I'm not using any server-related libraries. I'm using only the socket library of Python.
I'll appreciate any help to resolve this problem without using other libraries,
EDIT
Here is the relevant Python part :
import socket
import threading
import os
default_error_page = """\
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Error response</title>
</head>
<body>
<center>
<h1>Response</h1>
<p>Error code: %(code)d</p>
<p>Message: %(status)s.</p>
</center>
</body>
</html>
"""
default_header_status = "HTTP/1.1 %(code)d %(status)s\r\n"
default_header_content_type = "Content-Type: text/html; charset=utf-8\r\n\r\n"
buffer_size = 1024
def get_page(code):
    page = default_error_page
    if code == 200:
        pass
    else:
        file = open(os.path.dirname(__file__) + "/www/not-found.html", 'r')
        page = file.read()
    return page
class BaseServer:
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_name = ""
    host_name = ""
    host_port = 8000 # default port
    is_shutdown = False
    def __init__(self, name):
        self.server_name = name
    def start_server(self):
        thread = threading.Thread(target=self.run_server(), name=self.server_name)
        thread.start()
    def run_server(self):
        self.server_socket.bind((self.host_name, self.host_port)) # bind to host
        self.server_socket.listen()
        while not self.is_shutdown:
            conn, addr = self.server_socket.accept()
            self.handle_request(conn, addr)
    def handle_request(self, connection, address):
        print(str(address[0]) + " Connected! (port " + str(address[1]) + ")")
        result = self.parse_request(connection.recv(buffer_size))
        if result == 0:
            page = self.parse_response(200)
        else:
            page = self.parse_response(404)
        connection.sendall(bytes(page))
    def parse_request(self, data):
        if len(data) == 0:
            return
        strings = str(bytes(data).decode('utf-8')).split('\r\n')
        command, path, version = strings[0].split()
        print("command - " + command)
        print("path - " + path)
        print("version - " + version)
        status = 1
        if path == "/":
            status = 0
        return status
    def parse_response(self, code):
        status = "ERROR"
        if code == 200:
            status = "OK"
        elif code == 404:
            status = "NOT FOUND"
        base_header = (default_header_status % {'code': code, 'status': status})
        base_content_type = default_header_content_type
        # page = (default_error_page % {'code': code, 'status': status})
        page = str(get_page(code))
        return_string = str(base_header + base_content_type + page).encode('utf-8')
        print(return_string)
        return return_string
def main():
    server = BaseServer("Home Server")
    server.start_server()
if __name__ == "__main__":
    main()
And this is the HTML:
<html>
<head>
<link rel="stylesheet" type="text/css" href="/style/main.css"/>
<style>
*{
padding:0;
margin:0;
}
body{
background-color:#ffe6b3;
}
h1{
margin-top:30px;
background-color:#ffcc66;
font-size:3em;
display:inline-block;
color:#3a0000;
}
p{
margin-top:80px;
font-size:2em;
color:#3a0000;
}
#img404{
background-image:url(../images/404.gif);
width:100%;
height:50%;
background-repeat:no-repeat;
background-position:center 20%;
}
</style>
</head>
<body>
<center>
<div class=top>
<h1>ERROR 404</h1>
</div>
<p>
Sorry, we could not find the page :(
</p>
<div id="img404">
</div>
</center>
</body>
</html>
Sorry if it's not very readable, but I'm on the phone.
Dima.
Don't use relative paths for image like ../images/img.gif. Rather use full url or url relative to the root.
http://localhost/images/img.gif - full url
/images/img.gif - path relative to root url
Figured out my problem.
After I saw the logs, I realized that the browser sends another request, for the image.
And silly me, my code is:
if path ="/":
status = 0
else:
status = 1
So for every request which is not root("/") the server will return 404.
Oops

Categories

Resources