Skip to content

Commit

Permalink
latest commit
Browse files Browse the repository at this point in the history
  • Loading branch information
MikiSchikora committed Mar 23, 2023
1 parent ff1c6a0 commit 56f0b3d
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 16 deletions.
18 changes: 10 additions & 8 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,19 @@
parser.add_argument("--input", dest="input", required=False, default=None, type=str, help="A folder with the plate layout and the raw images to analyze. It should contain one subfolder (named after the plate batch) with the images of each 'plate_batch'.")

# optional arguments
parser.add_argument("--keep_tmp_files", dest="keep_tmp_files", required=False, default=False, action="store_true", help="Keep the intermediate files (forqca debugging).")
parser.add_argument("--min_nAUC_to_beConsideredGrowing", dest="min_nAUC_to_beConsideredGrowing", required=False, type=float, default=0.1, help="A float that indicates the minimum nAUC to be considered growing in susceptibility measures. This may depend on the experiment. This is added in the 'is_growing' field.")
parser.add_argument("--hours_experiment", dest="hours_experiment", required=False, type=float, default=24.0, help="A float that indicates the total experiment hours that are used to calculate the fitness estimates.")
parser.add_argument("--enhance_image_contrast", dest="enhance_image_contrast", required=False, type=str, default='True', help="True/False. Enhances contrast of images. Only for developers.")
parser.add_argument("--auto_accept", dest="auto_accept", required=False, default=False, action="store_true", help="Automatically accepts all the coordinates and bad spots. Only for developers.")

# developer args
parser.add_argument("--replace", dest="replace", required=False, default=False, action="store_true", help="Remove the --output folder to repeat any previously run processes.")
parser.add_argument("--reference_plate", dest="reference_plate", required=False, type=str, default=None, help="The plate to take as reference. It should be a plate with high growth in many spots. For example 'SC1-plate1' could be passed to this argument.")
parser.add_argument("--keep_tmp_files", dest="keep_tmp_files", required=False, default=False, action="store_true", help="Keep the intermediate files (for debugging). Only for developers.")
parser.add_argument("--replace", dest="replace", required=False, default=False, action="store_true", help="Remove the --output folder to repeat any previously run processes. Only for developers.")
parser.add_argument("--reference_plate", dest="reference_plate", required=False, type=str, default=None, help="The plate to take as reference. It should be a plate with high growth in many spots. For example 'SC1-plate1' could be passed to this argument. Only for developers.")
parser.add_argument("--break_after", dest="break_after", required=False, type=str, default=None, help="Break after some steps. Only for developers.")
parser.add_argument("--coords_1st_plate", dest="coords_1st_plate", required=False, default=False, action="store_true", help="Automatically transfers the coordinates of the 1st plate. Only for developers.")
parser.add_argument("--contrast_enhancement_image", dest="contrast_enhancement_image", required=False, type=str, default='auto', help="The plate to take as reference for contrast correction. It can be 'image_high_contrast' or 'auto'. Our testing suggests that 'auto' is better. Only for developers.")


# parse
opt = parser.parse_args()
Expand All @@ -69,8 +71,8 @@
fun.generate_analyze_images_window_mandatory()

# generate windows for boolean arguments
fun.generate_boolean_args_window(title='Keep temporary files\nat the end?', subtitle="(set 'Yes' to debug)", textVal_list=[(True, "Yes", "Arial"), (False, "No", "Arial bold")], opt_att="keep_tmp_files")
fun.generate_boolean_args_window(title='Skip manual verification of\ncoordinates and bad spots?', subtitle="(set 'Yes' at your own risk)", textVal_list=[(True, "Yes", "Arial"), (False, "No", "Arial bold")], opt_att="auto_accept")
# fun.generate_boolean_args_window(title='Keep temporary files\nat the end?', subtitle="(set 'Yes' to debug)", textVal_list=[(True, "Yes", "Arial"), (False, "No", "Arial bold")], opt_att="keep_tmp_files")
fun.generate_boolean_args_window(title='Skip manual verification of\ncoordinates and bad spots?', subtitle="(set 'Yes' at your own risk)", textVal_list=[(True, "Yes", "Arial"), (False, "No", "Arial")], opt_att="auto_accept")

