How to Urlretrieve and crop image based on data from CSV file? - pandas

I have a CSV file with url's and box coordinates (x coordinate of the top left corner, y coordinate of the top left corner, x coordinate of the bottom right corner and y coordinate of the bottom right corner) and I would like to acquire the image, crop it based on the coordinates (to 256x256) and then save the image. Unfortunately a solution to download the whole database and then create a separate with cropped images is difficult due to the size of the database. That for, it is necessary to create the image database with cropped images from the beginning. Another way is to save the image and then subsequently crop it and rewrite the initial image (and then i += 1 iterate to the next one).
Would the current approach work or should I use a different method for it? Additonally, how would I save the acquired images to a specified folder, as currently it downloads to the same folder as the script.
import urllib.request
import csv
import numpy as np
import pandas as pd
from io import BytesIO
import requests
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
filename = "images"
# open file to read
with open("data_test.csv".format(filename), 'r') as csvfile:
reader = csv.reader(csvfile)
# pop header row (1st row in csv)
header = next(reader)
# iterate on all lines
i = 0
for line in csvfile:
splitted_line = line.split(',')
# check if we have an image URL
if splitted_line[1] != '' and splitted_line[1] != "\n":
response = requests.get(splitted_line[1])
img = Image.open(BytesIO(response.content))
#crop_img = img[splitted_line[2]:splitted_line[3], splitted_line[4]:splitted_line[5]]
#crop_img = img[315:105, 370:173]
img.save(str(i) + ".png")
#crop_img = img[105:105+173,315:315+370]
#[y: y + h, x: x + w]
new_img = img.resize((256, 256))
new_img.save(str(i) + ".png")
imgplot = plt.imshow(img)
plt.show()
# urllib.request.urlopen(splitted_line[1])
print("Image saved for {0}".format(splitted_line[0]))
# img = cv2.imread(img_path, 0)
i += 1
else:
print("No result for {0}".format(splitted_line[0]))
Any further recommendations are welcome.
Edit: The latest version gives me error :
crop_img = img[105:105+173,315:315+370]
TypeError: 'JpegImageFile' object is not subscriptable

I solved the problem using Bytes.IO and some cropping/resizing techniques.
import csv
from io import BytesIO
import requests
from PIL import Image
import matplotlib.pyplot as plt
filename = "images"
# open file to read
with open("data_test.csv".format(filename), 'r') as csvfile:
reader = csv.reader(csvfile)
# pop header row (1st row in csv)
header = next(reader)
# iterate on all lines
i = 0
for line in csvfile:
splitted_line = line.split(',')
# check if we have an image URL
if splitted_line[1] != '' and splitted_line[1] != "\n":
response = requests.get(splitted_line[1])
img = Image.open(BytesIO(response.content))
#im.crop(box) ⇒ 4-tuple defining the left, upper, right, and lower pixel coordinate
left_x = int(splitted_line[2])
top_y = int(splitted_line[3])
right_x = int(splitted_line[4])
bottom_y = int(splitted_line[5])
crop = img.crop((left_x, top_y, right_x, bottom_y))
new_img = crop.resize((256, 256))
"""
# preview new images
imgplot = plt.imshow(new_img)
plt.show()
"""
new_img.save(str(i) + ".png")
print("Image saved for {0}".format(splitted_line[0]))
i += 1
else:
print("No result for {0}".format(splitted_line[0]))
Hope it will help someone. Any optimization recommendations are still welcome.

Related

Matplotlib transparent background without save() function

