Skip to content

Commit

Permalink
Merge 5c7b005 into 80e095b
Browse files Browse the repository at this point in the history
  • Loading branch information
veghp committed Sep 11, 2020
2 parents 80e095b + 5c7b005 commit 7cb2764
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 206 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Expand Up @@ -37,7 +37,7 @@ nosetests.xml
.pydevproject

# Temp files

.vscode/
*~

# Pipy codes
Expand Down
8 changes: 7 additions & 1 deletion .travis.yml
Expand Up @@ -3,6 +3,12 @@ python:
- "3.6"
# command to install dependencies
install:
- pip install coveralls pytest-cov==2.6 pytest==3.2.3
- pip install -e .
# command to run tests
script: pytest
script:
- python -m pytest -v --cov bioprinter --cov-report term-missing

after_success:
- coveralls

16 changes: 6 additions & 10 deletions LICENCE.txt
@@ -1,9 +1,6 @@
The MIT License (MIT)
[OSI Approved License]
MIT License

The MIT License (MIT)

Copyright (c) Edinburgh Genome Foundry 2015
Copyright (c) 2015 Edinburgh Genome Foundry

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand All @@ -12,14 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
48 changes: 30 additions & 18 deletions README.rst
@@ -1,33 +1,45 @@
.. raw:: html

<p align="center">
<img alt="Bioprinter logo" title="Bioprinter" src="https://raw.githubusercontent.com/Edinburgh-Genome-Foundry/bioprinter/master/docs/_static/images/title.png" width="550">
<br /><br />
</p>


BioPrinter
===========
==========

.. image:: https://travis-ci.org/Edinburgh-Genome-Foundry/bioprinter.svg?branch=master
:target: https://travis-ci.org/Edinburgh-Genome-Foundry/bioprinter
:alt: Travis CI build status (Python 3)

Bioprinter (webpage here_ ) is a Python module to produce living art. It transforms an image into files that a liquid dispenser can use to *print* the image to a plate using pigmented yeast or bacteria.
.. image:: https://coveralls.io/repos/github/Edinburgh-Genome-Foundry/bioprinter/badge.svg?branch=master
:target: https://coveralls.io/github/Edinburgh-Genome-Foundry/bioprinter?branch=master


Bioprinter (webpage here_) is a Python package for producing living art. It transforms an image into files that a liquid dispenser can use to *print* the image to a plate using pigmented yeast or bacteria.

Here are two examples of bio-art:

.. figure:: https://raw.githubusercontent.com/Edinburgh-Genome-Foundry/bioprinter/master/docs/_static/images/bioprint_dolly.jpeg
:align: center

Dolly drawn with baker yeast (white), violacein-producing yeast (black), and carotene-producing yeast (orange)
Dolly drawn with baker yeast (white), violacein-producing yeast (black), and carotene-producing yeast (orange).


.. figure:: https://raw.githubusercontent.com/Edinburgh-Genome-Foundry/bioprinter/master/docs/_static/images/bioprint_england.jpeg
:align: center

England flag drawn with 3 different strains of the bacterium *E. coli*.
British flag drawn with 3 different strains of the bacterium *E. coli*.

These bio-prints (and many others!) were presented at the University of Edinburgh's SynthSys Open days 2016 (strain selection and acoustic printing by Paulina Kanigowska).

Bioprinter is released on Github_ under the MIT licence (¢ Edinburgh Genome Foundry), everyone is welcome to contribute !
Bioprinter is released on Github_ under the MIT licence (Copyright 2015 Edinburgh Genome Foundry), everyone is welcome to contribute!

Bioprinter was written at the Edinburgh Genome Foundry by Zulko_ after an original idea and Matlab code by Mike Shen (`Mike's project on Github <https://github.com/mshen5/BioPointillism>`_).
Bioprinter was written at the Edinburgh Genome Foundry by Zulko_ after an original idea and Matlab code by Mike Shen (`project on Github <https://github.com/mshen5/BioPointillism>`_).

Installation
--------------

------------

If you have PIP installed: ::

Expand All @@ -39,32 +51,32 @@ Or unzip the source code in a directory and type in a terminal: ::


Usage
--------
-----