# define the output and input
opt.output = "%s%soutput_%s"%(opt.output, fun.get_os_sep(), fun.pipeline_name)
Expand Down Expand Up @@ -99,6 +101,7 @@
opt.input = fun.get_fullpath(opt.input)
opt.output = fun.get_fullpath(opt.output)
if not os.path.isdir(opt.input): raise ValueError("The folder provided in --input does not exist")
if opt.contrast_enhancement_image not in {"image_high_contrast", "auto"}: raise ValueError("contrast_enhancement_image should be 'image_high_contrast' or 'auto'")

# replace
if opt.replace is True: fun.delete_folder(opt.output)
Expand All @@ -111,8 +114,6 @@

# print the cmd
arguments = " ".join(["--%s %s"%(arg_name, arg_val) for arg_name, arg_val in [("os", opt.os), ("input", opt.input), ("output", opt.output), ("docker_image", opt.docker_image), ("min_nAUC_to_beConsideredGrowing", opt.min_nAUC_to_beConsideredGrowing), ("hours_experiment", opt.hours_experiment), ("enhance_image_contrast", opt.enhance_image_contrast)]])

if opt.keep_tmp_files is True: arguments += " --keep_tmp_files"
if opt.auto_accept is True: arguments += " --auto_accept"

full_command = "%s %s%smain.py %s"%(sys.executable, pipeline_dir, os_sep, arguments)
Expand All @@ -122,6 +123,7 @@
fun.print_with_runtime("Trying to run docker image. If this fails it may be because either the image is not in your system or docker is not properly initialized.")
fun.run_cmd('docker run -it --rm %s bash -c "sleep 1"'%(opt.docker_image))


#############################

######### GENERATE THE DOCKER CMD AND RUN #################
Expand Down Expand Up @@ -156,7 +158,7 @@
fun.delete_folder(tmp_input_dir); fun.make_folder(tmp_input_dir)

# init command with general features
docker_cmd = 'docker run --rm -it -e hours_experiment=%s -e KEEP_TMP_FILES=%s -e min_nAUC_to_beConsideredGrowing=%s -e enhance_image_contrast=%s -e reference_plate=%s -v "%s":/small_inputs -v "%s":/output -v "%s":/images'%(opt.hours_experiment, opt.keep_tmp_files, opt.min_nAUC_to_beConsideredGrowing, opt.enhance_image_contrast, str(opt.reference_plate), tmp_input_dir, opt.output, opt.input)
docker_cmd = 'docker run --rm -it -e contrast_enhancement_image=%s -e hours_experiment=%s -e KEEP_TMP_FILES=%s -e min_nAUC_to_beConsideredGrowing=%s -e enhance_image_contrast=%s -e reference_plate=%s -v "%s":/small_inputs -v "%s":/output -v "%s":/images'%(opt.contrast_enhancement_image, opt.hours_experiment, opt.keep_tmp_files, opt.min_nAUC_to_beConsideredGrowing, opt.enhance_image_contrast, str(opt.reference_plate), tmp_input_dir, opt.output, opt.input)

# add the scripts from outside
docker_cmd += ' -v "%s%sscripts":/workdir_app/scripts'%(pipeline_dir, fun.get_os_sep())
Expand Down
53 changes: 46 additions & 7 deletions scripts/app_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2699,13 +2699,35 @@ def get_contrast_for_image(filename):

"""Gets the contrast for the image"""

# load image and get stat
stat = ImageStat.Stat(PIL_Image.open(filename))
# load image and get RMS, a good wasy to measure contrast
contrast_value = ImageStat.Stat(PIL_Image.open(filename)).rms[0]

# returrn contrast
return stat.mean[0]
return contrast_value

def run_analyze_images_process_images(plate_layout_file, images_dir, outdir, enhance_image_contrast, reference_plate):
def generate_auto_image_high_contrast(filename, ref_image, square_size=100):

"""Generates a image with high contrast"""

if file_is_empty(filename):

# get size
width, height = PIL_Image.open(ref_image).size

# Create a new image with the specified size and gray background
image = PIL_Image.new('RGB', (width, height), color='gray')