I have this kind of an animation and I want to integrate it to my GUI.
here is the plot
But, the background color is set to black right now. Here is the code. I am using Windows 10 and for GUI I am mostly using PyQt6 but for the matplotlib I used mlp.use("TkAgg") because it didn't create output if I dont use TkAgg.
I want to make it transparent. I only want the curves. I searched on the internet but everything is about save() function. Isn't there another solution for this? I don't want to save it, I am using animation, therefore it should be transparent everytime, not in a image.
import queue
import sys
from matplotlib.animation import FuncAnimation
import PyQt6.QtCore
import matplotlib as mlp
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
as FigureCanvas
mlp.use("TkAgg")
import matplotlib.pyplot as plt
import numpy as np
import sounddevice as sd
plt.rcParams['toolbar'] = 'None'
plt.rcParams.update({
"figure.facecolor": "black", # red with alpha = 30%
})
# Lets define audio variables
# We will use the default PC or Laptop mic to input the sound
device = 0 # id of the audio device by default
window = 1000 # window for the data
downsample = 1 # how much samples to drop
channels = [1] # a list of audio channels
interval = 40 # this is update interval in miliseconds for plot
# lets make a queue
q = queue.Queue()
# Please note that this sd.query_devices has an s in the end.
device_info = sd.query_devices(device, 'input')
samplerate = device_info['default_samplerate']
length = int(window*samplerate/(1000*downsample))
plotdata = np.zeros((length,len(channels)))
# next is to make fig and axis of matplotlib plt
fig,ax = plt.subplots(figsize=(2,1))
fig.subplots_adjust(0,0,1,1)
ax.axis("off")
fig.canvas.manager.window.overrideredirect(1)
# lets set the title
ax.set_title("On Action")
# Make a matplotlib.lines.Line2D plot item of color green
# R,G,B = 0,1,0.29
lines = ax.plot(plotdata,color = "purple")
# We will use an audio call back function to put the data in
queue
def audio_callback(indata,frames,time,status):
q.put(indata[::downsample,[0]])
# now we will use an another function
# It will take frame of audio samples from the queue and update
# to the lines
def update_plot(frame):
global plotdata
while True:
try:
data = q.get_nowait()
except queue.Empty:
break
shift = len(data)
plotdata = np.roll(plotdata, -shift,axis = 0)
# Elements that roll beyond the last position are
# re-introduced
plotdata[-shift:,:] = data
for column, line in enumerate(lines):
line.set_ydata(plotdata[:,column])
return lines
# Lets add the grid
ax.set_yticks([0])
# ax.yaxis.grid(True)
""" INPUT FROM MIC """
stream = sd.InputStream(device = device, channels = max(channels),
samplerate = samplerate, callback = audio_callback)
""" OUTPUT """
ani = FuncAnimation(fig,update_plot,interval=interval,blit=True, )
plt.get_current_fig_manager().window.wm_geometry("200x100+850+450")
with stream:
plt.show()

Matplotlib Colormap Gray generates different results on a binary array of type int32

Goal: Avoid File Write/Read Operations
Task: Generate RGBA image as shown in the picture below (img1 from the code)
Issue: Without file write and read operations, Getting Black Image as shown in the picture below (img2 from the code)
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
# download file from here: https://drive.google.com/file/d/1R9MEeK-7vUM59An-frFtZv2dtTw-jhs7/view?usp=sharing
bin_mask = np.load("bin_mask.npy") #
# Method1: Does unnecessary file write/read but works
plt.imsave('img1.png', bin_mask, cmap=cm.gray)
img1 = Image.open('img1.png')
# Method 2: No file write but img1 != img2;
# Ref: https://stackoverflow.com/questions/10965417/how-to-convert-a-numpy-array-to-pil-image-applying-matplotlib-colormap
img2 = Image.fromarray(np.uint8(cm.gray(bin_mask)*255))
# unique values of img1: [0, 255]; dtype=uint8
# unique values of img2: [0, 1, 255]; dtype=uint8
print("img1 same as img2: ", img1 == img2) # False
This task seems trivial at first sight but I'm not sure why its behaving this way.
Any suggestions would be appreciated, Thanks in advance.
You have same results from both images when bin_mask value is 0 and different when it is 1.
print('Bin mask={}, img1={}, img2={}'.format(bin_mask[-1][-1] ,np.array(img1)[-1][-1] ,np.array(img2)[-1][-1] ))
# Bin mask=0, img1=[ 0 0 0 255], img2=[ 0 0 0 255]
print('Bin mask={}, img1={}, img2={}'.format(bin_mask[0][0] ,np.array(img1)[0][0] ,np.array(img2)[0][0] ))
# Bin mask=1, img1=[255 255 255 255], img2=[ 1 1 1 255]
Looking further when you call cm.gray(1) gives (0.00392156862745098, 0.00392156862745098, 0.00392156862745098, 1.0) and cm.gray(255) gives (1,1,1,1). So you should be multiplying 255 with bin_mask if you looking for same result.
Following lines will result same content of Img1 and Img2.
img3=Image.fromarray(np.uint8(cm.gray(bin_mask*255)*255))
print(img1 == img3) #result will be false, since this is not correct way to compare data in Image
print(list(img1.getdata()) == list(img3.getdata())) # result is True
However the way you are doing takes too much time when it is compared with Opencv. You can do same thing using OpenCV using following way.
img3 = cv2.cvtColor(np.array(bin_mask.astype(np.uint8) * 255), cv2.COLOR_GRAY2RGBA)
print('Result from Opencv=',np.all(img3 == np.array(img1))) # true
Refer bellow, for full code to understand and time taken by your method vs mine.
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import numpy as np
import cv2
import time
from PIL import Image
# download file from here: https://drive.google.com/file/d/1R9MEeK-7vUM59An-frFtZv2dtTw-jhs7/view?usp=sharing
bin_mask = np.load("/home/jagdish/Downloads/bin_mask.npy") #
# Method1: Does unnecessary file write/read but works
plt.imsave('img1.png', bin_mask, cmap=cm.gray)
img1 = Image.open('img1.png')
# Method 2: No file write but img1 != img2;
# Ref: https://stackoverflow.com/questions/10965417/how-to-convert-a-numpy-array-to-pil-image-applying-matplotlib-colormap
#Your way
img3=Image.fromarray(np.uint8(cm.gray(bin_mask*255)*255))
print('Comparing image class=',img1==img3)
print('Comparing content of Image=',list(img1.getdata()) == list(img3.getdata()))
#OpenCV way
img3 = cv2.cvtColor(np.array(bin_mask.astype(np.uint8) * 255), cv2.COLOR_GRAY2RGBA)
print('Result from Opencv=',np.all(img3 == np.array(img1)))
start_time = time.time()
for i in range(1000):
img3 = Image.fromarray(np.uint8(cm.gray(bin_mask,)*255))
print((time.time()-start_time)*1000)
start_time = time.time()
for i in range(1000):
img3 = cv2.cvtColor(np.array(bin_mask.astype(np.uint8) * 255), cv2.COLOR_GRAY2RGBA)
print((time.time()-start_time)*1000)
Here is time comparison for you.
Using matplotlib to process 1000 images 920 ms
using Opencv to process 1000 images 94 ms

How to tell `photutils` to plot only apertures which satisfy a condition?

I'm following an example in the photutils documentation to detect sources in an image:
from astropy.stats import sigma_clipped_stats
from photutils.datasets import load_star_image
import numpy as np
import matplotlib.pyplot as plt
from astropy.visualization import SqrtStretch
from astropy.visualization.mpl_normalize import ImageNormalize
from photutils.detection import DAOStarFinder
from photutils.aperture import CircularAperture
# Load image
hdu = load_star_image() # load a star image from the dataset
data = hdu.data[0:101, 0:101]
mean, median, std = sigma_clipped_stats(data, sigma = 3.0) # estimate noise
# Find stars in the image that have FWHMs of 3 pixels and peaks ~ 5 sigma > bg
daofind = DAOStarFinder(fwhm = 3.0, threshold = 5.*std)
sources = daofind(data - median)
# Print position and photometric data for each star in the image
for col in sources.colnames:
sources[col].info.format = '%.8g' # for consistent table output
positions = np.transpose((sources['xcentroid'], sources['ycentroid']))
apertures = CircularAperture(positions, r = 4.)
norm = ImageNormalize(stretch = SqrtStretch())
plt.imshow(data, cmap = 'Greys', origin = 'lower', norm = norm,
interpolation = 'nearest')
for i in range(len(sources)):
if sources[i][-1] < -2:
print(sources[i][-1])
apertures.plot(color = 'r', lw = 1.5, alpha = 0.5
Which produces
I've added the last four lines, with the intention to plot apertures around only the brightest stars. However, the for loop doesn't change the image. I understand why (it's plotting all apertures multiple times, once for each of the 4 stars with mag < -2), but how do I change it to plot them for only those stars?

How to read each txt, convert it into image, and save them in different images?

I would like to let my code read my txt file one by one, convert it into image, and save it with different image, i.e 300s, 600s, 900s,....
I made the code down and it says only the path but did not proceed with its next code.
Could you give me some advice or find the missing or mistaken part of my codes?
import numpy as np
import matplotlib.pyplot as plt
import glob
import cv2
import os
path = './Master_thesis/Code/dnn_simulation_result/'
interval = 300
folders = []
#r=root, d=dirctories, f=files
for r, d, f in os.walk(path):
if not d:
folders.append(r)
for f in folders:
print(r)
def txt2image(folders, skiprows) :
for folder_name in folders:
IsFile=(glob.glob(folder_name+"/*.*"))
for file in IsFile:
myArray = np.loadtxt(path, skiprows = skiprows)
# Set the nodata values to nan
myArray[myArray == -9999] = np.nan
# PRISM data is stored as an integer but scaled by 100
myArray *= 1
# Plot PRISM array again
fig, ax = plt.subplots()
ax.set_title('Flood area')
# Get the img object in order to pass it to the colorbar function
img_plot = ax.imshow(myArray, cmap='jet')
# Place a colorbar next to the map
cbar = fig.colorbar(img_plot)
ax.grid(True)
plt.show()
txt2image = cv2.imwrite('D:/Master_thesis/Code/dnn_simulation_result/dnn_simulation_result/{}.jpg', img_plot)
return txt2image
txt2image(folders, 0)

How to export TFRecords from pts label files for tensorflow object detection api?

usually when we generate TFRecords from xml label files (from labelimg for example), there are the values of x.min, x.max, y.min and y.max, which show a square label.
we can make a CSV data out of it and generate the TFRecords from it.
but in the case of pts, the values are as a non-square bounding box, e.g:
bounding_box: 534.588998862 232.095176337; 101.596234357 388.45367463; 51.3295676906 249.25367463; 484.322332196 92.8951763367
so there is four x and y points, not just two as the labelimg gives.
can someone explain to me how generate TFRecord from pts?
So just in case anyone else had the same question, i wrote a script that'll make those four points as a square with xmin xmax ymin ymax, so we can get the tfrecord easily as like from xml labelimg.
here it is:
import os
import glob
import pandas as pd
from PIL import Image
import csv
for pts_file in glob.glob("./labels" + '/*.pts'):
with open(pts_file) as f:
im=Image.open("./img/" + pts_file[9:-3] + "jpg")
filename = pts_file[9:-3] + "jpg"
width = str(im.size[0])
height = str(im.size[1])
classs = "fish"
lines = f.readlines()
content = [line.split(' ')for line in open (pts_file)]
xmax = max(int(float(content[0][1])), int(float(content[0][4])), int(float(content[0][7])), int(float(content[0][10])))
xmin = min(int(float(content[0][1])), int(float(content[0][4])), int(float(content[0][7])), int(float(content[0][10])))
ymax = max(int(float(content[0][3][0:5])), int(float(content[0][6][0:5])), int(float(content[0][9][0:5])), int(float(content[0][11][0:5])))
ymin = min(int(float(content[0][3][0:5])), int(float(content[0][6][0:5])), int(float(content[0][9][0:5])), int(float(content[0][11][0:5])))
fields=[filename,width,height,classs,xmin,ymin,xmax,ymax]
with open(r'name', 'a', newline='') as f:
writer = csv.writer(f)
writer.writerow(fields)
print('Successfully converted pts to csv.')