Skip to content

Commit

Permalink
Merge branch 'release-0.2.0'
Browse files Browse the repository at this point in the history
New features:
1. Set default colors for elemental mappings
2. Plot all elements in the  overlayed mapping by default
  • Loading branch information
blleng committed Sep 9, 2024
2 parents fbccd6f + bcfa289 commit 9b75b1d
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 122 deletions.
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This project is based on [RosettaSciIO](https://github.com/hyperspy/rosettasciio

## Install

Environment requirenments: `Python >= 3.8`
Environment requirements: `Python >= 3.8`

With pip:

Expand Down Expand Up @@ -77,22 +77,26 @@ NOTICE: Three arguments are required to specify the position and width of scale
#### Color of Elements
The `-e`/`--eds` option allow users to specify the color of elemental mappings (default: gray).
Default colors of elemental mapppings are corresponding to the following list in sequnce (*Matplotlib* default colors):
'<font color=#1f77b4>#1f77b4</font>', '<font color=#ff7f0e>#ff7f0e</font>', '<font color=#2ca02c>#2ca02c</font>', '<font color=#d62728>#d62728</font>', '<font color=#9467bd>#9467bd</font>', '<font color=#8c564b>#8c564b</font>', '<font color=#e377c2>#e377c2</font>', <font color=7f7f7f>7f7f7f</font>', '<font color=#bcbd22>#bcbd22</font>', '<font color=#17becf>#17becf</font>'
Convert-EMD provides `-e`/`--eds` option for users to customize the color of elemental mappings.
```bash
cemd -f INPUT_FILE -e ELEMENT_1 COLOR_1 ELEMENT_2 COLOR_2 ELEMENT_3 COLOR_3 ...
```
NOTICE: You don't need to specify all elemental colors, those undefined ones will be set according to the default color list.
#### Overlayed Mapping
The `-oe`/`--overlay` option decides which elements are overlyed.
The `-oe`/`--overlay` option decides which elements are overlyed (default: all).
```bash
cemd -f INPUT_FILE -oe ELEMENT_1 ElEMENT_2 ...
```
NOTICE: The colors in the overlayed mapping are corresponding to the color specified by the `-e` option.
Moreover, `-oa`/`--overlay_alpha` and `-sa`/`--substrate_alpha` options are provided to adjust the transparency of elemental layers (default: 1.0) and the HAADF layer (default: 0.5) respectively. The argument should be a float number between 0 and 1, 0 means totally transparent.
### Contrast (Histogram Equalization)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "convert-emd"
version = "0.1.1"
version = "0.2.0"
dependencies = ["rosettasciio[all]>=0.6"]
authors = [
{ name="Bing-Liang Leng", email="leng.bl@sjtu.edu.cn" },
Expand Down
131 changes: 15 additions & 116 deletions src/convert_emd/cemd.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
import os
import argparse
from rsciio.emd import file_reader
from skimage import exposure
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
import convert_emd.command as com
import convert_emd.function as emdfun
import convert_emd.drawing as draw

def main():
parser = argparse.ArgumentParser(description = "Convert .emd file generated by Velox to images")
parser.add_argument("-f", "--file", type = str, metavar = "FILE", help = "Input *.emd(Velox) filename (without \".emd\" extension)", required=True)
parser.add_argument("-o", "--out", type = str, metavar = "TYPE", help = "Type of output images.", default = "png")
parser.add_argument("-ns", "--no_scale", help = "Do not draw scale bar", action = "store_true")
parser.add_argument("-sc", "--scale_color", type = str, metavar = "COLOR", help = "Color of scale bar", default = "#ffffff")
parser.add_argument("-s", "--scale", type = float, nargs = 3, metavar = "FLOAT", help = "The position and width of scale bar (X_POSITION Y_POSITION WIDTH_FACTOR), the width is set as image-height/WIDTH_FACTOR", default = [0.75, 0.9167, 150.0])
parser.add_argument("-e", "--eds", type = str, nargs = "+", metavar = "Str", help = "The color of elemental mappings (default: gray)", default = [])
parser.add_argument("-oe", "--overlay", type = str, nargs = "+", metavar = "ELEMENT", help = "The elements for overlayed mapping", default = [])
parser.add_argument("-oa", "--overlay_alpha", type = float, metavar = "ALPHA", help = "Transparency of the overlayed elemental mapping (a value between 0 and 1, 0 means totally transparent)", default = "1.0")
parser.add_argument("-sa", "--substrate_alpha", type = float, metavar = "ALPHA", help = "Transparency of the HAADF substrate picture in elemental mapping (a value between 0 and 1, 0 means totally transparent).", default = "0.5")
parser.add_argument("-c", "--contrast_stretching", type = float, nargs = 2, metavar = "CONTRAST", help = "The parameter for contrast streaching, where the image is rescaled to include all intensities that fall within the given percentiles", default = [1, 99])
args = parser.parse_args()
args = com.parse().parse_args()

file = args.file
file_name = args.file
data = emdfun.get_data(file_name)
eds = emdfun.is_eds(data)
output_type = "." + args.out

if args.no_scale:
Expand All @@ -41,99 +28,11 @@ def main():
ele = i * 2
ele_color = ele + 1
eds_color[args.eds[ele]] = args.eds[ele_color]
mapping_overlay = args.overlay

convert_emd(file, output_type, scale_bar, sb_color, sb_x_start, sb_y_start, sb_width_factor, stretch, overlay_alpha, sub_alpha, eds_color, mapping_overlay)

def transparent_single_color_cmap(color):
return mcolors.LinearSegmentedColormap.from_list(
"", [mcolors.to_rgba(color, 0), mcolors.to_rgba(color, 1)]
)

def draw_scale_bar(frame, size_x, size_y, sb_x_start, sb_y_start, width_factor, sb_color):
sb_lst = [0.1,0.2,0.5,1,2,5,10,20,50,100,200,500,1000,2000,5000]
scale = frame["axes"][1]["scale"]
unit = frame["axes"][1]["units"]
sb_len_float = size_x * scale / 6
sb_len = sorted(sb_lst, key=lambda a: abs(a - sb_len_float))[0]
sb_len_px = sb_len / scale
sb_start_x, sb_start_y, sb_width = (size_x * sb_x_start , size_y * sb_y_start, size_y / width_factor)
return [plt.Rectangle((sb_start_x, sb_start_y), sb_len_px, sb_width, color=sb_color, fill=True), "_" + str(sb_len) + unit]


def convert_emd(file, output_type, scale_bar, sb_color, sb_x_start, sb_y_start, sb_width_factor, stretch, overlay_alpha, sub_alpha, eds_color, mapping_overlay):
output_dir = file + "/"
output_name = output_dir + file + "_"
data = file_reader(file + ".emd")

if not os.path.exists(output_dir):
os.makedirs(output_dir)
mapping_frame = []
overlay = False

for i in range(len(data)):
frame = data[i]
dim = frame["data"].ndim
title = frame["metadata"]["General"]["title"]

if dim == 2:
low_constrain, high_constrain = np.percentile(frame["data"], (stretch[0], stretch[1]))
frame["data"] = exposure.rescale_intensity(frame["data"], in_range=(low_constrain, high_constrain))

cmp = "gray"
if title in eds_color:
cmp = transparent_single_color_cmap(eds_color.get(title))
if title in mapping_overlay:
mapping_frame.append(i)
overlay = True
elif overlay and title == "HAADF":
mapping_frame.append(i)
HADDF_frame_num = len(mapping_frame) - 1

size_x, size_y = (frame["axes"][1]["size"], frame["axes"][0]["size"])
plt.figure(figsize=(size_x/100, size_y/100), facecolor="black")
ax = plt.gca()
plt.imshow(frame["data"], cmap=cmp)

if scale_bar == True:
bar = draw_scale_bar(frame, size_x, size_y, sb_x_start, sb_y_start, sb_width_factor, sb_color)
ax.add_patch(bar[0])
sb_text = bar[1]

plt.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0)
plt.margins = (0, 0)
plt.axis("off")
if scale_bar == True:
plt.savefig(output_name + title + "_" + str(i) + sb_text + output_type)
else:
plt.savefig(output_name + title + "_" + str(i) + output_type)
plt.close()

if overlay:
element = ""
HAADF_frame = data[mapping_frame[HADDF_frame_num]]
size_x, size_y = (HAADF_frame["axes"][1]["size"], HAADF_frame["axes"][0]["size"])

plt.figure(figsize=(size_x/100, size_y/100), facecolor="black")
ax = plt.gca()
plt.imshow(HAADF_frame["data"], cmap="gray", alpha=sub_alpha)
for i in range(len(mapping_frame)):
if i == HADDF_frame_num:
continue
title = data[mapping_frame[i]]["metadata"]["General"]["title"]
plt.imshow(data[mapping_frame[i]]["data"], cmap=transparent_single_color_cmap(eds_color.get(title)), alpha=overlay_alpha)
element = element + "_" + title

if scale_bar == True:
bar = draw_scale_bar(HAADF_frame, size_x, size_y, sb_x_start, sb_y_start, sb_width_factor, sb_color)
ax.add_patch(bar[0])
sb_text = bar[1]

plt.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0)
plt.margins = (0, 0)
plt.axis("off")
if scale_bar == True:
plt.savefig(output_name + "Overlay" + element + sb_text + output_type)
else:
plt.savefig(output_name + "Overlay" + element + output_type)
plt.close()
all_elements = emdfun.eds_elements(data)
if eds and len(all_elements):
mapping_overlay = args.overlay if len(args.overlay) else all_elements
overlay = True
else:
overlay = False

