|
\section{Data Size Decision Framework}
|
|
\label{sec:design}
|
|
|
|
This section presents the proposed method to generate a data array that has less size compared to jpeg image when some of distortion is allowed. We use the thermal data from FLIR ONE PRO.
|
|
|
|
The nearby pixels usually have similar values, except at the edge of objects. Hence, we can divide an image into several regions, and the pixels in a same region has similar value so we can use the average value to represent it and do not cause too much error. However, precisely divide an image into some polygon region needs a lot of computation power and difficult to describe the edge of each region. Also, determining the number of region is a challenge. Hence, to effectively describe regions we design that every region most be a rectangle, and every region can divide into 4 regions by cut in half at the middle of horizontal and vertical. The image will start from only contains one region, and 3 regions will be added per round since we divide a region into 4 pieces.
|
|
|
|
Our method is shown in Figure~\ref{fig:SystemArchitecture}. Data structure initialization only needs to do once if the size of a frame doesn't change. A thermal data will be loaded into our data structure and divide into several regions. Finally, the compressed data will be encoded by Huffman coding, and transmitted to database. When users want to use the thermal data, they can restore the data from the encoded data in database.
|
|
|
|
\begin{figure}[htbp]
|
|
\centering
|
|
\includegraphics[width=\columnwidth]{figures/SystemArchitecture.pdf}
|
|
\caption{System Architecture}
|
|
\label{fig:SystemArchitecture}
|
|
\end{figure}
|
|
|
|
\subsection{Region Represent Grammar}
|
|
|
|
For each frame, we can use a context-free language to represent it.
|
|
\begin{center}
|
|
\begin{tabular}{rl}
|
|
$S \rightarrow\ $ & $R$ \\
|
|
$R \rightarrow\ $ & $\alpha$\\
|
|
$R \rightarrow\ $ & $\beta RRRR$
|
|
\end{tabular}
|
|
\end{center}
|
|
|
|
$R$ refers to a region of frame, and it can either use the average $\alpha$ of the pixels in the region to represent the whole region or divide into four regions and left a remainder $\beta$. Dependence on size of compressed data, we can choose the amount of dividing regions. The context-free grammar starts from a region containing whole frame. For each $R$ we calculate a score based on the data the quality of data we can improve by dividing it into smaller regions. Figure~\ref{fig:pngImage} shows an example of thermal data which was took by FLIR ONE PRO. One of the possible outcome is Shown in Figure~\ref{fig:SeparateImage} if we divide the frame 6 times and it will have 19 regions. By this method, we can iteratively compress the thermal data until the number of regions reach our file size requirement or the error rate is less than the requirement.
|
|
|
|
|
|
\begin{figure}[ht]
|
|
\begin{minipage}[b]{0.45\linewidth}
|
|
\centering
|
|
\includegraphics[width=\linewidth]{figures/real.png}
|
|
\caption{PNG image, size = 46KB}
|
|
\label{fig:pngImage}
|
|
\end{minipage}
|
|
\hspace{0.05\linewidth}
|
|
\begin{minipage}[b]{0.45\linewidth}
|
|
\centering
|
|
\includegraphics[width=\linewidth]{figures/separate.png}
|
|
\caption{Region divided by our method}
|
|
\label{fig:SeparateImage}
|
|
\end{minipage}
|
|
\end{figure}
|
|
|
|
\subsection{Data Structure and Region Selection Algorithm}
|
|
|
|
To help us choose which region to be divided, we give every region a score, and put them into a heap. For each round, we pick the region with the highest score, and divide it into four subregions, calculate the score of subregions, and put them into the heap. We use the sum of square error of pixels in the region $R$ as the score of the region.
|
|
|
|
\begin{center}
|
|
\begin{tabular}{rl}
|
|
$\mu = $ & $E(R)$\\
|
|
$Score = $ & $\sum\limits_{X\in R} (X-\mu)^2$\\
|
|
$= $ & $\sum\limits_{X\in R} X^2 - |R|\mu^2$
|
|
\end{tabular}
|
|
\end{center}
|
|
|
|
By the equation shown above, we just need to know the sum of square and the sum of all pixels in the region to calculate the score of the region. We use a 4-dimension segment tree as a container to store all possible regions and its scores. Since segment tree is a complete tree, the size of tree is less than 2 times the number if pixels. For each node of segment tree, it records the range on both width and height it covered, sum $\sum\limits_{X\in R} X$, and sum of square $\sum\limits_{X\in R} X^2$ of pixels in the region. The root of segment tree starts is node number $0$, and each node $i$ has four child from node number from $i\times 4+1$ to $i\times 4+4$. Hence, we only need to allocate a large array and recursively process all nodes form root. Algorithm~\ref{code:SegmentTreePreprocess} shows how we generate the tree and calculate the sum of square and the sum of all nodes.
|
|
|
|
\begin{algorithm*}[h]
|
|
\caption{Segment Tree Preprocess}
|
|
\label{code:SegmentTreePreprocess}
|
|
\begin{algorithmic}[1]
|
|
\State $Tree = Array()$
|
|
\Function{setTreeNode}{$x, left, right, top, bottom$}
|
|
\If {$left = right$ \and $top = bottom$}
|
|
\State $Tree[x].Sum = Frame[left][top]$
|
|
\State $Tree[x].SquareSum = Frame[left][top]^2$
|
|
\Else
|
|
\State $setTreeNode(4x+1, left, (left+right)/2, top, (top+bottom)/2)$
|
|
\State $setTreeNode(4x+2, (left+right)/2, right, top, (top+bottom)/2)$
|
|
\State $setTreeNode(4x+3, left, (left+right)/2, (top+bottom)/2, bottom)$
|
|
\State $setTreeNode(4x+4, (left+right)/2, right, (top+bottom)/2, bottom)$
|
|
\State $Tree[x].Sum = \sum\limits_{i = 4x+1}^{4x+4} Tree[i].sum $
|
|
\State $Tree[x].SquareSum = \sum\limits_{i = 4x+1}^{4x+4} Tree[i].SquareSum$
|
|
\EndIf
|
|
\State $Tree[x].SquaredError = Tree[x].SquareSum - \frac{Tree[x].Sum^2}{(right-left+1)\times(bottom-top+1)}$
|
|
\EndFunction
|
|
\State $setTreeNode(0, 0, Frame.Width, 0, Frame.Height)$
|
|
\end{algorithmic}
|
|
\end{algorithm*}
|
|
|
|
For region selection, we use a priority queue to retrieve the region of considerate regions with highest score. The priority queue is made by heap, and start with only root of the segment tree. For each round the priority queue pop the item with highest score and push all its child in to the queue.
|
|
|
|
The compressed data size is depended on how many iterations of dividing the regions. The compressed data size will be about the number of iterations times four bytes. Algorithm~\ref{code:RegionSpecifiedSize} shows how we divide regions until specified data size.
|
|
|
|
\begin{algorithm*}[h]
|
|
\caption{Dividing regions depends on compressed data size}
|
|
\label{code:RegionSpecifiedSize}
|
|
\begin{algorithmic}[1]
|
|
\State $seperatedRegions = Array()$
|
|
\State $PriorityQueue = Heap()$
|
|
\State $PriorityQueue.Push(Tree[0].SquaredError, 0)$
|
|
\For{$i = 0..NumberOfIterations$}
|
|
\State $value, x = PriorityQueue.Pop()$
|
|
\State $seperatedRegions.push(x)$
|
|
\For{$j = 1..4$}
|
|
\State $PriorityQueue.Push(Tree[4x+j].SquaredError, 4x+j)$
|
|
\EndFor
|
|
\EndFor
|
|
\end{algorithmic}
|
|
\end{algorithm*}
|
|
|
|
The error rate of the compressed data is the sum of the squared error of regions in priority queue. Algorithm~\ref{code:RegionSpecifiedError} shows how we divide regions until specified RMSE.
|
|
|
|
\begin{algorithm*}[h]
|
|
\caption{Dividing regions depends on compressed data RMSE}
|
|
\label{code:RegionSpecifiedError}
|
|
\begin{algorithmic}[1]
|
|
\State $seperatedRegions = Array()$
|
|
\State $PriorityQueue = Heap()$
|
|
\State $PriorityQueue.Push(Tree[0].SquaredError, 0)$
|
|
\State $SquaredError = Tree[0].SquaredError$
|
|
\While{$\sqrt{(SquaredError/FrameSize)} > SpecifiedRMSE$}
|
|
\State $value, x = PriorityQueue.Pop()$
|
|
\State $seperatedRegions.push(x)$
|
|
\State $SquaredError$ -= $value$
|
|
\For{$j = 1..4$}
|
|
\State $PriorityQueue.Push(Tree[4x+j].SquaredError, 4x+j)$
|
|
\State $SquaredError$ += $Tree[4x+j].SquaredError$
|
|
\EndFor
|
|
\EndWhile
|
|
\end{algorithmic}
|
|
\end{algorithm*}
|
|
|
|
After the region dividing finished, we will generate the data string to be sent. The regions in $seperatedRegions$ will be replaced by a reminder for dividing and others in $PriorityQueue$ will be the average sensor reading in the region, and then compress the string by Huffman Coding.
|
|
|
|
The complexity of our algorithm can be divided into 3 parts. First part is to initialize the segment tree. The size of segment is depends on the size of the frame. If the number of pixels is $N$, the height of segment tree is $O(Nlog(N))$, and the number of nodes will be $O(N)$. The time complexity of initialize is $O(N)$. Second part is loading the thermal data. It will need to traverse whole tree from leaf to root. Since segment tree can be stored in an array, it also takes $O(N)$ time to load the thermal data. Third part is to divide regions. For each round, we pop an element from heap and push four elements into heap. If there is $K$ iterations, the size of heap will be $3K+1$. Time complexity of pop and push will be $O(log(K))$, and do it $5K$ times will be $O(Klog(K))$.
|