# Iterate over the pixels in the image and set their color to black or white based on their position
for x in range(width):
for y in range(height):
if (x // square_size) % 2 == (y // square_size) % 2: # the higher the 100, the bigger the squares
image.putpixel((x, y), (0, 0, 0))
# save
filename_tmp = "%s.tmp.tif"%filename
image.save(filename_tmp)
os.rename(filename_tmp, filename)

def run_analyze_images_process_images(plate_layout_file, images_dir, outdir, enhance_image_contrast, reference_plate, contrast_enhancement_image):

"""Takes the images and generates processed images that are cropped to be one in each plate"""

Expand Down Expand Up @@ -2817,13 +2839,30 @@ def run_analyze_images_process_images(plate_layout_file, images_dir, outdir, enh
plate_batch_to_raw_outdir = get_images_with_enhanced_contrast_all_images_concatenated(tmpdir, plate_batch_to_raw_outdir, plate_batch_to_images, image_ending) # this is not efficient because it does not scale
"""

# define the image with the highest contrast for reference
# amongst the images you have get the one with the highest cotrast
all_images = sorted(make_flat_listOflists([["%s/%s"%(plate_batch_to_raw_outdir[pb], img) for img in images] for pb, images in plate_batch_to_images.items()]))
image_to_contrast = pd.Series(dict(zip(all_images, map(get_contrast_for_image, all_images))))
image_highest_contrast = image_to_contrast.sort_values().index[-1]
real_image_highest_contrast = image_to_contrast.sort_values().index[-1]

# define the image of contrast for reference
if contrast_enhancement_image=="image_high_contrast":
image_high_contrast = real_image_highest_contrast
print("Using image with highest contrast (%s) as reference for contrast enhancement..."%("/".join(image_high_contrast.split("/")[-2:])))

elif contrast_enhancement_image=="auto":
image_high_contrast = "%s/black_white_image_high_contrast.tif"%tmpdir
generate_auto_image_high_contrast(image_high_contrast, "%s/%s"%(plate_batch_to_raw_outdir[plate_batch], plate_batch_to_images[plate_batch][0]), square_size=100)
print("Using automatic high-contrast image as reference for contrast enhancement...")

else: raise ValueError("invalid contrast_enhancement_image: %s"%contrast_enhancement_image)

# check that contrast correction is reasonable
if enhance_image_contrast is True and get_contrast_for_image(real_image_highest_contrast)>get_contrast_for_image(image_high_contrast): raise ValueError("The image with highest contrast has a higher contrast value (RMS=%.2f) than the image used as reference for contrast correction (RMS=%.2f). This is not allowed because it may bias the data. This likely means that your images have high contrast, so that you can run with enhance_image_contrast:False."%(get_contrast_for_image(real_image_highest_contrast), get_contrast_for_image(image_high_contrast)))


# rotate each plate set at the same time (not in parallel). Also increase contrast.
for I, plate_batch in enumerate(sorted(plate_batch_to_images)): process_image_rotation_all_images_batch(I+1, len(plate_batch_to_raw_outdir),plate_batch_to_raw_outdir[plate_batch], plate_batch_to_processed_outdir[plate_batch], plate_batch, plate_batch_to_images[plate_batch], image_ending, enhance_image_contrast, image_highest_contrast)
for I, plate_batch in enumerate(sorted(plate_batch_to_images)): process_image_rotation_all_images_batch(I+1, len(plate_batch_to_raw_outdir),plate_batch_to_raw_outdir[plate_batch], plate_batch_to_processed_outdir[plate_batch], plate_batch, plate_batch_to_images[plate_batch], image_ending, enhance_image_contrast, image_high_contrast)


# log
#print_with_runtime("Rotating images and Improving contrast took %.3f seconds"%(time.time()-start_time_rotation_contrast))
Expand Down
2 changes: 1 addition & 1 deletion scripts/run_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
else: raise ValueError("The argument passed to --reference_plate (%s) should have the format <plate_batch>-plate<plateID>. For example 'SC1-plate1'."%reference_plate)

# process images
if os.environ["MODULE"]=="analyze_images_process_images": fun.run_analyze_images_process_images("%s/plate_layout.xlsx"%SmallInputs, ImagesDir, OutDir, bool_dict[str(os.environ["enhance_image_contrast"])], reference_plate)
if os.environ["MODULE"]=="analyze_images_process_images": fun.run_analyze_images_process_images("%s/plate_layout.xlsx"%SmallInputs, ImagesDir, OutDir, bool_dict[str(os.environ["enhance_image_contrast"])], reference_plate, str(os.environ["contrast_enhancement_image"]))

# perform growth measurements for one image
elif os.environ["MODULE"]=="analyze_images_run_colonyzer_subset_images": fun.run_analyze_images_run_colonyzer_subset_images(OutDir, reference_plate)
Expand Down

0 comments on commit 56f0b3d

Please sign in to comment.