draw.convert_emd(file_name, data, output_type, scale_bar, sb_color, sb_x_start, sb_y_start, sb_width_factor, stretch, overlay_alpha, sub_alpha, eds_color, mapping_overlay, overlay)
89 changes: 89 additions & 0 deletions src/convert_emd/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import argparse

def parse():
parser = argparse.ArgumentParser(description = "Convert .emd file generated by Velox to images")

parser.add_argument (
"-f",
"--file",
type = str,
metavar = "FILE",
help = "Input *.emd(Velox) filename (without \".emd\" extension)",
required=True
)
parser.add_argument (
"-o",
"--out",
type = str,
metavar = "TYPE",
help = "Type of output images.",
default = "png"
)
parser.add_argument (
"-ns",
"--no_scale",
help = "Do not draw scale bar",
action = "store_true"
)
parser.add_argument (
"-sc",
"--scale_color",
type = str,
metavar = "COLOR",
help = "Color of scale bar",
default = "#ffffff"
)
parser.add_argument (
"-s",
"--scale",
type = float,
nargs = 3,
metavar = "FLOAT",
help = "The position and width of scale bar (X_POSITION Y_POSITION WIDTH_FACTOR), the width is set as image-height/WIDTH_FACTOR",
default = [0.75, 0.9167, 150.0]
)
parser.add_argument (
"-e",
"--eds",
type = str,
nargs = "+",
metavar = "Str",
help = "The color of elemental mappings (default: gray)",
default = []
)
parser.add_argument (
"-oe",
"--overlay",
type = str,
nargs = "+",
metavar = "ELEMENT",
help = "The elements for overlayed mapping",
default = []
)
parser.add_argument (
"-oa",
"--overlay_alpha",
type = float,
metavar = "ALPHA",
help = "Transparency of the overlayed elemental mapping (a value between 0 and 1, 0 means totally transparent)",
default = "1.0"
)
parser.add_argument (
"-sa",
"--substrate_alpha",
type = float,
metavar = "ALPHA",
help = "Transparency of the HAADF substrate picture in elemental mapping (a value between 0 and 1, 0 means totally transparent)",
default = "0.5"
)
parser.add_argument (
"-c",
"--contrast_stretching",
type = float,
nargs = 2,
metavar = "CONTRAST",
help = "The parameter for contrast streaching, where the image is rescaled to include all intensities that fall within the given percentiles",
default = [1, 99]
)

