Is there a way to check if script is running from subprocess? - python

Let's say I have a python script which reads all the images in a folder and resizes them. The script works all by his own, it takes in two arguments - the input folder and an output folder.
To have a visual response of the progress I'm using a progressbar which is printed out to the console/terminal.
resize.py:
import argparse
import fnmatch
import os
import PIL
from PIL import Image
from progress.bar import Bar
parser = argparse.ArgumentParser(description='Photo resizer.')
parser.add_argument('input_folder', nargs='?', default='', help="Input folder")
parser.add_argument('export_folder', nargs='?', default='', help="Output folder")
args = parser.parse_args()
if args.input_folder:
input_folder = args.input_folder
if args.export_folder:
export_resized_folder = args.export_folder
NEW_SIZE = 2000
inputFiles = []
for root, dirnames, filenames in os.walk(input_folder):
for filename in fnmatch.filter(filenames, '*.jpg'):
inputFiles.append(os.path.join(root, filename))
bar = Bar("Processing photos", max=len(inputFiles), check_tty=False)
for photo in inputFiles:
filename = os.path.basename(photo)
im = Image.open(photo)
im_width, im_height = im.size
if im_width > im_height:
new_width = NEW_SIZE
new_height = int(NEW_SIZE * im_height / im_width)
else:
new_height = NEW_SIZE
new_width = int(NEW_SIZE * im_width / im_height)
new_size = (new_width, new_height)
im_resized = im.resize(new_size, resample=PIL.Image.Resampling.LANCZOS)
im_resized.save(os.path.join(export_resized_folder, filename), quality=70)
bar.next()
bar.finish()
Now I have an another script (main_gui.py) which does some batch processing and one of the jobs is to resize the images. This script provides a simple GUI. When it comes to resizing the images, I use subprocess Popen to execute the script and pass in the input and output folders as args.
So in main_gui.py I start the subprocess:
script_path = "resize.py"
process = subprocess.Popen(["python", script_path, INPUT_FOLDER, OUTPUT_FOLDER], universal_newlines=True, stdout=subprocess.PIPE)
Now I'd like to see the progress in the GUI also. I don't know if I'm doing it correctly (It is a high probability that not, this is just the first thing that came to my mind)...
So in resize.py along with the progressbar I print out information about my progress and then read it in the main_gui.py and based on that information I update a tkinter progressbar.
In resize.py:
bar = Bar("Processing photos", max=len(inputFiles), check_tty=False)
print("**TOTAL** " + str(len(inputFiles)))
...
progressCounter = 1
for photo in inputFiles:
...
bar.next()
print("**PROGRESS** " + str(progressCounter))
progressCounter += 1
...
I read these values in main_gui.py
process = subprocess.Popen(["python", script_path], universal_newlines=True, stdout=subprocess.PIPE)
while process.poll() is None:
data = process.stdout.readline().strip()
print(data)
if "**TOTAL** " in data:
total = int(data.replace("**TOTAL** ", ""))
progressbarWidget['maximum'] = total
if "**PROGRESS** " in data and self.GUI:
progressCounter = int(data.replace("**PROGRESS** ", ""))
progressbarWidget['value'] = progressCounter
progressbarWidget.update_idletasks()
And at this point I'd like in my resize.py check if it is run by itself or by the subprocess, so I don't have the unnecessary print statements.
I tried pass in an env value as Charles suggested in the comments, but couldn't get it done

Trying to detect your parent process is an unnecessary amount of magic for this use case. Making it explicit with an optional argument will let others writing their own GUIs (potentially in non-Python languages) get the machine-readable status output without needing to try to fool the detection.
parser = argparse.ArgumentParser(description='Photo resizer.')
parser.add_argument('--progress', choices=('none', 'human', 'machine-readable'), default='none',
help="Should a progress bar be written to stderr in a human-readable form, to stdout in a machine-readable form, or not at all?")
parser.add_argument('input_folder', nargs='?', default='', help="Input folder")
parser.add_argument('export_folder', nargs='?', default='', help="Output folder")
args = parser.parse_args()
...and then later...
if args.progress == 'machine-readable':
pass # TODO: Write your progress messages for the programmatic consumer to stdout here
elif args.progress == 'human':
pass # TODO: Write your progress bar for a human reader to stderr here
while on the GUI side, adding --progress=human to the argument list:
process = subprocess.Popen([sys.executable, script_path, '--progress=human'],
universal_newlines=True, stdout=subprocess.PIPE)

