Skip to content

Commit

Permalink
45 pages in!
Browse files Browse the repository at this point in the history
  • Loading branch information
kspalaiologos committed Feb 1, 2023
1 parent b62865e commit 28b8698
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 9 deletions.
89 changes: 81 additions & 8 deletions doc/content/chapter2.tex
Original file line number Diff line number Diff line change
Expand Up @@ -852,21 +852,21 @@ \section{Products and two-dimensional convolution}
\raise "Expected a square kernel.")
(case (or (/= (mod kern-h 2) 1) (/= (mod kern-w 2) 1))
\raise "Expected an odd-sized kernel.")
(def kern-c \- (floor (/ kern-w 2)) 1)
(def kern-c \floor (/ kern-w 2))
(def conv-range \outer-product
(range (- x kern-c) \- (+ x kern-w) kern-c)
(range (- y kern-c) \- (+ y kern-h) kern-c))
(def conv-mat \:$(lift cell-mat) conv-range)
\foldl + 0 \* (flatten conv-mat) \flatten kern
\end{Verbatim}

Now, define the \verb|convolution-2d| function that takes argument the input matrix and the kernel matrix and yields the convolution of the two matrices:
Now, define the \verb|convolution-2d| function that takes argument the input matrix and the kernel matrix and yields the convolution of the two matrices using the \verb|parallel-map-idx| function which behaves comparably to \verb|$:| (the parallel variant of \verb|:|), except it also passes the index in the list to the function:

