diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 2e20ba6..e175167 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -49,17 +49,21 @@ To set up `img2cmap` for local development: Now you can make your changes locally. -4. When you're done making changes run all the checks and docs builder with `tox `_ one command:: +4. Install development requirements:: + + pip install img2cmap[dev] + +5. When you're done making changes run all the checks and docs builder with `tox `_ one command:: tox -5. Commit your changes and push your branch to GitHub:: +6. Commit your changes and push your branch to GitHub:: git add . git commit -m "Your detailed description of your changes." git push origin name-of-your-bugfix-or-feature -6. Submit a pull request through the GitHub website. +7. Submit a pull request through the GitHub website. Pull Request Guidelines ----------------------- diff --git a/Dockerfile b/Dockerfile index 9005f8f..2b631e8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,9 +6,11 @@ EXPOSE 8501 WORKDIR /app COPY . /app +RUN apt-get update \ + && apt-get install --yes --no-install-recommends \ + gcc g++ libffi-dev RUN pip install --upgrade pip -RUN pip install streamlit -RUN pip install -e . +RUN pip install .[streamlit] ENTRYPOINT [ "streamlit", "run"] CMD ["/app/streamlit/app.py"] diff --git a/README.rst b/README.rst index e256a32..3c51440 100644 --- a/README.rst +++ b/README.rst @@ -5,9 +5,6 @@ img2cmap Usage ===== -Basic ------ - **Create colormaps from images in three lines of code!** | First, ``ImageConverter`` class converts images to arrays of RGB values. @@ -157,7 +154,7 @@ of the image. hexcodes ^^^^^^^^ -When running the ``generate_cmap`` or the ``generate_optimal_cmap`` methods the ImageConverter object will automatically +When running the ``generate_cmap`` or the ``generate_optimal_cmap`` methods the ImageConverter object will automatically capture the resulting hexcodes from the colormap and store them as an attribute. .. code-block:: python3 @@ -196,6 +193,14 @@ Documentation https://img2cmap.readthedocs.io/ +Web App +======= + +Check out the web app at https://img2cmap.fly.dev + +.. image:: images/webapp_image.png + :align: center + Status ====== @@ -248,6 +253,12 @@ Status Development =========== +Install the development requirements: + +:: + + pip install img2cmap[dev] + To run all the tests run:: tox diff --git a/images/webapp_image.png b/images/webapp_image.png new file mode 100644 index 0000000..7d8f99f Binary files /dev/null and b/images/webapp_image.png differ diff --git a/setup.py b/setup.py index 636e6da..56d1166 100755 --- a/setup.py +++ b/setup.py @@ -73,9 +73,8 @@ def read(*names, **kwargs): "kneed", ], extras_require={ - "dev": ["black", "requests"], - # eg: - # 'rst': ['docutils>=0.11'], - # ':python_version=="2.6"': ['argparse'], + "dev": ["black", "requests", "tox"], + "streamlit": ["streamlit", "st-annotated-text"], + "all": ["black", "requests", "tox", "streamlit", "st-annotated-text"], }, ) diff --git a/streamlit/app.py b/streamlit/app.py index 6ca1f6c..33b4454 100644 --- a/streamlit/app.py +++ b/streamlit/app.py @@ -5,12 +5,33 @@ import matplotlib.patches as patches import matplotlib.pyplot as plt import numpy as np +from annotated_text import annotated_text from mpl_toolkits.axes_grid1 import make_axes_locatable import streamlit as st from img2cmap import ImageConverter +def colorpicker(color): + """Logic to decide between black or white text for a given background color. + https://stackoverflow.com/a/3943023/4541548 + """ + red, green, blue = mpl.colors.to_rgb(color) + newrgb = [] + for c in red, green, blue: + c = c / 255.0 + if c <= 0.04045: + newrgb.append(c / 12.92) + else: + newrgb.append(((c + 0.055) / 1.055) ^ 2.4) + L = 0.2126 * newrgb[0] + 0.7152 * newrgb[1] + 0.0722 * newrgb[2] + # why did I have to use 179 instead of 0.179? + if L > 0.179 / 1000: + return "#000000" + else: + return "#ffffff" + + # @profile def main(): warnings.filterwarnings("ignore") @@ -38,7 +59,9 @@ def main(): if user_image is not None: user_image = BytesIO(user_image.getvalue()) elif file_or_url == "url": - user_image = st.sidebar.text_input("Paste an image URL", "https://static1.bigstockphoto.com/3/2/3/large1500/323952496.jpg") + user_image = st.sidebar.text_input( + "Paste an image URL", "https://static1.bigstockphoto.com/3/2/3/large1500/323952496.jpg" + ) else: st.warning("Please select an option") @@ -85,21 +108,26 @@ def get_image_converter(user_image, remove_transparent): cb.set_ticks([]) st.pyplot(fig1) - st.caption( - "The original image has been resized to a smaller size, if you want to see " - "the colormap for the full size image, use the Python package." - ) - colors1 = [mpl.colors.rgb2hex(c) for c in cmap.colors] - st.text("Hex Codes (click to copy on far right)") + + # determine whether to show the text in white or black + bw_mask = [colorpicker(c) for c in colors1] + + st.header("Hex Codes") + annotated_text(*[(hexcode, "", hexcode, text_color) for hexcode, text_color in zip(colors1, bw_mask)]) st.code(colors1) + st.caption("Click copy button on far right to copy hex codes to clipboard.") st.header("Detect optimal number of colors") - max_colors = st.number_input("Max number of colors in cmap (more colors = longer runtime)", min_value=2, max_value=20, value=10) + max_colors = st.number_input( + "Max number of colors in cmap (more colors = longer runtime)", min_value=2, max_value=20, value=10 + ) optimize = st.button("Optimize") if optimize: with st.spinner("Optimizing... (this can take up to a minute)"): - cmaps, best_n_colors, ssd = converter.generate_optimal_cmap(max_colors=max_colors, palette_name="", random_state=random_state) + cmaps, best_n_colors, ssd = converter.generate_optimal_cmap( + max_colors=max_colors, palette_name="", random_state=random_state + ) figopt, ax = plt.subplots(figsize=(7, 5)) @@ -124,7 +152,9 @@ def get_image_converter(user_image, remove_transparent): ax.set_xticks([]) # best - rect = patches.Rectangle((0, best_n_colors), ymax, 1, linewidth=1, facecolor="none", edgecolor="black", linestyle="--") + rect = patches.Rectangle( + (0, best_n_colors), ymax, 1, linewidth=1, facecolor="none", edgecolor="black", linestyle="--" + ) ax.add_patch(rect) # minus 2, one for starting at 2 and one for 0-indexing