return parser
91 changes: 91 additions & 0 deletions src/convert_emd/drawing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import os
import matplotlib.pyplot as plt
import convert_emd.function as emdfun

def draw_scale_bar(frame, size_x, size_y, sb_x_start, sb_y_start, width_factor, sb_color):
sb_lst = [0.1,0.2,0.5,1,2,5,10,20,50,100,200,500,1000,2000,5000]
scale, unit = emdfun.get_scale(frame)
sb_len_float = size_x * scale / 6
sb_len = sorted(sb_lst, key=lambda a: abs(a - sb_len_float))[0]
sb_len_px = sb_len / scale
sb_start_x, sb_start_y, sb_width = (size_x * sb_x_start , size_y * sb_y_start, size_y / width_factor)
return [plt.Rectangle((sb_start_x, sb_start_y), sb_len_px, sb_width, color=sb_color, fill=True), "_" + str(sb_len) + unit]

def convert_emd(file_name, data, output_type, scale_bar, sb_color, sb_x_start, sb_y_start, sb_width_factor, stretch, overlay_alpha, sub_alpha, eds_color, mapping_overlay, overlay):
output_dir = file_name + "/"
output_name = output_dir + file_name + "_"

if not os.path.exists(output_dir):
os.makedirs(output_dir)
mapping_frame = []
ele = 0
default_colors = emdfun.default_colors()