Related

How to create a folder in python?

I am running a code in python where I get images from input file, and create another folder as output and a file csv. The code that I run is as below:
# import the necessary packages
from PIL import Image
import argparse
import random
import shutil
import glob2
import uuid
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--input", required = True,
help = "input directory of images")
ap.add_argument("-o", "--output", required = True,
help = "output directory")
ap.add_argument("-c", "--csv", required = True,
help = "path to CSV file for image counts")
args = vars(ap.parse_args())
# open the output file for writing
output = open(args["csv"], "w")
# loop over the input images
for imagePath in glob2.iglob(args["input"] + "/*/*.jpg"):
# generate a random filename for the image and copy it to
# the output location
filename = str(uuid.uuid4()) + ".jpg"
shutil.copy(imagePath, args["output"] + "/" + filename)
# there is a 1 in 500 chance that multiple copies of this
# image will be used
if random.randint(0, 500) == 0:
# initialize the number of times the image is being
# duplicated and write it to the output CSV file
numTimes = random.randint(1, 8)
output.write("%s,%d\n" % (filename, numTimes))
# loop over a random number of times for this image to
# be duplicated
for i in range(0, numTimes):
image = Image.open(imagePath)
# randomly resize the image, perserving aspect ratio
factor = random.uniform(0.95, 1.05)
width = int(image.size[0] * factor)
ratio = width / float(image.size[0])
height = int(image.size[1] * ratio)
image = image.resize((width, height), Image.ANTIALIAS)
# generate a random filename for the image and copy
# it to the output directory
adjFilename = str(uuid.uuid4()) + ".jpg"
shutil.copy(imagePath, args["output"] + "/" + adjFilename)
# close the output file
output.close()
After running the code I get only csv file, but I don't get output folder.
The way I run the code is:
python gather.py --input 101_ObjectCategories --output images --csv output.csv
Please can you help me how to solve the problem, because I need the output folder for next steps, running next functions.
I would recommend the following approach:
import os
from pathlib import Path
Path('path').mkdir(parents=True, exist_ok=True)
This works cross-platform and doesn't overwrite the directories if they already exist.
You should try the os module. It has a mkdir method that creates a directory based on the path you give it as a parameter.
import os
os.mkdir("path")
While most answers suggest using os.mkdir() I suggest you rather go for os.makedirs() which would recursively create all the missing folders in your path, which usually is more convinient.
import os
os.makedirs('foo/bar')
Docs: https://docs.python.org/3/library/os.html#os.makedirs

OpenCV read video files with multiple streams/tracks

I have a video file that contains multiple streams as shown below using VLC media player:
Video Information
When I try to read it using Python + OpenCV using the following code:
vidObj = cv2.VideoCapture("video.avi")
ret, frame = vidObj.read()
I can only read the first track of the video. How can I read all the video tracks at the same time?
As far as I could tell, OpenCV does not allow choosing video stream, so this is not possible. However, you can do it rather easily with ffmpeg command line utilities:
import numpy as np
import json
import subprocess
def videoInfo(filename):
proc = subprocess.run([
*"ffprobe -v quiet -print_format json -show_format -show_streams".split(),
filename
], capture_output=True)
proc.check_returncode()
return json.loads(proc.stdout)
def readVideo(filename):
cmd = ["ffmpeg", "-i", filename]
streams = 0
for stream in videoInfo(filename)["streams"]:
index = stream["index"]
if stream["codec_type"] == "video":
width = stream["width"]
height = stream["height"]
cmd += "-map", f"0:{index}"
streams = streams + 1
cmd += "-f", "rawvideo", "-pix_fmt", "rgb24", "-"
shape = np.array([streams, height, width, 3])
with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc:
while True:
data = proc.stdout.read(shape.prod()) # One byte per each element
if not data:
return
yield np.frombuffer(data, dtype=np.uint8).reshape(shape)
Note that the code reads all video streams and assumes that each has the same resolution. It lacks proper error handling but got the job done in my scientific project.
For example, reading stereoscopic stream:
import matplotlib.pyplot as plt
for left, right in readVideo("testvideo.mkv"):
plt.imshow(left)
plt.show()
plt.imshow(right)
plt.show()

using python gooey how to open another GUI after clicking one out of multiple button

