I have a snippet that reads my images locally (returns binary), i.e:
image = 'car.jpg'
with open(image, 'rb') as image_file:
content = image_file.read()
I'm trying to recreate the same but with images that are hosted on the internet, I have tried with the following but I've had no luck.
from urllib.request import urlopen
from PIL import Image
import io
url = 'https://somewebsite.com/fm-01/car.jpg'
image = urlopen(url)
image_file = io.BytesIO(image.read())
im = Image.open(image_file)
im = im.tobytes()
EDIT ...
Same exact image, one on google cloud storage and the other one locally.
They have different bytes when opening them.
with open('car.jpg','rb') as image_file:
content = image_file.read()
print(len(content))
size : 234712
url = 'https://storage.googleapis.com/fm-01/car.jpg'
img = Image.open(urlopen(url))
image = img.tobytes()
print(len(image))
size : 1077600
It is simple as:
from urllib.request import urlopen
url = 'https://somewebsite.com/fm-01/car.jpg'
img = urlopen(url).read()
You have not understood the comment I made explaining exactly this under Mikhail's answer.
The 234,712 bytes is the size of the JPEG-encoded data in the disk file - it includes the image height, width, date, GPS coordinates, camera manufacturer and the all the pixels of the image DCT-compressed into a JPEG.
The 1,077,600 bytes is the size of the array you will need to create in memory to hold the uncompressed red, green and blue pixels in, say 898x400 pixels at 3 bytes (1 red, 1 green and 1 blue) per pixel.
898 * 400 * 3 = 1077600
Related
I am trying to work with IMDb API. My code thus far is
import http.client
import json
import requests
conn = http.client.HTTPSConnection("imdb-api.com", 443)
payload = ''
headers = {'User-agent': 'Chrome/95.0'}
conn.request("GET", "https://imdb-api.com/en/API/MostPopularMovies/<API_Key>",headers=headers)
res = conn.getresponse()
data = res.read()
convertedDict = json.loads(data.decode("utf-8"))
imagepath = r'venv/files/image.jpeg'
req = requests.get(convertedDict['items'][0]['image'], headers=headers)
with open(imagepath, 'wb') as file:
file.write(req.content)
This allows me to download the image of the first popular movie, however, the image size is really small. This is the link that I am downloading. I know that if I get rid of everything after # the image will become a lot larger. Is there a way to edit the link such that I can drop everything after # and even edit the numbers after UX with code?
Everything I try to do with string or URL operations give's me an error
https://m.media-amazon.com/images/M/MV5BZWMyYzFjYTYtNTRjYi00OGExLWE2YzgtOGRmYjAxZTU3NzBiXkEyXkFqcGdeQXVyMzQ0MzA0NTM#._V1_UX128_CR0,3,128,176_AL_.jpg
Thank you in advance
Explanation
(code example below)
Here's how to get a bigger image of the size you want. Given this URL,
https://m.media-amazon.com/images/M/MV5BZWMyYzFjYTYtNTRjYi00OGExLWE2YzgtOGRmYjAxZTU3NzBiXkEyXkFqcGdeQXVyMzQ0MzA0NTM#._V1_UX128_CR0,3,128,176_AL_.jpg
There's a substring of it:
UX128_CR0,3,128,176
This has three important parts:
The first 128 resizes the image by width, keeping ratio
The second 128 controls the container width that the image appears in
176 controls the container height that the image appears in.
So, we can view the structure like this:
UX<image_width>_CR0,3,<container_width>,<container_height>
As an example, to double the image size:
UX256_CR0,3,256,352_AL_.jpg
(Click here to see: https://m.media-amazon.com/images/M/MV5BZWMyYzFjYTYtNTRjYi00OGExLWE2YzgtOGRmYjAxZTU3NzBiXkEyXkFqcGdeQXVyMzQ0MzA0NTM#.V1_UX256_CR0,3,256,352_AL.jpg
Update: Example of how you might do it in Python.
import re
resize_factor = 2 # Image size multiple
url = "https://m.media-amazon.com/images/M/MV5BZWMyYzFjYTYtNTRjYi00OGExLWE2YzgtOGRmYjAxZTU3NzBiXkEyXkFqcGdeQXVyMzQ0MzA0NTM#._V1_UX128_CR0,3,128,176_AL_.jpg"
#
# resize_factor : Image size multiplier (e.g., resize_factor = 2 doubles the image size, positive integer only)
# url : full URL of the image
# return : string of the new URL
#
def getURL(resize_factor, url):
# Regex for pattern matching relevant parts of the URL
p = re.compile(".*UX([0-9]*)_CR0,([0-9]*),([0-9]*),([0-9]*).*")
match = p.search(url)
if match:
# Get the image dimensions from the URL
img_width = str(int(match.group(1)) * resize_factor)
container_width = str(int(match.group(3)) * resize_factor)
container_height = str(int (match.group(4)) * resize_factor)
# Change the image dimensions
result = re.sub(r"(.*UX)([0-9]*)(.*)", r"\g<1>"+ img_width +"\g<3>", url)
result = re.sub(r"(.*UX[0-9]*_CR0,[0-9]*,)([0-9]*)(.*)", r"\g<1>"+ img_width +"\g<3>", result)
result = re.sub(r"(.*UX[0-9]*_CR0,[0-9]*,[0-9]*,)([0-9]*)(.*)", r"\g<1>"+ container_height +"\g<3>", result)
return result
#
# Test
#
print (getURL(resize_factor,url))
Edit: Typo
I'm trying to detect width and height of an image before saving it to the database and S3. The image is in bytes.
This is an example of an image before saved to Django ImageField:
NOTE: I don't want to use ImageFields height_field and width_field as it slows down the server tremendously for some reason so I want to do it manually.
The image is downloaded using requests:
def download_image(url):
r = requests.get(url, stream=True)
r.raw.decode_content = True
return r.content
To get the width/height of an image from a binary string, you would have to try to parse the binary string with an image library. The easiest one for the job would be pillow.
import requests
from PIL import Image
import io
def download_image(url):
r = requests.get(url, stream=True)
r.raw.decode_content = True
return r.content
image_url = "https://picsum.photos/seed/picsum/300/200"
image_data = download_image(image_url)
image = Image.open(io.BytesIO(image_data))
width = image.width
height = image.height
print(f'width: {width}, height: {height}')
width: 300, height: 200
On my model save method I want to generate an avatar and upload to a ImageField (self.avatar).
The code below runs but the image it uploads is blank when I view it. I've tested the generator actually works by saving directly to disk without BytesIO stream i.e. image.save("test.jpeg", format=filetype, optimize=True) and this works. So, the issue appears to be with how am using BytesIO and SimpleUploadedFile.
Save method
from .generate_avatar import Avatar
from django.core.files.uploadedfile import SimpleUploadedFile
avatar = Avatar.generate(128, self.display_name, "JPEG")
self.avatar = SimpleUploadedFile("temp.jpeg", avatar.read1(0))
Generate Method
def generate(cls, size, string, filetype="JPEG"):
"""
Generates a squared avatar with random background color.
:param size: size of the avatar, in pixels
:param string: string to be used to print text and seed the random
:param filetype: the file format of the image (i.e. JPEG, PNG)
"""
render_size = max(size, Avatar.MIN_RENDER_SIZE)
image = Image.new('RGB', (render_size, render_size),
cls._background_color(string))
draw = ImageDraw.Draw(image)
font = cls._font(render_size)
text = cls._text(string)
draw.text(cls._text_position(render_size, text, font),
text,
fill=cls.FONT_COLOR,
font=font)
stream = BytesIO()
image = image.resize((size, size), Image.ANTIALIAS)
image.save(stream, format=filetype, optimize=True)
return stream
You need to reset file position. Otherwise file position will be at the end of the file; Reading from there will return empty byte string.
avatar = Avatar.generate(128, self.display_name, "JPEG")
avatar.seek(0) # <---
self.avatar = SimpleUploadedFile("temp.jpeg", avatar.read())
This block of code is to save the image data read from a byte array to the Image img
val = bytearray(message.msg)
size = re.split(r',', message.messageSize)
img = Image.new("L", (int(size[0]), int(size[1])), 0)
pix = img.load()
counter = 0
for y in range(int(size[1])):
for x in range(int(size[0])):
pix[x, y] = val[counter]
counter = counter + 1
Then if I img.save('test.png', 'PNG') and QPixmap('test.png'), it will display the image normally. But if I use the following method, the image would be twisted.(screenshot and image are attached to the hyperlinks below)
self.imgQ = ImageQt(img) # img is PIL Image type
img.save('test.png', 'PNG') # this will be success
pixMap = QtGui.QPixmap.fromImage(self.imgQ)
self.scene1.clear()
self.scene1.addPixmap(pixMap)
self.scene1.update()
self.viewMessage.fitInView(QRectF(0, 0, int(size[0]), int(size[1])), Qt.KeepAspectRatio)
after I implemented the code above, the image is shown twisted. But if I saved the image, the image looks correct.
[update] even if I use the PNG that I saved, it still twisted.
im = Image.open('hehe.png')
I have uploaded the PNG file here.
I am trying to generate a image which produces gif like effect by flushing the response continuously to the browser, I am relatively new to django/python and tried with following code testing for both text and image. The generator for text works fine but in case of image only first version of image is being generated.
I tried searching but can't find anything on this. I am confused how to proceed with this or if this is at all possible with django or If the idea is conceptually wrong.
Image Generator :
def refreshx():
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
size = (1000,500)
im = Image.new('RGB', size)
draw = ImageDraw.Draw(im)
red = (255,255,255)
text_pos = (10,30)
draw.text(text_pos, str(datetime.datetime.today()), fill=red)
buffer = StringIO.StringIO()
im.save(buffer, 'PNG')
yield buffer.getvalue()
time.sleep(5)
buffers = StringIO.StringIO()
ims = Image.new('RGB', size)
draws = ImageDraw.Draw(ims)
text_poss = (30,80)
draws.text(text_poss, 'dasdasdsa', fill=red)
print 'been there'
ims.save(buffers, 'PNG')
yield buffers.getvalue()
Text Generator :
def testgenerator():
yield str(datetime.datetime.today())
time.sleep(5)
yield str(datetime.datetime.today())
View Function :
def test(request):
#return HttpResponse(testgenerator());
return HttpResponse(refreshx(), mimetype="image/png")
EDIT :
I learned while researching that there's a concept called gifsocket, I'm looking into it..please suggest if anyone has experience with these