for i in range(len(data)):
frame = data[i]
dim = frame["data"].ndim
title = emdfun.get_title(frame)

if dim == 2:
frame["data"] = emdfun.contrast_stretch(frame, stretch)
cmp = "gray"
if overlay and not emdfun.is_eds_spectrum(frame):
if title in mapping_overlay: mapping_frame.append(i)
if title in eds_color:
cmp = emdfun.create_cmp(eds_color[title])
elif title == "HAADF":
mapping_frame.append(i)
HAADF_frame_num = len(mapping_frame) - 1
else:
cmp = emdfun.create_cmp(default_colors[ele])
eds_color[title] = default_colors[ele]
ele += 1
if ele > 9: ele = 0

size_x, size_y = (frame["axes"][1]["size"], frame["axes"][0]["size"])
plt.figure(figsize=(size_x/100, size_y/100), facecolor="black")
ax = plt.gca()
plt.imshow(frame["data"], cmap=cmp)

if scale_bar == True:
bar = draw_scale_bar(frame, size_x, size_y, sb_x_start, sb_y_start, sb_width_factor, sb_color)
ax.add_patch(bar[0])
sb_text = bar[1]

plt.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0)
plt.margins = (0, 0)
plt.axis("off")
if scale_bar == True:
plt.savefig(output_name + title + "_" + str(i) + sb_text + output_type)
else:
plt.savefig(output_name + title + "_" + str(i) + output_type)
plt.close()

if overlay:
element = ""
HAADF_frame = data[mapping_frame[HAADF_frame_num]]
size_x, size_y = (HAADF_frame["axes"][1]["size"], HAADF_frame["axes"][0]["size"])

plt.figure(figsize=(size_x/100, size_y/100), facecolor="black")
ax = plt.gca()
plt.imshow(HAADF_frame["data"], cmap="gray", alpha=sub_alpha)
for i in range(len(mapping_frame)):
if i == HAADF_frame_num:
continue
title = emdfun.get_title(data[mapping_frame[i]])
plt.imshow(data[mapping_frame[i]]["data"], cmap = emdfun.create_cmp(eds_color[title]), alpha = overlay_alpha)
element = element + "_" + title

if scale_bar == True:
bar = draw_scale_bar(HAADF_frame, size_x, size_y, sb_x_start, sb_y_start, sb_width_factor, sb_color)
ax.add_patch(bar[0])
sb_text = bar[1]

plt.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0)
plt.margins = (0, 0)
plt.axis("off")
if scale_bar == True:
plt.savefig(output_name + "Overlay" + element + sb_text + output_type)
else:
plt.savefig(output_name + "Overlay" + element + output_type)
plt.close()
Loading

0 comments on commit 9b75b1d

Please sign in to comment.