Q:

Image streaming with OpenCV and flask - Why imencode is needed?

I have a flask web application which reads one image and display it in my web browser
app.py
from flask import Response
from flask import Flask
from flask import render_template
import cv2

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html")

def GetImage():
    global img
    while True:
        yield(b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + bytearray(img) + b'\r\n')

@app.route("/stream")
def stream():
    return Response(GetImage(), mimetype = "multipart/x-mixed-replace; boundary=frame")

if(__name__ == "__main__"):
    img = cv2.imread("Cat.jpg", 0)
    app.run(debug = True, threaded = True, use_reloader = False)

index.html
<html>
    <body>
        <img src="{{ url_for('stream') }}">
    </body>
</html>

This example doesn´t work, because the image isn´t displayed in the browser.
But when I change GetImage in the following way the image will be displayed in the browser:
def GetImage():
    global img
    (flag, encodedImage) = cv2.imencode(".jpg", img)
    while True:
        yield(b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + bytearray(encodedImage) + b'\r\n')

So why do I need this imencode? The image is stored as jpg on my harddisk so why do I have to encode it again as JPG?

A:

If you have JPEG file then you can use standard open() and read() to read it as raw bytes data without decompressing it to array with all pixels - so later you don't have to compress it back to JPEG data using imencode()
 img = open("Cat.jpg", "rb").read()

and then you can display it 
 b'Content-Type: image/jpeg\r\n\r\n'+ img + b'\r\n'

I read it directly in bytes mode - open(..., 'rb') - so I don't have to use bytearray() to convert string to bytes. Besides, reading in text mode it could convert some chars (like "new line") and create incorrect data.

But to send single file you can use send_file()
@app.route("/image")
def image():
    return send_file('Cat.jpg')

Working example. 
Opening http://127.0.0.1:5000/stream in Chrome it shows image. 
My Firefox had problem to display image - it was reading data all time - till I added time.sleep()
I also added version which adds header Content-Length:
from flask import Flask, Response, send_file
import time

app = Flask(__name__)

@app.route("/")
def index():
    return "Hello World"

def get_image():
    while True:
        yield(b'--frame\r\n'
              b'Content-Type: image/jpeg\r\n\r\n'+ img + b'\r\n')
        time.sleep(0.01) # my Firefox needs some time to display image / Chrome displays image without it

def get_image_with_size():
    length = str(len(img)).encode() # convert to bytes
    while True:
        yield(b'--frame\r\n'
              b'Content-Type: image/jpeg\r\n'
              b'Content-Length: ' + length + b'\r\n'
              b'\r\n'+ img + b'\r\n')
        time.sleep(0.01) # my Firefox needs some time to display image / Chrome displays image without it

@app.route("/stream")
def stream():
    return Response(get_image(), mimetype="multipart/x-mixed-replace; boundary=frame")

@app.route("/image")
def image():
    return send_file('Cat.jpg')

if(__name__ == "__main__"):
    img = open('Cat.jpg', 'rb').read()
    app.run()

