Commit 13e9ac15 authored by Sander Mathijs van Veen's avatar Sander Mathijs van Veen

fixed merge conflict.

parents 81f843ff 3647b016
......@@ -14,5 +14,8 @@ robotica/
*.swo
*.swp
*.toc
*.cmi
*.cmo
*.exe
*#
*~
(* Check if a given year is a leap year *)
let isLeapYear y =
y > 1582 && y mod 4 = 0 && (y mod 100 != 0 || y mod 400 = 0)
;;
(* Test the isLeapYear function with a number of known leap years *)
let test_isLeapYear y expect =
let yields = isLeapYear y in
Printf.printf "%d -> %s, (should be %b, yields %b)\n"
y (if yields = expect then "success" else "failure") expect yields
;;
test_isLeapYear 1500 false;;
test_isLeapYear 1900 false;;
test_isLeapYear 1904 true;;
(* Give a proper English character string representation of a date *)
let date2str day month year =
if day < 1 || day > 31 || month < 1 || month > 12 || year < 0 then
raise (Failure "invalid date")
else
(* Get the textual postfix of a day number *)
let getDayPostfix day =
match day with
1 | 21 | 31 -> "st"
| 2 | 22 -> "nd"
| 3 | 23 -> "rd"
| _ -> "th" in
let months_str = ["January"; "February"; "March"; "April"; "May";
"June"; "July"; "August"; "September"; "October"; "November";
"December"] in
let month_str = List.nth months_str (month - 1) in
Printf.sprintf "%s %d%s, %4d" month_str day (getDayPostfix day) year
;;
(* Test the date2str function with a few common and exceptional cases *)
let test_date2str d m y expect =
let str = (date2str d m y) in
Printf.printf "%d %d %d -> %s, (should be %s, yields %s)\n"
d m y (if str = expect then "success" else "failure") expect str
;;
test_date2str 1 1 2010 "January 1st, 2010";;
test_date2str 9 2 2010 "February 9th, 2010";;
test_date2str 3 3 2011 "March 3rd, 2011";;
test_date2str 22 12 2012 "December 22nd, 2012";;
(* Sum all digits of a natural number *)
let rec sum_digits n =
if n < 0 then raise (Failure "cannot sum digits in integers below 0") else
let str = string_of_int n in
let l = String.length str in
if l = 1 then
int_of_string str
else
int_of_char str.[0] - 48
+ sum_digits (int_of_string (String.sub str 1 (l - 1)))
;;
(* Get the digital root of a natural number n *)
let rec digitRoot n =
let sum = sum_digits n in
if (String.length (string_of_int sum) = 1) then sum else (digitRoot sum)
;;
(* Test the digitRoot function for a few known solutions *)
let test_digitRoot n expect =
let root = digitRoot n in
Printf.printf "%d -> %s, (should be %d, yields %d)\n"
n (if root = expect then "success" else "failure") expect root
;;
test_digitRoot 123 6;;
test_digitRoot 65536 7;;
(* Check if a given string is a palindrome *)
let rec isPalindrome str =
let l = (String.length str) in
l < 2 ||
(str.[0] = str.[l - 1] && (isPalindrome (String.sub str 1 (l - 2))))
;;
(* Test the isPalindrome function with a few known palindromes *)
let test_isPalindrome str expect =
let result = (isPalindrome str) in
Printf.printf "%s -> %s, (should be %b, yields %b)\n"
str (if result = expect then "success" else "failure") expect result
;;
test_isPalindrome "foo" false;; (* non-palindrome of odd length *)
test_isPalindrome "foobar" false;; (* non-palindrome of even length *)
test_isPalindrome "lepel" true;; (* palindrome of odd length *)
test_isPalindrome "foof" true;; (* palindrome of even length *)
test_isPalindrome "" true;; (* The empty string is a palindrome *)
\documentclass[10pt,a4paper]{article}
\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage{amsmath,hyperref,graphicx,booktabs,float,listings}
% Paragraph indentation
\setlength{\parindent}{0pt}
\setlength{\parskip}{1ex plus 0.5ex minus 0.2ex}
\title{Functional Languages - Assignment series 2}
\author{Tadde\"us Kroes (6054129)}
\begin{document}
\maketitle
\section{Assignment 4}
Assignment: \emph{Define the Boolean functions negation, disjunction and
conjunction as $\lambda$-terms.}
\newcommand{\s}{\hspace{2mm}}
\newcommand{\true}{\lambda a.\lambda b.a}
\newcommand{\truea}{\lambda u.\lambda v.u}
\newcommand{\false}{\lambda a.\lambda b.b}
\newcommand{\falsea}{\lambda u.\lambda v.v}
\subsection{Negation}
Negation flips the value of a boolean (TRUE becomes FALSE and vice versa).
Consider $\neg = \lambda b.\lambda x.\lambda y.((b \s y) \s x)$.
Given that $TRUE = \true$ and $FALSE = \false$, we can make the following
derivations:
\begin{table}[H]
\begin{tabular}{rl}
$\neg TRUE$ & $= (\lambda b.\lambda x.\lambda y.((b \s y) \s x) \s \true)$ \\
& $\rightarrow_{\beta} \lambda x.\lambda y.((\true \s y) \s x)$ \\
& $\rightarrow_{\beta} \lambda x.\lambda y.(\lambda b.y \s x)$ \\
& $\rightarrow_{\beta} \lambda x.\lambda y.y$ \\
& $\equiv_{\alpha} FALSE$ \\
& \\
$\neg FALSE$ & $= (\lambda b.\lambda x.\lambda y.((b \s y) \s x) \s \false)$ \\
& $\rightarrow_{\beta} \lambda x.\lambda y.((\false \s y) \s x)$ \\
& $\rightarrow_{\beta} \lambda x.\lambda y.(\lambda b.b \s x)$ \\
& $\rightarrow_{\beta} \lambda x.\lambda y.x$ \\
& $\equiv_{\alpha} TRUE$ \\
\end{tabular}
\end{table}
TRUE and FALSE are both flipped, so the function $\neg$ is correct.
\pagebreak
\subsection{Disjunction}
The disjunction function $\vee$ should have the following properties: \\
TRUE $\vee$ TRUE = TRUE \\
TRUE $\vee$ FALSE = TRUE \\
FALSE $\vee$ TRUE = TRUE \\
FALSE $\vee$ FALSE = FALSE
Consider $\vee = \lambda p.\lambda q.((p \s p) \s q)$. Given the functions for
TRUE and FALSE, the combination of the following derivations proves that the
function $\vee$ is correct:
\begin{table}[H]
\begin{tabular}{rl}
$TRUE \s\vee\s TRUE$ & $= (\lambda p.(\lambda q.((p \s p) \s q) \s \true) \s \true)$ \\
& $\rightarrow_{\beta} (\lambda q.((\true \s \true) \s q) \s \true)$ \\
& $\rightarrow_{\beta} ((\true \s \true) \s \true)$ \\
& $\rightarrow_{\alpha,\beta} (\lambda b.\truea \s \true)$ \\
& $\rightarrow_{\beta} \truea$ \\
& $\equiv_{\alpha} TRUE$ \\
& \\
$TRUE \s\vee\s FALSE$ & $= (\lambda p.(\lambda q.((p \s p) \s q) \s \false) \s \true)$ \\
& $\rightarrow_{\beta} (\lambda q.((\true \s \true) \s q) \s \false)$ \\
& $\rightarrow_{\beta} ((\true \s \true) \s \false)$ \\
& $\rightarrow_{\alpha,\beta} (\lambda b.\truea \s \false)$ \\
& $\rightarrow_{\beta} \truea$ \\
& $\equiv_{\alpha} TRUE$ \\
& \\
$FALSE \s\vee\s TRUE$ & $= (\lambda p.(\lambda q.((p \s p) \s q) \s \true) \s \false)$ \\
& $\rightarrow_{\beta} (\lambda q.((\false \s \false) \s q) \s \true)$ \\
& $\rightarrow_{\beta} ((\false \s \false) \s \true)$ \\
& $\rightarrow_{\beta} (\lambda b.b \s \true)$ \\
& $\rightarrow_{\beta} \true$ \\
& $\equiv_{\alpha} TRUE$ \\
& \\
$FALSE \s\vee\s FALSE$ & $= (\lambda p.(\lambda q.((p \s p) \s q) \s \false) \s \false)$ \\
& $\rightarrow_{\beta} (\lambda q.((\false \s \false) \s q) \s \false)$ \\
& $\rightarrow_{\beta} ((\false \s \false) \s \false)$ \\
& $\rightarrow_{\beta} (\lambda b.b \s \false)$ \\
& $\rightarrow_{\beta} \false$ \\
& $\equiv_{\alpha} FALSE$ \\
\end{tabular}
\end{table}
\pagebreak
\subsection{Conjunction}
The disjunction function $\wedge$ should have the following properties: \\
TRUE $\wedge$ TRUE = TRUE \\
TRUE $\wedge$ FALSE = FALSE \\
FALSE $\wedge$ TRUE = FALSE \\
FALSE $\wedge$ FALSE = FALSE
Consider $\wedge = \lambda p.\lambda q.((p \s q) \s p)$. Given the functions for
TRUE and FALSE, the combination of the following derivations proves that the
function $\wedge$ is correct:
\begin{table}[H]
\begin{tabular}{rl}
$TRUE \s\wedge\s TRUE$ & $= (\lambda p.(\lambda q.((p \s q) \s p) \s \true) \s \true)$ \\
& $\rightarrow_{\beta} (\lambda q.((\true \s q) \s \true) \s \true)$ \\
& $\rightarrow_{\beta} ((\true \s \true) \s \true)$ \\
& $\rightarrow_{\alpha,\beta} (\lambda b.\truea \s \true)$ \\
& $\rightarrow_{\beta} \truea$ \\
& $\equiv_{\alpha} TRUE$ \\
& \\
$TRUE \s\wedge\s FALSE$ & $= (\lambda p.(\lambda q.((p \s q) \s p) \s \false) \s \true)$ \\
& $\rightarrow_{\beta} (\lambda q.((\true \s q) \s \true) \s \false)$ \\
& $\rightarrow_{\beta} ((\true \s \false) \s \true)$ \\
& $\rightarrow_{\alpha,\beta} (\lambda b.\falsea \s \true)$ \\
& $\rightarrow_{\beta} \falsea$ \\
& $\equiv_{\alpha} FALSE$ \\
& \\
$FALSE \s\wedge\s TRUE$ & $= (\lambda p.(\lambda q.((p \s q) \s p) \s \true) \s \false)$ \\
& $\rightarrow_{\beta} (\lambda q.((\false \s q) \s \false) \s \true)$ \\
& $\rightarrow_{\beta} ((\false \s \true) \s \false)$ \\
& $\rightarrow_{\beta} (\lambda b.b \s \false)$ \\
& $\rightarrow_{\beta} \false$ \\
& $\equiv_{\alpha} FALSE$ \\
& \\
$FALSE \s\wedge\s FALSE$ & $= (\lambda p.(\lambda q.((p \s q) \s p) \s \false) \s \false)$ \\
& $\rightarrow_{\beta} (\lambda q.((\false \s q) \s \false) \s \false)$ \\
& $\rightarrow_{\beta} ((\false \s \false) \s \false)$ \\
& $\rightarrow_{\beta} (\lambda b.b \s \false)$ \\
& $\rightarrow_{\beta} \false$ \\
& $\equiv_{\alpha} FALSE$ \\
\end{tabular}
\end{table}
\section{Assignment 5}
See appendix \ref{appendix:ass5} (file \emph{ass5.ml}) for my implementation
of the functions \texttt{isLeapYear}, \texttt{date2str}, \texttt{digitRoot}
and \texttt{isPalindrome}.
\pagebreak
\appendix
\section{ass5.ml}
\label{appendix:ass5}
\lstinputlisting{ass5.ml}
\end{document}
(*
* Check if the given year is a leap year. This will return true if the given
* integer is larger than 1582, can be divided by 4, but not by 100, or it can
* be divided by 400. Otherwise, false is returned.
*)
let isLeapYear x =
x > 1582 && x mod 4 == 0 && (x mod 100 != 0 || x mod 400 == 0)
;;
let testLeapYear x =
Printf.printf "%d: %b\n" x (isLeapYear x);;
(testLeapYear 1400);; (* false *)
(testLeapYear 1582);; (* false *)
(testLeapYear 1583);; (* false *)
(testLeapYear 1584);; (* true *)
(testLeapYear 1700);; (* false *)
(testLeapYear 2000);; (* true *)
(*
* Implementation of date2str.
*
* Given a correct calendar triple (day, month, year), return a proper English
* date. For example: (1, 2, 2011) returns "February 1st, 2011".
*
* The suffix of the day number is not related to the actual month, so "February
* 31st, 2011" can be generated. However, there is no reason to implement error
* checking to prevent returning unvalid day/month combinations (not part of the
* assignment). Note: the assigment clearly states that a correct calendar
* triple is the input of date2str.
*
* This implementation does check for invalid day numbers or invalid month
* numbers. The exception Hell will be raised for these invalid integers.
*)
exception Hell
(*
* Given a day number, return the day number followed by its English suffix. For
* example, this will return ``1st'' for day 1, ``23rd'' for day 23 and ``12th'
* for day 12. If the day number is negative or the day number is not between 1
* and 31 (inclusive), Hell will be raised.
*)
let day_with_suffix day =
match day with
1 | 21 | 31 -> (string_of_int day) ^ "st"
| 2 | 22 -> (string_of_int day) ^ "nd"
| 3 | 23 -> (string_of_int day) ^ "rd"
| _ when (day > 0 && day < 32) -> (string_of_int day) ^ "th"
| _ -> raise Hell
;;
(*
* Return the English name of a month number (a number between 1 and 12,
* inclusive). If the month number is not defined, Hell will be raised.
*)
let month_name month =
match month with
1 -> "January"
| 2 -> "February"
| 3 -> "March"
| 4 -> "April"
| 5 -> "May"
| 6 -> "June"
| 7 -> "July"
| 8 -> "August"
| 9 -> "September"
| 10 -> "October"
| 11 -> "November"
| 12 -> "December"
| _ -> raise Hell
;;
let date2str day month year =
Printf.sprintf "%s %s, %d" (month_name month) (day_with_suffix day) year
;;
(* correct date2str input: *)
print_string (date2str 2 2 2011);;
(* expected a raised Hell exception: *)
print_string (date2str (-2) 2 2011);;
(*
* Calculate the digital root of a number: add up all of its digits, and do that
* recursively until a single digit is obtained.
*)
let rec digitRoot ?(sum=0) number =
let res = match number with
| _ when number < 10 -> (sum + number)
| _ -> (digitRoot ~sum:(sum + (number mod 10)) (number / 10))
in if res < 10 then res else digitRoot res
;;
let test_digitRoot input =
print_string "--- test_digitRoot ---\n";
Printf.printf "%d -> %d\n" input (digitRoot input)
;;
test_digitRoot 20;;
test_digitRoot 24;;
test_digitRoot 1234;; (* = 1 *)
test_digitRoot 65536;; (* = 7 *)
test_digitRoot 12345678;; (* = 9 *)
test_digitRoot 18273645;; (* = 9 *)
test_digitRoot 123456789;; (* = 9 *)
test_digitRoot 1234567890123456789;; (* = 9 *)
test_digitRoot 999999999998;; (* = 8 *)
test_digitRoot 5674;; (* = 4 *)
#load "str.cma";;
(*
* The function isPalindrome checks if a given character string is a palindrome,
* i.e. it is identical whether being read from left to right or from right to
* left. Note that this function removes punctuation and word dividers before
* checking the given character string.
*)
let isPalindrome raw =
(*
* Check if the left and right character of the string are the same. An
* empty string and a string with length 1 is by definition a palindrome.
* Check uses a character string `s' and its length `l' to recursively
* determine if `s' is a palindrome.
*)
let rec check s l =
l < 2 || (s.[0] == s.[l-1] && (check (String.sub s 1 (l-2)) (l-2)))
in
(* Remove punctuation / word dividers -> only alphanumeric chars remain. *)
let filter = Str.regexp "[^a-zA-Z0-9]+" in
let filtered = String.lowercase (Str.global_replace filter "" raw) in
(check filtered (String.length filtered))
;;
let test_isPalindrome str =
Printf.printf "isPalindrome(\"%s\") -> %b\n" str (isPalindrome str)
;;
test_isPalindrome "";;
test_isPalindrome "a";;
test_isPalindrome "baas saab";;
test_isPalindrome "never odd or even";;
test_isPalindrome "Was it a rat i saw?";;
test_isPalindrome "expected failure!";;
#!/usr/bin/env python
from matplotlib.pyplot import imread, imshow, subplot, show
from matplotlib.pyplot import imread, imshow, subplot, show, axis
from numpy import arctan2, zeros, append, pi
from numpy.linalg import norm
from scipy.ndimage import convolve1d
......@@ -29,19 +29,19 @@ def canny(F, s, Tl=None, Th=None):
# Gradient norm and rounded angle
G[p] = norm(append(Gy[p], Gx[p]))
A[p] = int(round(arctan2(Gy[p], Gx[p]) * 4 / pi + 1)) % 4
A[p] = (4 - int(round(4 * arctan2(Gy[p], Gx[p]) / pi))) % 4
# Non-maximum suppression
E = zeros(F.shape)
compare = [((-1, 0), (1, 0)), ((-1, 1), (1, -1)), \
((0, 1), (0, -1)), ((1, 1), (-1, -1))]
for y in xrange(F.shape[0]):
for x in xrange(F.shape[1]):
g = G[y, x]
a = A[y, x]
compare = [((y, x - 1), (y, x + 1)), ((y - 1, x - 1), \
(y + 1, x + 1)), ((y - 1, x), (y + 1, x)), \
((y + 1, x - 1), (y - 1, x + 1))]
na, nb = compare[a]
a, b = compare[A[y, x]]
na = (y + a[1], x + a[0])
nb = (y + b[1], x + b[0])
if (not in_image(na, G) or g > G[na]) \
and (not in_image(nb, G) or g > G[nb]):
......@@ -101,19 +101,24 @@ if __name__ == '__main__':
# Execute with tracing edges
E, T = canny(F, s, float(argv[2]), float(argv[3]))
subplot(131)
subplot(131, title='Original image')
imshow(F, cmap='gray')
subplot(132)
axis('off')
subplot(132, title='Gradient magnitudes')
imshow(E, cmap='gray')
subplot(133)
axis('off')
subplot(133, title='Thresholds applied')
imshow(T, cmap='gray')
axis('off')
else:
# Execute until nn-maximum suppression
E = canny(F, s)
subplot(121)
subplot(121, title='Original image')
imshow(F, cmap='gray')
subplot(122)
axis('off')
subplot(122, title='Gradient magnitudes')
imshow(E, cmap='gray')
axis('off')
show()
......@@ -2,7 +2,7 @@
from numpy import zeros, arange, meshgrid, array, matrix
from math import ceil, exp, pi, sqrt
from matplotlib.pyplot import imread, imshow, plot, xlabel, ylabel, show, \
subplot, xlim, savefig, axis
subplot, xlim, savefig, axis, ticklabel_format
from mpl_toolkits.mplot3d import Axes3D
from scipy.ndimage import convolve, convolve1d
from time import time
......@@ -96,11 +96,13 @@ if __name__ == '__main__':
G = convolve(F, W, mode='nearest')
# Show the original image, kernel and convoluted image respectively
subplot(131)
subplot(131, title='Original image')
imshow(F, cmap='gray')
plot_kernel(W, subplot(132, projection='3d'))
subplot(133)
axis('off')
plot_kernel(W, subplot(132, projection='3d', title='Kernel'))
subplot(133, title='Convoluted image')
imshow(G, cmap='gray')
axis('off')
elif argv[1] == '1d':
"""Calculate the gaussian kernel using derivatives of the specified
order in both directions."""
......@@ -119,11 +121,13 @@ if __name__ == '__main__':
W = Fy.T * Fx
# Show the original image, kernel and convoluted image respectively
subplot(131)
subplot(131, title='Original image')
imshow(F, cmap='gray')
plot_kernel(W, subplot(132, projection='3d'))
subplot(133)
axis('off')
plot_kernel(W, subplot(132, projection='3d', title='Kernel'))
subplot(133, title='Convoluted image')
imshow(G, cmap='gray')
axis('off')
elif argv[1] == 'timer':
"""Time the performance of a 1D/2D convolution and plot the results."""
if len(argv) < 3:
......
\documentclass[10pt,a4paper]{article}
\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage{amsmath,hyperref,graphicx,booktabs,float}
% Paragraph indentation
\setlength{\parindent}{0pt}
\setlength{\parskip}{1ex plus 0.5ex minus 0.2ex}
\title{Image processing 4: Local Structure}
\author{Sander van Veen \& Tadde\"us Kroes \\ 6167969 \& 6054129}
\begin{document}
\maketitle
\section{Analytical Local Structure}
\subsection{Derivatives}
\label{sub:derivatives}
We have been given the following function:
$$f(x, y) = A sin(Vx) + B cos(Wy)$$
The partial derivatives $f_x, f_y, f_{xx}, f_{xy}$ and $f_{yy}$ can be
derived as follows:
\begin{table}[H]
\begin{tabular}{rl}
$f_x$ & $= \frac{\delta f}{\delta x}$ \\
& $= A \frac{\delta}{\delta x} sin(Vx) + B \frac{\delta}{\delta x} cos(Wy)$ \\
& $= A cos(Vx) \cdot V + B \cdot 0$ \\
& $= AV cos(Vx)$ \\
& \\
$f_y$ & $= \frac{\delta f}{\delta y}$ \\
& $= A \frac{\delta}{\delta y} sin(Vx) + B \frac{\delta}{\delta y} cos(Wy)$ \\
& $= A \cdot 0 - B sin(Wy) \cdot W$ \\
& $= -BW sin(Wy)$ \\
& \\
$f_{xx}$ & $= \frac{\delta f_x}{\delta x}$ \\
& $= AV \frac{\delta}{\delta x} cos(Vx)$ \\
& $= -AV^2 sin(Vx)$ \\
& \\
$f_{xy}$ & $= \frac{\delta f_x}{\delta y} = AV \frac{\delta}{\delta y} cos(Vx) = 0$ \\
& \\
$f_{yy}$ & $= \frac{\delta f_y}{\delta y}$ \\
& $= -BW \frac{\delta}{\delta y} sin(Wy)$ \\
& $= -BW^2 cos(Wy)$ \\
\end{tabular}
\end{table}
\pagebreak
\subsection{Plots}
The following plots show $f(x, y)$ and its first and second derivatives. The
image on the left shows $f_x$ and $f_y$. The image on the right shows $f_{xx}$
and $f_{yy}$ in a quiver plot over $f(x, y)$. The arrows point towards the
largest increase of gray value, which means that the derivations in chapter
\ref{sub:derivatives} are correct.
\begin{figure}[H]
\hspace{-2cm}
\includegraphics[scale=.8]{samples.pdf}
\caption{Plots of $f(x, y)$ and its first and second derivatives.}
\end{figure}
\section{Gaussian Convolution}
\subsection{Implementation}
All Gaussian functions are implemented in the file \emph{gauss.py}. The
\texttt{Gauss} function fills a 2D array with the values of the 2D Gaussian
function\footnote{\label{footnote:gaussian-filter}
\url{http://en.wikipedia.org/wiki/Gaussian\_filter}}:
$$g_{2D}(x, y) = \frac{1}{2 \pi \sigma^2} e^{-\frac{x^2 + y^2}{2 \sigma^2}}$$
This function converges to zero, but never actually equals zero. The filter's
size is therefore chosen to be $\lceil 6 * \sigma \rceil$ in each direction by
convention (since values for $x,y > 3 * \sigma$ are negligible). Finally,
because the sum of the filter should be equal to 1, it is divided by its own
sum.
The result of the \texttt{Gauss} function is shown in figure
\ref{fig:gauss-2d}. The subplots respectively show the original image, the
Gaussian kernel and the convolved image.
\begin{figure}[H]
\hspace{-3cm}
\includegraphics[scale=.6]{gauss_2d_5.pdf}
\caption{The result of \texttt{python gauss.py 2d 5}.}
\label{fig:gauss-2d}
\end{figure}
\subsection{Measuring Performance}
We've timed the runtime of the \texttt{Gauss} function for
$\sigma = 1,2,3,5,7,9,11,15,19$, the results are in figure
\ref{fig:times-2d}. The graph shows a computational complexity of
$\mathcal{O}(\sigma^2)$.
\begin{figure}[H]
\center
\includegraphics[scale=.5]{gauss_times_2d.pdf}
\caption{The result of \texttt{python gauss.py timer 2d 5} (so, each
timing has been repeated 5 times and then averaged).}
\label{fig:times-2d}
\end{figure}
\section{Separable Gaussian Convolution}
\subsection{Implementation}
The \texttt{Gauss1} function uses the 1D Gaussian
function\ref{footnote:gaussian-filter}:
$$g_{1D}(x) = \frac{1}{\sqrt{2 \pi} \cdot \sigma} e^{-\frac{x^2}{2 \sigma^2}}$$
This function returns a 1D array of kernel values, which is used by the
function \texttt{convolve1d}. Using the separability property, the following
code snippets produce the same result:
\begin{verbatim}
W = Gauss1(s)
G = convolve1d(F, W, axis=0, mode='nearest')
G = convolve1d(G, W, axis=1, mode='nearest')
\end{verbatim}
as opposed to:
\begin{verbatim}
G = convolve(F, Gauss(s), mode='nearest')
\end{verbatim}
The timing results of the first code snippet are displayed in figure
\ref{fig:times-1d}. The graph shows that the 1D convolution has a
computational complexity of $\mathcal{O}(\sigma)$, which is much faster than
the 2D convolution (certainly for higher scales).
\begin{figure}[H]
\center
\includegraphics[scale=.5]{gauss_times_1d.pdf}
\caption{The result of \texttt{python gauss.py timer 1d 50}.}
\label{fig:times-1d}
\end{figure}
\section{Gaussian Derivatives}
\subsection{Separability}
We can show analytically that all derivatives of the 2D Gaussian function
are separable as well:
\begin{table}[H]
\begin{tabular}{rll}
$ \frac{\delta}{\delta x} \frac{\delta}{\delta y} G_{2D}(x, y)$
& $= \frac{\delta}{\delta x} (\frac{\delta}{\delta y} (G_{1D}(x)
\cdot G_{1D}(y)))$ & $G_{2D}(x, y) = G_{1D}(x) \cdot G_{1D}(y)$ is
given \\
& $= \frac{\delta}{\delta x} (G_{1D}(x) \cdot
\frac{\delta}{\delta y} G_{1D}(y))$ & because $G_{1D}(x)$ is
constant with respect to $y$ \\
& $= \frac{\delta}{\delta x} G_{1D}(x) \cdot
\frac{\delta}{\delta y} G_{1D}(y)$ & \\
\end{tabular}
\end{table}
The separability property is used in the \texttt{gD} function, by calling the
\texttt{convolve1d} function separately for each direction. This implementation
yields the 2-jet of scale 4 of the cameraman image in figure \ref{fig:jet}.
\begin{figure}[H]
\center
\includegraphics[scale=.4]{jet_4.pdf}
\caption{The result of \texttt{python gauss.py jet 4}.}
\label{fig:jet}
\end{figure}
\section{Canny Edge detector}
The Canny Edge Detector is implemented in the file \emph{canny.py}. For the
algorithm, we used the Wiki
page\footnote{\url{http://en.wikipedia.org/wiki/Canny\_edge\_detector}}. The
different Wiki sections are marked by comments with similar descriptions. Since
the Wiki page is self-explanatory, we will not discuss the algorithm itself in
this report.
The program usage is as follows:
\begin{verbatim}
python canny.py SCALE [ LOWER_THRESHOLD HIGHER_THRESHOLD ]
\end{verbatim}
The scale is obviously used for finding the intensity gradient, and the
thresholds are used in the "Hysterisis thresholding" part. Note that the
thresholds are optional, because the assignment instructs to create a function
\texttt{canny(F, s)} without any arguments for thresholds. Therefore, we were
not sure whether to implement this section. If the thresholds are specified,
the resulting plot will contain an additional image containing a binary image
of edges. The thresholds can be specified in the range 0-255, they are scaled
down by the program to match the image's color range. An example execution of
edge detection on the cameraman image using a scale of 2, a lower threshold of
50 and a higher threshold of 70, can be viewed in figure \ref{fig:canny}.
\begin{figure}[H]
\hspace{-5cm}
\includegraphics[scale=.6]{canny_2_50_70.pdf}
\caption{The result of \texttt{python canny.py 2 50 70}.}
\label{fig:canny}
\end{figure}
\end{document}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment