In [3]:
# invisible
import matplotlib as mpl
mpl.rcParams['figure.dpi'] = 300



Bildverarbeitung

Einführung

Charlie Chaplin, verändert mit Python, NumPy und Matplotlib

Es war noch nie einfacher, ein Bild aufzunehmen als heute. Alles was Sie normalerweise brauchen ist ein Handy. Dies sind die Grundvoraussetzungen, um ein Bild aufzunehmen und anzusehen. Das Fotografieren ist kostenlos, wenn wir die Kosten für das Mobiltelefon nicht berücksichtigen, das ohnehin oft für andere Zwecke gekauft wird. Vor einer Generation brauchten Amateur- und echte Künstler spezielle und oft teure Ausrüstung, und die Kosten pro Bild waren alles andere als kostenlos.

Wir machen Fotos, um großartige Momente unseres Lebens in der Zeit zu konservieren. "Eingelegte Erinnerungen", die bereit sind, in Zukunft nach Belieben "geöffnet" zu werden.

Ähnlich wie beim Einmachen müssen wir die richtigen Konservierungsmittel verwenden. Natürlich bietet uns das Mobiltelefon auch eine Reihe von Bildverarbeitungssoftware, aber sobald wir eine große Anzahl von Fotos verarbeiten müssen, benötigen wir andere Tools. Hier kommen Programmierung und Python ins Spiel. Python und seine Module wie Numpy, Scipy, Matplotlib und andere Spezialmodule bieten die optimale Funktionalität, um mit der Flut von Bildern fertig zu werden.

Um Ihnen das notwendige Wissen zu vermitteln, befasst sich dieses Kapitel unseres Python-Tutorials mit der grundlegenden Bildverarbeitung und -manipulation. Zu diesem Zweck verwenden wir die Module NumPy, Matplotlib und SciPy.

Wir beginnen mit dem scipy Paket misc. Die Hilfedatei besagt, dass scipy.misc verschiedene Dinge enthalte, die man sonstwo nicht unterbringen konnte. So enthält es auch beispielsweise ein paar Bilder, wie das folgende:

In [1]:
from scipy import misc
import matplotlib.pyplot as plt

ascent = misc.ascent()
plt.gray()
plt.imshow(ascent)
plt.show()
python_bildverarbeitung: Graph 0

Zusätzlich zum Bild erkennen wir, dass die Ticks an den Achsen ebenfalls ausgegeben werden. Das ist sicherlich interessant, wenn Sie Informationen zur Größe und Pixel-Position benötigen, jedoch möchte man in den meisten Fällen das Bild ohne diese Informationen sehen. Indem wir den Befehl plt.axis("off") hinzufügen, können wir die Ticks und Achsen ausblenden:

In [2]:
from scipy import misc

ascent = misc.ascent()
import matplotlib.pyplot as plt
plt.axis("off") # removes the axis and the ticks
plt.gray()
plt.imshow(ascent)
plt.show()
python_bildverarbeitung 2: Graph 1

Wir sehen, dass der Typ des Bildes ein Integer-Array ist:

In [4]:
ascent.dtype
Out[4]:
dtype('int64')

Wir können auch die Größe des Bildes prüfen:

In [5]:
ascent.shape
Out[5]:
(512, 512)

Das misc-Packet beinhaltet auch ein Bild eines Waschbären:

In [6]:
import scipy.misc
face = scipy.misc.face()
print(face.shape)
print(face.max)
print(face.dtype)
plt.axis("off")
plt.gray()
plt.imshow(face)
plt.show()
(768, 1024, 3)
<built-in method max of numpy.ndarray object at 0x7fd05e484f30>
uint8
python_bildverarbeitung 3: Graph 2
In [9]:
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

Matplotlib unterstützt nur PNG-Bilder.

In [10]:
img = mpimg.imread('frankfurt.png')
In [11]:
print(img[:3])
[[[0.4117647  0.5686275  0.8       ]
  [0.40392157 0.56078434 0.7921569 ]
  [0.40392157 0.5686275  0.79607844]
  ...
  [0.48235294 0.62352943 0.81960785]
  [0.47843137 0.627451   0.81960785]
  [0.47843137 0.62352943 0.827451  ]]

 [[0.40784314 0.5647059  0.79607844]
  [0.40392157 0.56078434 0.7921569 ]
  [0.40392157 0.5686275  0.79607844]
  ...
  [0.48235294 0.62352943 0.81960785]
  [0.47843137 0.627451   0.81960785]
  [0.48235294 0.627451   0.83137256]]

 [[0.40392157 0.5686275  0.79607844]
  [0.40392157 0.5686275  0.79607844]
  [0.40392157 0.5686275  0.79607844]
  ...
  [0.48235294 0.62352943 0.81960785]
  [0.48235294 0.62352943 0.81960785]
  [0.4862745  0.627451   0.83137256]]]
In [12]:
plt.axis("off")
imgplot = plt.imshow(img)
python_bildverarbeitung 4: Graph 3
In [13]:
lum_img = img[:,:,1]
print(lum_img)
[[0.5686275  0.56078434 0.5686275  ... 0.62352943 0.627451   0.62352943]
 [0.5647059  0.56078434 0.5686275  ... 0.62352943 0.627451   0.627451  ]
 [0.5686275  0.5686275  0.5686275  ... 0.62352943 0.62352943 0.627451  ]
 ...
 [0.31764707 0.32941177 0.32941177 ... 0.30588236 0.3137255  0.31764707]
 [0.31764707 0.3137255  0.32941177 ... 0.3019608  0.32156864 0.3372549 ]
 [0.31764707 0.3019608  0.33333334 ... 0.30588236 0.32156864 0.33333334]]
In [14]:
plt.axis("off")
imgplot = plt.imshow(lum_img)
python_bildverarbeitung 5: Graph 4



Färbung, Schatten und Farbton

Jetzt möchten wir auf die Tönung eines Bildes eingehen. Tönung ist ein Ausdruck aus der Farb-Theorie ist eine oft von Malern verwendete Technik. Es ist kaum vorstellbar, wenn man an Maler denkt, gleichzeitig nicht an die Niederlande zu denken. Wir verwenden ein Bild mit "Holländischen Windmühlen" in unserem nächsten Beispiel. (Das Bild wurde in Kinderdijk aufgenommen, ein Dorf in den Niederlanden über 15km östlich von Rotterdam und über 50km entfernt von Den Haag. Es ist ein UNESCO Weltkulturerbe seit 1997.)

In [15]:
windmills = mpimg.imread('windmills.png')

plt.axis("off")
plt.imshow(windmills)
plt.imshow(windmills)
Out[15]:
<matplotlib.image.AxesImage at 0x7fd05c8f1190>
python_bildverarbeitung 6: Graph 5

Wir möchten nun das Bild tönen. Das bedeutet wir "mischen" unsere Farben mit weiss. Dies wird die Helligkeit des Bildes erhöhen. Wir schreiben dafür eine Python-Funktion, welche ein Bild und eine Prozent-Angabe als Parameter entgegen nimmt. "percentage" auf 0 zu setzen, wird das Bild nicht verändern. Wenn Sie es auf eins setzen, wird das Bild vollständig aufgehellt:

In [16]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

def tint(imag, percent):
    """
    imag: the image which will be shaded
    percent: a value between 0 (image will remain unchanged
             and 1 (image will completely white)
    """
    tinted_imag = imag + (np.ones(imag.shape) - imag) * percent
    return tinted_imag

windmills = mpimg.imread('windmills.png')

tinted_windmills = tint(windmills, 0.8)
plt.axis("off")
plt.imshow(tinted_windmills)
plt.imshow(tinted_windmills)
Out[16]:
<matplotlib.image.AxesImage at 0x7fd05e3dba90>
python_bildverarbeitung 7: Graph 6

Ein Schatten ist eine Mischung einer Farbe mit Schwarz, was die Helligkeit verringert.

In [17]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

def shade(imag, percent):
    """
    imag: the image which will be shaded
    percent: a value between 0 (image will remain unchanged
             and 1 (image will be blackened)
    """
    tinted_imag = imag * (1 - percent)
    return tinted_imag

windmills = mpimg.imread('windmills.png')

tinted_windmills = shade(windmills, 0.7)
plt.imshow(tinted_windmills)
Out[17]:
<matplotlib.image.AxesImage at 0x7fd05e516310>
python_bildverarbeitung 8: Graph 7
In [18]:
def vertical_gradient_line(image, reverse=False):
    """
    We create a horizontal gradient line with the shape (1, image.shape[1], 3))
    The values are incremented from 0 to 1, if reverse is False,
    otherwise the values are decremented from 1 to 0.
    """
    number_of_columns = image.shape[1]
    if reverse:
        C = np.linspace(1, 0, number_of_columns)
    else:
        C = np.linspace(0, 1, number_of_columns)
    C = np.dstack((C, C, C))
    return C

horizontal_brush = vertical_gradient_line(windmills)
tinted_windmills =  windmills * horizontal_brush
plt.axis("off")
plt.imshow(tinted_windmills)
Out[18]:
<matplotlib.image.AxesImage at 0x7fd06077e090>
python_bildverarbeitung 9: Graph 8

Wir werden das Bild von rechts nach links tönen indem wir den "reverse" Parameter der Python-Funktion auf "True" setzen:

In [21]:
def vertical_gradient_line(image, reverse=False):
    """
    We create a horizontal gradient line with the shape (1, image.shape[1], 3))
    The values are incremented from 0 to 1, if reverse is False,
    otherwise the values are decremented from 1 to 0.
    """
    number_of_columns = image.shape[1]
    if reverse:
        C = np.linspace(1, 0, number_of_columns)
    else:
        C = np.linspace(0, 1, number_of_columns)
    C = np.dstack((C, C, C))
    return C

horizontal_brush = vertical_gradient_line(windmills, reverse=True)
tinted_windmills =  windmills * horizontal_brush
plt.axis("off")
plt.imshow(tinted_windmills)
Out[21]:
<matplotlib.image.AxesImage at 0x7fd05c852110>
python_bildverarbeitung 10: Graph 9
In [24]:
def horizontal_gradient_line(image, reverse=False):
    """
    We create a vertical gradient line with the shape (image.shape[0], 1, 3))
    The values are incremented from 0 to 1, if reverse is False,
    otherwise the values are decremented from 1 to 0.
    """
    number_of_rows, number_of_columns = image.shape[:2]
    C = np.linspace(1, 0, number_of_rows)
    C = C[np.newaxis,:]
    C = np.concatenate((C, C, C)).transpose()
    C = C[:, np.newaxis]
    return C

vertical_brush = horizontal_gradient_line(windmills)
tinted_windmills =  windmills 
plt.imshow(tinted_windmills)
Out[24]:
<matplotlib.image.AxesImage at 0x7fd05c80bd10>
python_bildverarbeitung 11: Graph 10

Ein Farbton wird entweder durch die Mischung einer Farbe mit Grau produziert, oder durch gleichzeitige Tönung und Schattierung.

In [25]:
charlie = mpimg.imread('Chaplin.png')
plt.gray()
print(charlie)
plt.imshow(charlie)
[[0.16470589 0.16862746 0.1764706  ... 0.         0.         0.        ]
 [0.16078432 0.16078432 0.16470589 ... 0.         0.         0.        ]
 [0.15686275 0.15686275 0.16078432 ... 0.         0.         0.        ]
 ...
 [0.         0.         0.         ... 0.         0.         0.        ]
 [0.         0.         0.         ... 0.         0.         0.        ]
 [0.         0.         0.         ... 0.         0.         0.        ]]
Out[25]:
<matplotlib.image.AxesImage at 0x7fd05c774310>
python_bildverarbeitung 12: Graph 11
In [26]:
colored = np.dstack((charlie*0.1, charlie*1, charlie*0.5))

plt.imshow(colored)
Out[26]:
<matplotlib.image.AxesImage at 0x7fd05c6d6f50>
python_bildverarbeitung 13: Graph 12
Graustufenbilder: http://scikit-image.org/docs/dev/auto_examples/plot_tinting_grayscale_images.html

Im folgenden Beispiel werden verschiedene "colormaps" verwendet. Die "colormaps" befinden sich in matplotlib.pyplot.cm.datad:

In [29]:
plt.cm.datad.keys()
Out[29]:
dict_keys(['Blues', 'BrBG', 'BuGn', 'BuPu', 'CMRmap', 'GnBu', 'Greens', 'Greys', 'OrRd', 'Oranges', 'PRGn', 'PiYG', 'PuBu', 'PuBuGn', 'PuOr', 'PuRd', 'Purples', 'RdBu', 'RdGy', 'RdPu', 'RdYlBu', 'RdYlGn', 'Reds', 'Spectral', 'Wistia', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd', 'afmhot', 'autumn', 'binary', 'bone', 'brg', 'bwr', 'cool', 'coolwarm', 'copper', 'cubehelix', 'flag', 'gist_earth', 'gist_gray', 'gist_heat', 'gist_ncar', 'gist_rainbow', 'gist_stern', 'gist_yarg', 'gnuplot', 'gnuplot2', 'gray', 'hot', 'hsv', 'jet', 'nipy_spectral', 'ocean', 'pink', 'prism', 'rainbow', 'seismic', 'spring', 'summer', 'terrain', 'winter', 'Accent', 'Dark2', 'Paired', 'Pastel1', 'Pastel2', 'Set1', 'Set2', 'Set3', 'tab10', 'tab20', 'tab20b', 'tab20c', 'Blues_r', 'BrBG_r', 'BuGn_r', 'BuPu_r', 'CMRmap_r', 'GnBu_r', 'Greens_r', 'Greys_r', 'OrRd_r', 'Oranges_r', 'PRGn_r', 'PiYG_r', 'PuBu_r', 'PuBuGn_r', 'PuOr_r', 'PuRd_r', 'Purples_r', 'RdBu_r', 'RdGy_r', 'RdPu_r', 'RdYlBu_r', 'RdYlGn_r', 'Reds_r', 'Spectral_r', 'Wistia_r', 'YlGn_r', 'YlGnBu_r', 'YlOrBr_r', 'YlOrRd_r', 'afmhot_r', 'autumn_r', 'binary_r', 'bone_r', 'brg_r', 'bwr_r', 'cool_r', 'coolwarm_r', 'copper_r', 'cubehelix_r', 'flag_r', 'gist_earth_r', 'gist_gray_r', 'gist_heat_r', 'gist_ncar_r', 'gist_rainbow_r', 'gist_stern_r', 'gist_yarg_r', 'gnuplot_r', 'gnuplot2_r', 'gray_r', 'hot_r', 'hsv_r', 'jet_r', 'nipy_spectral_r', 'ocean_r', 'pink_r', 'prism_r', 'rainbow_r', 'seismic_r', 'spring_r', 'summer_r', 'terrain_r', 'winter_r', 'Accent_r', 'Dark2_r', 'Paired_r', 'Pastel1_r', 'Pastel2_r', 'Set1_r', 'Set2_r', 'Set3_r', 'tab10_r', 'tab20_r', 'tab20b_r', 'tab20c_r'])
In [30]:
import numpy as np
import matplotlib.pyplot as plt

charlie = plt.imread('Chaplin.png')

#  colormaps plt.cm.datad
# cmaps = set(plt.cm.datad.keys())
cmaps = {'afmhot', 'autumn', 'bone', 'binary', 'bwr', 'brg', 
         'CMRmap', 'cool', 'copper', 'cubehelix', 'Greens'}

X = [(4, 3, 1, (1, 0, 0)), 
     (4, 3, 2, (0.5, 0.5, 0)), 
     (4, 3, 3, (0, 1, 0)), 
     (4, 3, 4, (0, 0.5, 0.5)),  
     (4, 3, (5, 8), (0, 0, 1)), 
     (4, 3, 6, (1, 1, 0)), 
     (4, 3, 7, (0.5, 1, 0) ),
     (4, 3, 9, (0, 0.5, 0.5)),
     (4, 3, 10, (0, 0.5, 1)), 
     (4, 3, 11, (0, 1, 1)),
     (4, 3, 12, (0.5, 1, 1))]

fig = plt.figure(figsize=(6, 5))

#fig.subplots_adjust(bottom=0, left=0, top = 0.975, right=1)
for nrows, ncols, plot_number, factor in X:
    sub = fig.add_subplot(nrows, ncols, plot_number)
    sub.set_xticks([])
    
    sub.imshow(charlie*0.0002, cmap=cmaps.pop())
    sub.set_yticks([])
python_bildverarbeitung 14: Graph 13