Hi I have following code which is working properly for python GUI based on wxpython-gooey. However my task is to create a main GUI with multiple buttons and after clicking any button respective GUI should open and process the respective task and close. The code i have is not for main GUI but after clicking button on main GUI the below code's GUI must open, the sub-GUI code is as follows
from __future__ import print_function
import pandas as pd
import numpy as np
import glob
import os
import json
from argparse import ArgumentParser
from gooey import Gooey, GooeyParser
#Gooey(program_name="Jio Project")
def parse_args():
""" Use GooeyParser to build up the arguments we will use in our script
Save the arguments in a default json file so that we can retrieve them
every time we run the script.
"""
stored_args = {}
# get the script name without the extension & use it to build up
# the json filename
script_name = os.path.splitext(os.path.basename(__file__))[0]
args_file = "{}-args.json".format(script_name)
# Read in the prior arguments as a dictionary
if os.path.isfile(args_file):
with open(args_file) as data_file:
stored_args = json.load(data_file)
parser = GooeyParser(description='Qualcomm-Samsung a Jio project')
parser.add_argument('data_directory',
action='store',
default=stored_args.get('data_directory'),
widget='DirChooser',
help="Source directory that contains Excel files")
parser.add_argument('output_directory',
action='store',
widget='DirChooser',
default=stored_args.get('output_directory'),
help="Output directory to save summary report")
parser.add_argument('cust_file',
action='store',
default=stored_args.get('cust_file'),
widget='FileChooser',
help='Customer Account Status File')
parser.add_argument('-d', help='Start date to include',
default=stored_args.get('d'),
widget='DateChooser')
args = parser.parse_args()
# Store the values of the arguments so we have them next time we run
with open(args_file, 'w') as data_file:
# Using vars(args) returns the data as a dictionary
json.dump(vars(args), data_file)
return args
def combine_files(src_directory):
""" Read in all of the sales xlsx files and combine into 1
combined DataFrame
"""
all_data = pd.DataFrame()
for f in glob.glob(os.path.join(src_directory, "sales-*.xlsx")):
df = pd.read_excel(f)
all_data = all_data.append(df, ignore_index=True)
all_data['date'] = pd.to_datetime(all_data['date'])
return all_data
def add_customer_status(sales_data, customer_file):
""" Read in the customer file and combine with the sales data
Return the customer with their status as an ordered category
"""
df = pd.read_excel(customer_file)
all_data = pd.merge(sales_data, df, how='left')
# Default everyone to bronze if no data included
all_data['status'].fillna('bronze', inplace=True)
# Convert the status to a category and order it
all_data["status"] = all_data["status"].astype("category")
all_data["status"].cat.set_categories(["gold", "silver", "bronze"], inplace=True)
return all_data
def save_results(sales_data, output):
""" Perform a summary of the data and save the data as an excel file
"""
summarized_sales = sales_data.groupby(["status"])["unit price"].agg([np.mean])
output_file = os.path.join(output, "sales-report.xlsx")
writer = pd.ExcelWriter(output_file, engine='xlsxwriter')
summarized_sales = summarized_sales.reset_index()
summarized_sales.to_excel(writer)
if __name__ == '__main__':
# maingui=main_args()
conf = parse_args()
print("Reading sales files")
sales_df = combine_files(conf.data_directory)
print("Reading customer data and combining with sales")
customer_status_sales = add_customer_status(sales_df, conf.cust_file)
print("Saving sales and customer summary data")
save_results(customer_status_sales, conf.output_directory)
print("Done")
In order to create a real custom interactive gui, I would advice you to get familier with a framwork such as mentioned here: https://wiki.python.org/moin/GuiProgramming
But in order to answer the specific question, Gooey has some little disadvantages, which make it difficult to span a new Gooey window from within the original window (I will not get into detail here), but what you can do is, spawn a new window as a subprocess. Minimal example below:
import sys
import os
from argparse import ArgumentParser
from subprocess import Popen, PIPE
from gooey import Gooey
from gooey import GooeyParser
#Gooey()
def main():
parser = GooeyParser(
description='''Test''')
required = parser.add_argument_group('Optional arguments')
parser.add_argument(
'-i', '--input',
required=False,
dest = 'input',
help='Test_file',
widget='FileChooser',
)
args = parser.parse_args()
########################################
# call the next gooey as subprocess from here
# should work on any system
########################################
PYTHON_PATH = sys.executable
process = Popen([PYTHON_PATH, 'spawn_next.py'], stdout=PIPE, stderr=PIPE)
output, error = process.communicate()
print(output)
print(error)
if __name__ == "__main__":
main()
Then you can call the file spawn_next.py, in which you can open another Goopy window like:
from gooey import Gooey
from gooey import GooeyParser
#Gooey()
def main():
parser = GooeyParser(
description='''Test''')
required = parser.add_argument_group('Optional arguments')
parser.add_argument(
'-i', '--input',
required=False,
dest = 'input',
help='Test_file',
widget='FileChooser',
)
args = parser.parse_args()
main()