In the same folder as your code, place an image. It can have any resolution, but keep in mind that the width/height ratio of the plate it is printed on is 1.5. Make sure that a specific color is used to mark the un-pigmented background of the image, here we use blue:
The image file to be printed can have any resolution, but note that the width/height ratio of the plate the image is printed on is 1.5. Ensure that a specific color is used to mark the un-pigmented background of the image, here we use blue:

.. figure:: https://raw.githubusercontent.com/Edinburgh-Genome-Foundry/bioprinter/master/docs/_static/images/dolly.jpeg
:align: center

Then write the following code in ``dolly.py``:
Run the following code in Python or save in ``dolly.py``:

.. code:: python
from bioprinter import bioprint
bioprint(
image_filename="../docs/_static/images/dolly.jpeg",
image_filename="/path/to/image/dolly.jpeg",
output_filename="dolly.csv",
bg_color=[0, 0, 255], # blue background represents empty wells
pigments_wells={"A1": [0, 0, 0], # black yeast in source well A1
"A2": [250, 120, 10], # orange yeast in well A2
"A3": [255, 255, 255]}, # white yeast in well A3
bg_color=[0, 0, 255], # blue background represents empty wells
pigments_wells={"A1": [0, 0, 0], # black yeast in source well A1
"A2": [250, 120, 10], # orange yeast in well A2
"A3": [255, 255, 255]}, # white yeast in well A3
quantified_image_filename="dolly_preview.jpeg"
)
Execute in a terminal with ``python dolly.py``. This will produce a ``dolly.csv`` file as well as a preview image of the final printing (so that you can check if the image looks good at this low resolution).
The saved script can be executed in a terminal with ``python dolly.py``. This will produce a ``dolly.csv`` file as well as a preview image of the final printing (so that you can check if the image looks good at this low resolution).

Prepare a source plate with the right pigmented yeasts in wells A1, A2, A3, use an agar plate as the destination plate, and feed ``dolly.csv`` to the `Labcyte Echo <http://www.labcyte.com/products/liquidhandling/echo-555-liquid-handler>`_. Once the printing is finished, incubate 2 days at 30C (it would be one day at 37C for bacteria). Enjoy the result !
Prepare a source plate with the right pigmented yeasts in wells A1, A2, A3, use an agar plate as the destination plate, and feed ``dolly.csv`` to the `Labcyte Echo Acoustic Liquid Handler <https://www.labcyte.com/products/liquid-handling/echo-liquid-handlers>`_ (tested with Labcyte Echo 555 Series). Once the printing is finished, incubate 2 days at 30C (it would be one day at 37C for bacteria). Enjoy the result!


.. _here: http://edinburgh-genome-foundry.github.io/bioprinter/
Expand Down
1 change: 0 additions & 1 deletion bioprinter/__init__.py
Expand Up @@ -14,4 +14,3 @@
from .bioprinter import bioprint

from .version import __version__

76 changes: 43 additions & 33 deletions bioprinter/bioprinter.py
Expand Up @@ -16,6 +16,7 @@
import numpy as np
from PIL import Image


def _rownumber_to_rowname(num):
"""Return the row name corresponding to the row number.
Expand All @@ -24,22 +25,30 @@ def _rownumber_to_rowname(num):
if num < 26:
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[num]
else:
return "".join([_rownumber_to_rowname(int(num / 26) - 1),
_rownumber_to_rowname(num % 26)])
return "".join(
[_rownumber_to_rowname(int(num / 26) - 1), _rownumber_to_rowname(num % 26)]
)


def bioprint(image_filename, output_filename, bg_color, pigments_wells,
resolution=(192, 128), transfer_volume=2.5,
pigment_well_capacity=25000, transfer_rate=150,
quantified_image_filename=None):
def bioprint(
image_filename,
output_filename,
bg_color,
pigments_wells,
resolution=(192, 128),
transfer_volume=2.5,
pigment_well_capacity=25000,
transfer_rate=150,
quantified_image_filename=None,
):
"""Generate a CSV that can be used in the Echo to print the given picture.
Example
=======
# Let's print the EGF logo !
bioprint(
img_filename = "egf_logo.jpeg",
image_filename = "egf_logo.jpeg",
output_filename = "egf_logo.csv",
bg_color=[255,255,255], # The background of the image is white
pigments_wells= { "A1": [0,0,0], # black
Expand Down Expand Up @@ -82,7 +91,7 @@ def bioprint(image_filename, output_filename, bg_color, pigments_wells,
value on our Echo.
pigment_well_capacity
Volume in microliters that one pigments well can dispense. The
Volume in microliters that one pigment well can dispense. The
function raises an error if the total number of pixels for one
color exceeds the content of one pigment well, i.e. if
transfer_volume * number_pixels > well_capacity
Expand All @@ -94,13 +103,11 @@ def bioprint(image_filename, output_filename, bg_color, pigments_wells,
quantified_image_filename
If a path is provided, the quantified picture will be saved as an
image file under this name.
"""
pigments_wells, pigments_colors = zip(*pigments_wells.items())

# Constants of the problem
colors = np.vstack([np.array(bg_color),
np.array(pigments_colors)]).astype(float)
colors = np.vstack([np.array(bg_color), np.array(pigments_colors)]).astype(float)
resolution_w, resolution_h = resolution
resolution_ratio = 1.0 * resolution_w / resolution_h

Expand All @@ -125,13 +132,14 @@ def bioprint(image_filename, output_filename, bg_color, pigments_wells,
image = image.resize(image, new_size)
image = np.array(image)


# QUANTIFY THE ORIGINAL IMAGE WITH THE PROVIDED PIGMENTS COLORS

image_color_distances = np.dstack([
((1.0*image - color.reshape((1, 1, 3)))**2).sum(axis=2)
for color in colors
])
image_color_distances = np.dstack(
[
((1.0 * image - color.reshape((1, 1, 3))) ** 2).sum(axis=2)
for color in colors
]
)
# now image_color_distances[x,y,i] represents the distance between color
# i and the color of the image pixel at [x,y].
image_quantnumbers = image_color_distances.argmin(axis=2)
Expand All @@ -142,36 +150,38 @@ def bioprint(image_filename, output_filename, bg_color, pigments_wells,
counter = Counter(image_quantnumbers.flatten())
for color, count in counter.items():
if (color != 0) and (count > max_pixels_per_color):
err_message = ("Too much pixels of color #%d. " % (color) +
"Counted %d, max authorized %d" % (
count, max_pixels_per_color))
err_message = "Too much pixels of color #%d. " % (
color
) + "Counted %d, max authorized %d" % (count, max_pixels_per_color)
raise ValueError(err_message)

# WRITE THE CSV
# TO DO: write the wells in an order that miminizes the Echo's travels.
with open(output_filename, 'w') as csvfile:
writer = csv.writer(csvfile, delimiter=',')
writer.writerow([u'Source Well',
u'Destination Well',
u'Transfer Volume'])
with open(output_filename, "w") as csvfile:
writer = csv.writer(csvfile, delimiter=",")
writer.writerow([u"Source Well", u"Destination Well", u"Transfer Volume"])
for i, row in enumerate(image_quantnumbers):
for j, color in enumerate(row):
if color != 0:
writer.writerow([
pigments_wells[color-1], # source well
_rownumber_to_rowname(i) + str(j+1), # target "well"
transfer_volume])
writer.writerow(
[
pigments_wells[color - 1], # source well
_rownumber_to_rowname(i) + str(j + 1), # target "well"
transfer_volume,
]
)

# ESTIMATE THE PRINTING TIME

total_pixels = sum([count for (color, count) in counter.items()
if color > 0])
print("%d pixels will be printed in appr. %.1f minutes" % (
total_pixels, total_pixels / transfer_rate))
total_pixels = sum([count for (color, count) in counter.items() if color > 0])
print(
"%d pixels will be printed in appr. %.1f minutes"
% (total_pixels, total_pixels / transfer_rate)
)

# SAVE THE QUANTIFIED VERSION OF THE IMAGE IF A FILENAME IS PROVIDED

if quantified_image_filename is not None:
image_quantified = np.array([colors[y] for y in image_quantnumbers])
pil_image = Image.fromarray(image_quantified.astype('uint8'))
pil_image = Image.fromarray(image_quantified.astype("uint8"))
pil_image.save(quantified_image_filename)
2 changes: 1 addition & 1 deletion bioprinter/version.py
@@ -1 +1 @@
__version__ = "0.1.2"
__version__ = "0.1.3"

0 comments on commit 7cb2764

Please sign in to comment.