\begin{Verbatim}
defun convolve (mat kern) \let-seq
(def cell-mat (cell mat))
\map-idx (lambda (y row)
\map-idx (lambda (x _e)
\parallel-map-idx (lambda (y row)
\parallel-map-idx (lambda (x _e)
\convolve-step cell-mat kern x y) row) mat
\end{Verbatim}

Expand All @@ -893,7 +893,7 @@ \section{Products and two-dimensional convolution}
Define a function that takes RGB image data (two-dimensional integer matrix), splits the color channels and applies a convolution kernel to each channel, then fuses the results together.

\begin{Verbatim}
defun convolve-rgb (img kern) (
defun convolve-rgb (img kern) (let-seq
; Extract the R, G and B channels of the image data.
(def r \$($(^/ 255)@bit:unpack _ '(0 8))%[0] img)
(def g \$($(^/ 255)@bit:unpack _ '(8 16))%[0] img)
Expand Down Expand Up @@ -934,7 +934,7 @@ \section{Products and two-dimensional convolution}
\raise "Expected a square kernel.")
(case (or (/= (mod kern-h 2) 1) (/= (mod kern-w 2) 1))
\raise "Expected an odd-sized kernel.")
(def kern-c \- (floor (/ kern-w 2)) 1)
(def kern-c \floor (/ kern-w 2))
(def conv-range \outer-product
(range (- x kern-c) \- (+ x kern-w) kern-c)
(range (- y kern-c) \- (+ y kern-h) kern-c))
Expand All @@ -943,13 +943,86 @@ \section{Products and two-dimensional convolution}
(defun public:convolve (mat kern) \let-seq
(def cell-mat (cell mat))
\map-idx (lambda (y row)
\map-idx (lambda (x _e)
\parallel-map-idx (lambda (y row)
\parallel-map-idx (lambda (x _e)
\convolve-step cell-mat kern x y) row) mat)
(defun public:convolve-rgb (img kern) (let-seq
; Extract the R, G and B channels of the image data.
(def r \$($(^/ 255)@bit:unpack _ '(0 8))%[0] img)
(def g \$($(^/ 255)@bit:unpack _ '(8 16))%[0] img)
(def b \$($(^/ 255)@bit:unpack _ '(16 24))%[0] img)
; Convolve each channel and convert back to integer values.
(defun quantize x (cond ((< x 0) 0) ((> x 1) 255) ((round@* x 255))))
(def r \quantize%[0] \public:convolve r kern)
(def g \quantize%[0] \public:convolve g kern)
(def b \quantize%[0] \public:convolve b kern)
; Merge the channels back together.
((lambda (r g b)
(bit:pack (tie 0 8 r) (tie 8 16 g) (tie 16 24 b) (tie 24 32 255)))%[0] r g b)))
"OK"
\end{Verbatim}

Finally, test the box blur on an image. Consider the following \textit{original}, 256x256 RGB image:

\begin{figure}[h]
\caption{lena-256.png}
\centering
\includegraphics[width=0.25\textwidth]{figures/lena-256.jpg}
\end{figure}

Decrease numerical precision to speed up the computation:

\begin{Verbatim}
--> import "convolution.lisp"
OK
--> let ((fr 10)) (img:write "lena-blurry.png" (convolve-rgb
... (img:read "lena-256.jpg")
... (* (/ 9) '((1 1 1) (1 1 1) (1 1 1)))))
lena-blurry.png
\end{Verbatim}

\begin{figure}[h]
\caption{lena-blurry.png}
\centering
\includegraphics[width=0.25\textwidth]{figures/lena-blurry.png}
\end{figure}

Another interesting operation to check is the Gaussian blur:

\begin{Verbatim}
--> import "convolution.lisp"
OK
--> let ((fr 10)) (img:write "lena-gauss.png" (convolve-rgb
... (img:read "lena-256.jpg")
... (* (/ 16) '((1 2 1) (2 4 2) (1 2 1)))))
lena-gauss.png
\end{Verbatim}

\begin{figure}[h]
\caption{lena-gauss.png}
\centering
\includegraphics[width=0.25\textwidth]{figures/lena-gauss.png}
\end{figure}

Finally, the edge detection kernel:

\begin{Verbatim}
--> import "convolution.lisp"
OK
--> let ((fr 10)) (img:write "lena-edge.png" (convolve-rgb
... (img:read "lena-256.jpg")
... '((-1 -1 -1) (-1 8 -1) (-1 -1 -1))))
lena-edge.png
\end{Verbatim}

\begin{figure}[h]
\caption{lena-edge.png}
\centering
\includegraphics[width=0.25\textwidth]{figures/lena-edge.png}
\end{figure}

\section{Searching and partitioning}

\section{Sorting and permutations}
Expand Down
Binary file added doc/figures/lena-blurry.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/figures/lena-edge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/figures/lena-gauss.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/main.pdf
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public static void registerDefault(Environment env) {
env.setp("filter-idx", new Atom(new FilterIdx()));
env.setp("⍭¨", new Atom(new FilterIdx()));
env.setp("map-idx", new Atom(new MapIdx()));
env.setp("parallel-map-idx", new Atom(new ParallelMapIdx()));
env.setp("parallel-filter", new Atom(new ParallelFilter()));
env.setp("⍭∵", new Atom(new ParallelFilter()));
env.setp("∨?", new Atom(new Any()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package palaiologos.kamilalisp.runtime.array.hof;

import com.google.common.collect.Streams;
import palaiologos.kamilalisp.atom.*;
import palaiologos.kamilalisp.error.TypeError;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class ParallelMapIdx extends PrimitiveFunction implements Lambda {
@Override
public Atom apply(Environment env, List<Atom> args) {
assertArity(args, 2);
Callable reductor = args.get(0).getCallable();
if (args.get(1).getType() == Type.LIST) {
List<Atom> list = args.get(1).getList();
Stream<Pair<BigInteger, Atom>> s = Streams.zip(
Stream.iterate(BigInteger.ZERO, i -> i.add(BigInteger.ONE)), list.stream(), Pair::new);
return new Atom(s.parallel().map(p -> Evaluation.evaluate(env, reductor, List.of(new Atom(p.fst()), p.snd()))).toList());
} else if (args.get(1).getType() == Type.STRING) {
String str = args.get(1).getString();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
sb.append(Evaluation.evaluate(env, reductor, List.of(new Atom(BigInteger.valueOf(i)), new Atom(String.valueOf(str.charAt(i))))).getString());
}
return new Atom(sb.toString());
} else {
throw new TypeError("Expected a list or a string as the second argument to `map-idx'.");
}
}

@Override
protected String name() {
return "map-idx";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public Atom apply(Environment env, List<Atom> args) {
Atom result = Evaluation.evaluate(childEnv, data.get(data.size() - 1));
for (String key : childEnv.keys())
if (!env.has(key) && key.startsWith("public:"))
env.set(key.substring(6), childEnv.get(key));
env.set(key.substring(7), childEnv.get(key));
return result;
} catch (IOException e) {
throw new RuntimeException(e);
Expand Down

0 comments on commit 28b8698

Please sign in to comment.