Running a python.py file with datas

this code allows you to resize and view the file named "lena-gray.png" in the same folder.
from PIL import Image
jpgfile = Image.open("lena-gray.png")
high = input("Genişliğini giriniz : ")
wid = input("Yüksekliğini giriniz : ");
out = jpgfile.resize((int(high), int(wid)))
out.show()
But instead of entering these values step by step, I want to enter
$ python mywork.py lena-gray.png 50 100
So I want to run it without opening the file and see the result. Can this be done on Phyton? Can you help me?
----------------------------------------Edit----------------------------------
I updated my code like down.
import sys
from PIL import Image
firstarg = str(sys.argv[1])
secondarg = int(sys.argv[2])
thirdarg = int(sys.argv[3])
jpgfile = Image.open(firstarg)
yuks = secondarg
gen = thirdarg
out = jpgfile.resize((int(yuks), int(gen)))
out.show()
And my codes work!
You want to look into argparse.
It's the standard Python way to handle command line arguments.
EDIT: And if you want to check if the file is actually there or allow giving a list of files, look into glob.
EDIT2: This should do:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("filename", help="an image file", type=str)
parser.add_argument("width", help="display width", type=int)
parser.add_argument("height", help="display height", type=int)
clargs = parser.parse_args()
from PIL import Image
jpgfile = Image.open(clargs.filename)
out = jpgfile.resize(clargs.height, clargs.width)
out.show()
EDIT3: If you really want to do it according to the answer by #tendstoZero, this should be correct:
import sys
filename = sys.argv[1]
height = int(sys.argv[2])
width = int(sys.argv[3])
from PIL import Image
jpgfile = Image.open(filename)
out = jpgfile.resize(height, width)
out.show()
You shouldn't cast to int twice, and you shouldn't use meaningless intermediate variables.
import sys
firstarg=sys.argv[1]
secondarg=sys.argv[2]
thirdarg=sys.argv[3]
sys.argv[0] should be the script name .
You should be using something like this above snippet in your script.

Pytesser inaccurate

Simple question. When I run this image through pytesser, i get $+s. How can I fix that?
EDIT
So... my code generates images similar to the image linked above, just with different numbers, and is supposed to solve the simple math problem, which is obviously impossible if all I can get out of the picture is $+s
Here's the code I'm currently using:
from pytesser import *
time.sleep(2)
i = 0
operator = "+"
while i < 100:
time.sleep(.1);
img = ImageGrab.grab((349, 197, 349 + 452, 197 + 180))
equation = image_to_string(img)
Then I'm going to go on to parse equation... as soon as I get pytesser working.
Try my little function. I'm running tesseract from the svn repo, so my results might be more accurate.
I'm on Linux, so on Windows, I'd imagine that you'll have to replace tesseract with tesseract.exe to make it work.
import tempfile, subprocess
def ocr(image):
tempFile = tempfile.NamedTemporaryFile(delete = False)
process = subprocess.Popen(['tesseract', image, tempFile.name], stdout = subprocess.PIPE, stdin = subprocess.PIPE, stderr = subprocess.STDOUT)
process.communicate()
handle = open(tempFile.name + '.txt', 'r').read()
return handle
And a sample Python session:
>>> import tempfile, subprocess
>>> def ocr(image):
... tempFile = tempfile.NamedTemporaryFile(delete = False)
... process = subprocess.Popen(['tesseract', image, tempFile.name], stdout = subprocess.PIPE, stdin = subprocess.PIPE, stderr = subprocess.STDOUT)
... process.communicate()
... handle = open(tempFile.name + '.txt', 'r').read()
... return handle
...
>>> print ocr('326_fail.jpg')
0+1
if you're in linux, use gocr is more accurate. you can use it through
os.system("/usr/bin/gocr %s") % (sample_image)
and use readlines from stdout for manipulating output result to everything what you want (i.e creating output from gocr for specific variable).

Categories