Downloading the first frame of a twitch.tv stream - python

Using this api I've managed to download stream data, but I can't figure out how to parse it. I've looked at the RMTP format, but it doesn't seem to match.
from livestreamer import Livestreamer
livestreamer = Livestreamer()
# set to a stream that is actually online
plugin = livestreamer.resolve_url("http://twitch.tv/froggen")
streams = plugin.get_streams()
stream = streams['mobile_High']
fd = stream.open()
data = fd.read()
I've uploaded an example of the data here.
Ideally I wouldn't have to parse it as video, I only need the first keyframe as an image. Any help would be greatly appreciated!
Update: Ok, I got OpenCV working, it works for grabbing the first frame of a random video file I had. However, it produced a nonsense image when I used the same code on file with stream data.

Alright, I figured it out. Made sure to write as binary data, and OpenCV is able to decode the first video frame. The resulting image had R and B channels switched, but that was easily corrected. Downloading about 300 kB seems to be enough to be sure that the full image is there.
import time, Image
import cv2
from livestreamer import Livestreamer
# change to a stream that is actually online
livestreamer = Livestreamer()
plugin = livestreamer.resolve_url("http://twitch.tv/flosd")
streams = plugin.get_streams()
stream = streams['mobile_High']
# download enough data to make sure the first frame is there
fd = stream.open()
data = ''
while len(data) < 3e5:
data += fd.read()
time.sleep(0.1)
fd.close()
fname = 'stream.bin'
open(fname, 'wb').write(data)
capture = cv2.VideoCapture(fname)
imgdata = capture.read()[1]
imgdata = imgdata[...,::-1] # BGR -> RGB
img = Image.fromarray(imgdata)
img.save('frame.png')
# img.show()

Related

PIL - saving file in memory?

I just want to open the image files in a folder, and convert them to jpeg if they are not already jpeg. Only thing is I need to save the file in memory, not to file. The reason is, in fact I'm reading the images from tfrecod file (tensorflow data file format), extract the image from it, check the file format, if not jpeg, convert to jpeg and then write back to tfrecord file after decoding properly. Because tensorflow object detection api doesn't accept any image format than jpeg unfortunately. Anyways, that's just the explanation why I need it.
To be able to do that, I need to keep the file in memory. So here is my code:
for counter, filename_with_path in enumerate(filenames):
e = next(iter(tf.data.TFRecordDataset([filename_with_path])))
example = tf.train.Example()
example.ParseFromString(e.numpy())
parsed = example.features.feature
image_raw = parsed['image/encoded'].bytes_list.value[0]
# After this point is important
stream = BytesIO(image_raw)
image = Image.open(stream) # Image is pillow image
stream.close()
if image.format != 'JPEG':
tempFile = BytesIO()
image.convert('RGB')
image.save(tempFile, format="JPEG")
newStream = BytesIO(tempFile)
img = Image.open(newStream)
newStream.close()
print(filename, image.format)
print(filename, img.format)
When I run this, I get ValueError: I/O operation on closed file. on the line
image.save(tempFile, format="JPEG")
Any idea why this gives error? I saw this as suggested way to write in memory file: How to write PNG image to string with the PIL?
The error is not about tempFile but about stream. You should not do stream.close() until you are done with image. This is a lazy API, so it can handle large images more efficiently.
for counter, filename_with_path in enumerate(filenames):
...
stream = BytesIO(image_raw)
image = Image.open(stream) # Image is pillow image
# remove this line:
# stream.close()
if image.format != 'JPEG':
tempFile = BytesIO()
image.convert('RGB')
image.save(tempFile, format="JPEG")
# this wants bytes, not another BytesIO object, so read it
newStream = BytesIO(tempFile.read())
img = Image.open(newStream)
# same thing, don't close until you are done with img
# newStream.close()
print(filename, image.format)
print(filename, img.format)
From the Pillow's Image.open docs:
This is a lazy operation; this function identifies the file, but the file remains open and the actual image data is not read from the file until you try to process the data (or call the load() method).

How to create high res JPEG with Wand from binary string

