diff --git a/notes/bioimaging2.slides.html b/notes/bioimaging2.slides.html new file mode 100644 index 0000000..1c398e2 --- /dev/null +++ b/notes/bioimaging2.slides.html @@ -0,0 +1,16103 @@ + + +
+ + + + + + +%%html
+<script src="https://bits.csb.pitt.edu/preamble.js"></script>
+
import numpy as np; import matplotlib.pyplot as plt; import matplotlib.cm as cm
+%matplotlib inline
+from PIL import Image
+im = Image.open('imgs/dogs.png')
+im
+
As a reminder, we can use the point
method to apply an arbitrary function to each pixel.
pointed = im.point(lambda x: x+100)
+
pointed
+
A filter applies a convolution kernel to an image.
+Although this sounds fancy, this is just a generalization of the point
method where the function takes as input the neighborhood of the pixels.
The kernel is represented by an $n$x$n$ matrix where the target pixel is in the center.
+The output of the filter is the sum of the products of the matrix elements with the corresponding pixels.
+Examples (from Wikipedia):
++ | + + | ++ + | +
Identity | Blur | Edge Detection | +
%%html
+<div id="im2ef" style="width: 500px"></div>
+<script>
+
+ var divid = '#im2ef';
+ jQuery(divid).asker({
+ id: divid,
+ question: "If a pixel and all its neighbors are the same color, how will its color change after applying the edge detection filter?",
+ answers: ['Same','Brighter','Darker','Black',"White"],
+ server: "https://bits.csb.pitt.edu/asker.js/example/asker.cgi",
+ charter: chartmaker})
+
+$(".jp-InputArea .o:contains(html)").closest('.jp-InputArea').hide();
+
+
+</script>
+
The PIL ImageFilter module includes a number of built-in convolution kernels that can be applied using the image filter
method.
from PIL import ImageFilter
+
+print(ImageFilter.SMOOTH.filterargs) #shape, denominator, offset, coefficients
+
((3, 3), 13, 0, (1, 1, 1, 1, 5, 1, 1, 1, 1)) ++
filt = ImageFilter.SMOOTH.filterargs
+print(np.array(filt[3]).reshape(filt[0]),filt[1])
+
[[1 1 1] + [1 5 1] + [1 1 1]] 13 ++
The output is the sum of the product of the coefficients and the corresponding pixels divided by the denominator and incremented by the offset.
+ +def twoimgs(im1,im2):
+ '''Return the result of putting two images next to each other'''
+ w = im1.width+im2.width
+ h = im1.height
+ im = Image.new('RGB', (w, h))
+ im.paste(im1, (0,0))
+ im.paste(im2, (im1.width,0))
+ return im
+
filt = ImageFilter.BLUR.filterargs
+print(np.array(filt[3]).reshape(filt[0]),filt[1])
+twoimgs(im,im.filter(ImageFilter.BLUR))
+
[[1 1 1 1 1] + [1 0 0 0 1] + [1 0 0 0 1] + [1 0 0 0 1] + [1 1 1 1 1]] 16 ++
filt = ImageFilter.SMOOTH.filterargs
+print(np.array(filt[3]).reshape(filt[0]),filt[1])
+twoimgs(im,im.filter(ImageFilter.SMOOTH))
+
[[1 1 1] + [1 5 1] + [1 1 1]] 13 ++
filt = ImageFilter.FIND_EDGES.filterargs
+print(np.array(filt[3]).reshape(filt[0]),filt[1])
+
[[-1 -1 -1] + [-1 8 -1] + [-1 -1 -1]] 1 ++
twoimgs(im,im.filter(ImageFilter.FIND_EDGES))
+
filt = ImageFilter.SHARPEN.filterargs
+print(np.array(filt[3]).reshape(filt[0]),filt[1])
+twoimgs(im,im.filter(ImageFilter.SHARPEN))
+
[[-2 -2 -2] + [-2 32 -2] + [-2 -2 -2]] 16 ++
There are also non-linear filters that don't compute sums of products. Instead, they compare the values of the neighboring pixels (e.g., max value, min value, median value).
+ +twoimgs(im,im.filter(ImageFilter.MaxFilter(5)))
+
Recursion is when a function calls itself on a small version of the problem to compute the answer.
+It is a very useful way to think about complex, but decomposable, problems.
+ + +def fib(n):
+ if n <= 1: return 1
+ return fib(n-1)+fib(n-2)
+
+fib(5)
+
8+
A recursive function must have a base case, an input that is eventually reached that does not require any further calls to the function ($n \le 1$ above).
+What happens if you forget the base case?
+ +def brokenfib(n):
+ return brokenfib(n-1)+brokenfib(n-2)
+
+brokenfib(5)
+
+--------------------------------------------------------------------------- +RecursionError Traceback (most recent call last) +Cell In[16], line 4 + 1 def brokenfib(n): + 2 return brokenfib(n-1)+brokenfib(n-2) +----> 4 brokenfib(5) + +Cell In[16], line 2, in brokenfib(n) + 1 def brokenfib(n): +----> 2 return brokenfib(n-1)+brokenfib(n-2) + +Cell In[16], line 2, in brokenfib(n) + 1 def brokenfib(n): +----> 2 return brokenfib(n-1)+brokenfib(n-2) + + [... skipping similar frames: brokenfib at line 2 (2970 times)] + +Cell In[16], line 2, in brokenfib(n) + 1 def brokenfib(n): +----> 2 return brokenfib(n-1)+brokenfib(n-2) + +RecursionError: maximum recursion depth exceeded+
Every time you call a function, the function and its arguments are placed on the call stack.
+The call stack will eventually run out of memory.
+Python limits how many calls can be on the call stack.
+ +import sys
+sys.getrecursionlimit()
+
3000+
sys.setrecursionlimit(6000) #broken fib will now take twice as long to crash
+
if n <= 1: return 1
)n-1
, n-2
)fib(n-1)
, fib(n-2)
)fib(n-1)+fib(n-2)
)def fun(L):
+ val = L[0]
+ if len(L) == 1: return val
+ val2 = fun(L[1:])
+ if val > val2: return val
+ return val2
+
%%html
+<div id="im2rec" style="width: 500px"></div>
+<script>
+
+ var divid = '#im2rec';
+ jQuery(divid).asker({
+ id: divid,
+ question: "What is the return value of <tt>fun([9,4,6,1,3,10,2])</tt>?",
+ answers: ['9','4','35','1','3','10','2','Error'],
+ server: "https://bits.csb.pitt.edu/asker.js/example/asker.cgi",
+ charter: chartmaker})
+
+$(".jp-InputArea .o:contains(html)").closest('.jp-InputArea').hide();
+
+
+</script>
+
Click on a pixel, fill that pixel and all touching pixels of the same color with the fill color.
+Flood-fill (pixel, target-color, replacement-color)
What is/are the recursive step(s)?
+What is the base case?
+ +Flood-fill (pixel, target-color, replacement-color):
+!wget http://bits.csb.pitt.edu/images/image1.tif
+
--2023-10-30 20:44:07-- http://bits.csb.pitt.edu/images/image1.tif +Resolving bits.csb.pitt.edu (bits.csb.pitt.edu)... 136.142.4.139 +Connecting to bits.csb.pitt.edu (bits.csb.pitt.edu)|136.142.4.139|:80... connected. +HTTP request sent, awaiting response... 200 OK +Length: 921814 (900K) [image/tiff] +Saving to: ‘image1.tif.1’ + +image1.tif.1 100%[===================>] 900.21K --.-KB/s in 0.02s + +2023-10-30 20:44:07 (55.5 MB/s) - ‘image1.tif.1’ saved [921814/921814] + ++
im = Image.open('image1.tif')
+(r,g,b) = im.split()
+b
+
The idea is to threshold the image and then count the number of white blobs.
+ +blobs = b.point(lambda x: 255 if x > 45 else 0)
+blobs
+
load
blobs = b.point(lambda x: 255 if x > 45 else 0)
+
+pixels = blobs.load()
+
+def flood(x,y,w,h,pixels):
+ pass #implement
+