I'm trying to convert some PDFs to high res jpegs using imagemagick . I'm working on win 10, 64 with python 3.62 - 64 bit and wand 0.4.4. At the command line I have :
$ /e/ImageMagick-6.9.9-Q16-HDRI/convert.exe -density 400 myfile.pdf -scale 2000x1000 test3.jpg.
which is working well for me.
In python:
from wand.image import Image
file_path = os.path.dirname(os.path.abspath(__file__))+os.sep+"myfile.pdf"
with Image(filename=file_path, resolution=400) as image:
image.save()
image_jpeg = image.convert('jpeg')
Which is giving me low res JPEGs . How do I translate this into my wand code to do the same thing?
edit:
I realized that the problem is that the input pdf has to be read into the Image object as a binary string, so based on http://docs.wand-py.org/en/0.4.4/guide/read.html#read-blob I tried:
with open(file_path,'rb') as f:
image_binary = f.read()
f.close()
with Image(blob=image_binary,resolution=400) as img:
img.transform('2000x1000', '100%')
img.make_blob('jpeg')
img.save(filename='out.jpg')
This reads the file in ok, but the output is split into 10 files. Why? I need to get this into 1 high res jpeg.
EDIT:
I need to send the jpeg to an OCR api, so I was wondering if I could write the output to a file like object. Looking at https://www.imagemagick.org/api/magick-image.php#MagickWriteImageFile, I tried :
emptyFile = Image(width=1500, height=2000)
with Image(filename=file_path, resolution=400) as image:
library.MagickResetIterator(image.wand)
# Call C-API Append method.
resource_pointer = library.MagickAppendImages(image.wand,
True)
library.MagickWriteImagesFile(resource_pointer,emptyFile)
This gives:
File "E:/ENVS/r3/pdfminer.six/ocr_space.py", line 113, in <module>
test_file = ocr_stream(filename='test4.jpg')
File "E:/ENVS/r3/pdfminer.six/ocr_space.py", line 96, in ocr_stream
library.MagickWriteImagesFile(resource_pointer,emptyFile)
ctypes.ArgumentError: argument 2: <class 'TypeError'>: wrong type
How can I get this working?
Why? I need to get this into 1 high res jpeg.
The PDF contains pages that ImageMagick considers individual images in a "stack". The wand library provides a wand.image.Image.sequance to work with each page.
However, to append all images into a single JPEG. You can either iterate over each page & stitch them together, or call C-API's method MagickAppendImages.
from wand.image import Image
from wand.api import library
import ctypes
# Map C-API not provided by wand library.
library.MagickAppendImages.argtypes = [ctypes.c_void_p, ctypes.c_int]
library.MagickAppendImages.restype = ctypes.c_void_p
with Image(filename="path_to_document.pdf", resolution=400) as image:
# Do all your preprocessing first
# Ether word directly on the wand instance, or iterate over each page.
# ...
# To write all "pages" into a single image.
# Reset the stack iterator.
library.MagickResetIterator(image.wand)
# Call C-API Append method.
resource_pointer = library.MagickAppendImages(image.wand,
True)
# Write C resource directly to disk.
library.MagickWriteImages(resource_pointer,
"output.jpeg".encode("ASCII"),
False)
Update:
I need to send the jpeg to an OCR api ...
Assuming your using OpenCV's python API, you'll only need to iterate over each page, and pass the image-file data to the OCR via numpy buffers.
from wand.image import Image
import numpy
import cv2
def ocr_process(file_data_buffer):
""" Replace with whatever your OCR-API calls for """
mat_instance = cv2.imdecode(file_data_buffer)
# ... work ...
source_image="path_to_document.pdf"
with Image(filename=source_image, resolution=400) as img:
for page in img.sequence:
file_buffer = numpy.asarray(bytearray(page.make_blob("JPEG")),
dtype=numpy.uint8)
ocr_process(file_buffer)
so I was wondering if I could write the output to a file like object
Don't assume that python "image" objects (or underlining C structures) from different libraries are comparable with each other.
Without knowing the OCR api, I can't help you past the wand part, but I can suggest one of the following...
Use temporary intermediate files. (slower I/O, but easier to learn/develop/debug)
with Image(filename=INPUT_PATH) as img:
# work
img.save(filename=OUTPUT_PATH)
# OCR work on OUTPUT_PATH
Use file descriptors if the OCR API supports it. (Same as above)
with open(INPUT_PATH, 'rb') as fd:
with Image(file=fd) as img:
# work
# OCR work ???
Use blobs. (faster I/O but need a lot more memory)
buffer = None
with Image(filename=INPUT_PATH) as img:
# work
buffer = img.make_blob(FORMAT)
if buffer:
# OCR work ???
Even More Updates
Wrapping all the comments together, a solution might be...
from wand.image import Image
from wand.api import library
import ctypes
import requests
# Map C-API not provided by wand library.
library.MagickAppendImages.argtypes = [ctypes.c_void_p, ctypes.c_int]
library.MagickAppendImages.restype = ctypes.c_void_p
with Image(filename='path_to_document.pdf', resolution=400) as image:
# ... Do pre-processing ...
# Reset the stack iterator.
library.MagickResetIterator(image.wand)
# Call C-API Append method.
resource_pointer = library.MagickAppendImages(image.wand, True)
# Convert to JPEG.
library.MagickSetImageFormat(resource_pointer, b'JPEG')
# Create size sentinel.
length = ctypes.c_size_t()
# Write image blob to memory.
image_data_pointer = library.MagickGetImagesBlob(resource_pointer,
ctypes.byref(length))
# Ensure success
if image_data_pointer and length.value:
# Create buffer from memory address
payload = ctypes.string_at(image_data_pointer, length.value)
# Define local filename.
payload_filename = 'my_hires_image.jpg'
# Post payload as multipart encoded image file with filename.
requests.post(THE_URL, files={'file': (payload_filename, payload)})
What about something like:
ok = Image(filename=file_path, resolution=400)
with ok.transform('2000x1000', '100%') as image:
image.compression_quality = 100
image.save()
or:
with ok.resize(2000, 1000)
related:
https://github.com/dahlia/wand/blob/13c4f544bd271fe298ac8dde44fbf178b349361a/docs/guide/resizecrop.rst
Python 3 Wand How to make an unanimated gif from multiple PDF pages

python livestreamer stream to image

The current code I have:
from livestreamer import Livestreamer
session = Livestreamer()
stream = session.streams('http://www.twitch.tv/esl_csgo')
stream = stream['source']
fd = stream.open()
with open("/tmp/stream.dat", 'wb') as f:
while True:
data = fd.read(1024)
f.write(data)
I would like to get a frame out of this stream and cut it before storing as a png image
This piece of code works and it can be played with vlc player without a problem but I would like to attain a frame without saving files the entire time to reduce IO on hard disks.
I've tried to use cv2 but I couldn't find my way around the API after installing it https://github.com/BVLC/caffe/wiki/Ubuntu-16.04-or-15.10-OpenCV-3.1-Installation-Guide

From JPG to b64encode to cv2.imread()

For a program I am writing, I am transferring an image from one computer - using base64.b64encode(f.read(image)) - and trying to read it in the receiving script without saving it to hard drive (in an effort to minimize process time). I'm having a hard time figuring out how to read the image into OpenCV without saving it locally.
Here is what my code for sending the image looks like:
f = open(image.jpg)
sendthis = f.read()
f.close()
databeingsent = base64.b64encode(sendthis)
client.publish('/image',databeingsent,0)
# this is an MQTT publish, details for SO shouldn't be relevant
Meanwhile, here is the code receiving it. (This is in an on_message function, since I'm using MQTT for the transfer.)
def on_message(client, userdata, msg): # msg.payload is incoming data
img = base64.b64decode(msg.payload)
source = cv2.imread(img)
cv2.imshow("image", source)
After the message decodes, I have the error:
"TypeError: Your input type is not a numpy array".
I've done some searching, and I can't seem to find a relevant solution - some exist regarding converting from text files to numpy using b64, but none really relate to using an image and immediately reading that decoded data into OpenCV without the intermediary step of saving it to the harddrive (using the inverse process used to read the file in the "send" script).
I'm still pretty new to Python and OpenCV, so if there's a better encoding method to send the image - whatever solves the problem. How the image is sent is irrelevant, so long as I can read it in on the receiving end without saving it as a .jpg to disk.
Thanks!
You can get a numpy array from you decoded data using:
import numpy as np
...
img = base64.b64decode(msg.payload)
npimg = np.fromstring(img, dtype=np.uint8)
Then you need imdecode to read the image from a buffer in memory. imread is meant to load an image from a file.
So:
import numpy as np
...
def on_message(client, userdata, msg): # msg.payload is incoming data
img = base64.b64decode(msg.payload);
npimg = np.fromstring(img, dtype=np.uint8);
source = cv2.imdecode(npimg, 1)
From the OpenCV documentation we can see that:
imread : Loads an image from a file.
imdecode : Reads an image from a buffer in memory.
Seem a better way to do what you want.

Piped FFMPEG won't write frames correctly

I am using Python's Image module to load JPEGs and modify them. After I have a modified image, I want to load that image in to a video, using more modified images as frames in my video.
I have 3 programs written to do this:
ImEdit (My image editing module that I wrote)
VideoWriter (writes to an mp4 file using FFMPEG) and
VideoMaker (The file I'm using to do everything)
My VideoWriter looks like this...
import subprocess as sp
import os
import Image
FFMPEG_BIN = "ffmpeg"
class VideoWriter():
def __init__(self,xsize=480,ysize=360,FPS=29,
outDir=None,outFile=None):
if outDir is None:
print("No specified output directory. Using default.")
outDir = "./VideoOut"
if outFile is None:
print("No specified output file. Setting temporary.")
outFile = "temp.mp4"
if (outDir and outFile) is True:
if os.path.exists(outDir+outFile):
print("File path",outDir+outFile, "already exists:",
"change output filename or",
"overwriting will occur.")
self.outDir = outDir
self.outFile = outFile
self.xsize,self.ysize,self.FPS = xsize,ysize,FPS
self.buildWriter()
def setOutFile(self,fileName):
self.outFile = filename
def setOutDir(self,dirName):
self.outDir = dirName
def buildWriter(self):
commandWriter = [FFMPEG_BIN,
'-y',
'-f', 'rawvideo',
'-vcodec','mjpeg',
'-s', '480x360',#.format(480,
'-i', '-',
'-an', #No audio
'-r', str(29),
'./{}//{}'.format(self.outDir,self.outFile)]
self.pW = sp.Popen(commandWriter,
stdin = sp.PIPE)
def writeFrame(self,ImEditObj):
stringData = ImEditObj.getIm().tostring()
im = Image.fromstring("RGB",(309,424),stringData)
im.save(self.pW.stdin, "JPEG")
self.pW.stdin.flush()
def finish(self):
self.pW.communicate()
self.pW.stdin.close()
ImEditObj.getIm() returns an instance of a Python Image object
This code works to the extent that I can load one frame in to the video and no matter how many more calls to writeFrame that I do, the video only every ends up being one frame long. I have other code that works as far as making a video out of single frames and that code is nearly identical to this code. I don't know what difference there is though that makes this code not work as intended where the other code does work.
My question is...
How can I modify my VideoWriter class so that I can pass in an instance of an Python's Image object and write that frame to an output file? I also would like to be able to write more than one frame to the video.
I've spent 5 hours or more trying to debug this, having not found anything helpful on the internet, so if I missed any StackOverflow questions that would point me in the right direction, those would be appreciated...
EDIT:
After a bit more debugging, the issue may have been that I was trying to write to a file that already existed, however, this doesn't make much sense with the -y flag in my commandWriter. the -y flag should overwrite any file that already exists. Any thoughts on that?
I suggest that you follow the OpenCV tutorial in writing videos. This is a very common way of writing video files from Python, so you should find many answers on the internet, if you can't get certain things to work.
Note that the VideoWriter will discard (and won't write) any frames that are not in the exact same pixel size that you give it on initialization.

Categories

Resources