Added Arduino and Compression

This commit is contained in:
Kenneth Jao 2024-10-24 02:51:33 -04:00
parent 838e6e9d89
commit b023b561d8
15 changed files with 1072 additions and 0 deletions

View File

@ -0,0 +1,46 @@
#include <bcconfig.h>
#include <BigNumber.h>
#include <number.h>
#define numberOfSensors 50
int flexSensorPins[] = {A0, A1, A2, A3};
int mapValue;
String reference[] = {"L","H","R"};
String finalReadable;
String final;
int i = 0;
void setup() {
Serial.begin(9600);
BigNumber::begin();
}
String compression(int values[]) {
String combinedInt = "";
String header = "";
for (i = 0; i < numberOfSensors; i++) {
if(values[i] < 10) {
if(values[i] < 0) {
header += String(i) + ",";
combinedInt += String(values[i] * -1);
} else {
combinedInt += "0" + String(values[i]);
}
} else {
combinedInt += String(values[i]);
}
}
return header.substring(0,header.length()-1) + "." + combinedInt;
}
void loop() {
finalReadable = "";
final = "";
int dataValues[] = {32, 62, 10, 7, -45, 91, -95, 27, 54, 50, 67, 84, 92,
68, 10, 53, 79, 65, 06, 62, 60, -15, 52, 63, 71, 9,
52, -86, 68, 52, 96, 30, 31, 50, 24, 56, -61, 54, 40, 26,
33, 34, 32, 28, 2, 96, 3, 77, 66, 97};
Serial.println(compression(dataValues));
delay(250);
}

View File

@ -0,0 +1,88 @@
#include <bcconfig.h>
#include <BigNumber.h>
#include <number.h>
#define numberOfSensors 50
int flexSensorPins[] = {A0, A1, A2, A3};
int mapValue;
String reference[] = {"L","H"};
String finalReadable;
String final;
int i = 0;
void setup() {
Serial.begin(9600);
BigNumber::begin();
}
String toBinary(String intString, int len) {
String binary = "";
char s[len];
intString.toCharArray(s, len);
BigNumber lrgInt(s);
BigNumber two = 2;
BigNumber zero = 0;
int remainder;
while (lrgInt > zero) {
remainder = lrgInt % two;
lrgInt = lrgInt / two;
lrgInt = lrgInt - lrgInt % lrgInt
lrgInt
binary = String(remainder) + binary;
Serial.println(binary);
}
return binary;
}
String compression(int values[]) {
String finalCompress = "";
String combinedInt = "";
String binString = "";
char* s;
int counter = 0;
int change = 0;
for (i = 0; i < numberOfSensors; i++) {
if(String(values[i]).length() < 2) {
combinedInt += "0" + String(values[i]);
} else {
combinedInt += String(values[i]);
}
}
binString = toBinary(combinedInt, combinedInt.length());
for (i = 0; i <= binString.length(); i++) {
if (binString.charAt(i) == binString.charAt(change)) {
counter += 1;
} else {
if(counter == 1) {
finalCompress = finalCompress + reference[String(binString.charAt(0)).toInt()];
} else {
finalCompress = finalCompress + String(counter) + reference[String(binString.charAt(0)).toInt()];
}
change = i;
counter = 1;
}
}
return finalCompress;
}
void loop() {
finalReadable = "";
final = "";
int dataValues[] = {32, 62, 10, 7, 45, 91, 95, 27, 54, 50, 67, 84, 92, 68, 10,
53, 79, 65, 6, 62, 60, 15, 52, 63, 71, 9, 52, 86,
68, 52, 96, 30, 31, 50, 24, 56, 61, 54, 40, 26,
33, 34, 32, 28, 2, 96, 3, 77, 66, 97};
//for(int i = 0; i < numberOfSensors; i++) {
//mapValue = map(analogRead(flexSensorPins[i]), 512, 853, 0, 180) - 25;
//finalReadable = finalReadable + String(mapValue) + ",";
//dataValues[i] = mapValue;
String hello = compression(dataValues);
//Serial.println(hello);
delay(250);
}

View File

@ -0,0 +1,46 @@
#define numberOfSensors 3
int muxPins[] = {5,6,7};
int dataPin = A0;
int lastDataValues[] = {0,0,0};
int i = 0;
String delLast(String input) {
return input.substring(0,input.length()-1);
}
void setup() {
for(i = 0; i < sizeof(muxPins); i++) {
pinMode(muxPins[i],OUTPUT);
digitalWrite(muxPins[i],LOW);
}
pinMode(dataPin, INPUT);
Serial.begin(115200);
}
void loop() {
digitalWrite(5,LOW);digitalWrite(6,LOW);digitalWrite(7,HIGH);
String finalShort = "";
String finalReal = "";
String raw = "";
int mapValue = 0;
for(int i = 0; i < numberOfSensors; i++) {
String bin = String(i,BIN);
while(bin.length() < 3) {
bin = "0" + bin;
}
for(int j = 0; j < 3; j++) {
digitalWrite(muxPins[j],bin.substring(bin.length()-j-1,bin.length()-j).toInt());
}
mapValue = map(analogRead(dataPin), 530, 810, 0, 180);
finalShort = finalShort + String(mapValue - lastDataValues[i]) + ",";
finalReal = finalReal + String(mapValue) + ",";
raw = raw + String(analogRead(dataPin)) + ",";
lastDataValues[i] = mapValue;
}
Serial.println(delLast(raw) + " :: " + delLast(finalReal) + " :: " + delLast(finalShort));
delay(100);
}

View File

@ -0,0 +1,192 @@
// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class using DMP (MotionApps v2.0)
// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#include "Wire.h"
/* I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files
* for both classes must be in the include path of your project
*/
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#define NUMBER_OF_SENSORS 3 //// YOU MAY NEED TO CHANGE THIS
// Default I2C address is 0x68
// AD0 LOW(0) = 0x68 (Default for SparkFun breakout and InvenSense evaluation board)
// AD0 HIGH(1) = 0x69
// MPU Control Variables
MPU6050 mpu;
bool dmpReady; // Set true if DMP init was successful.
uint8_t devStatus; // Return status after each device operation. (0 = success, !0 = error)
uint8_t mpuIntStatus; // Holds interrupt status byte from MPU.
uint16_t packetSize; // Expected DMP packet size. (Default is 42 bytes)
uint16_t fifoCount; // Count of all bytes currently in FIFO.
uint8_t fifoBuffer[64]; // FIFO storage buffer.
// Orientation and Motion Variables
Quaternion q; // [W, X, Y, Z] Quaternion container.
VectorFloat gravity; // [X, Y, Z] Gravity vector
float ypr[3]; // [Yaw, Pitch, Roll] array container.
//Digital Pins Reference Variables
const int latchPin = 2; //ST_CP or RCLK
const int clockPin = 1; //SH_CP or SRCLK
const int dataPin = 3; //DS or SER
// Other Variables
String finalParts[NUMBER_OF_SENSORS];
String final = "";
//==============================================================
void switchSensor(int sensorNumber) {
/* The shift register shifts out bits to the ADO lines. In binary:
* 0001 = Sensor on Q0(QA) 2^0 = 1 = 0001 in binary
* 0010 = Sensor on Q1(QB) 2^1 = 2 = 0010 in binary
*/
int data = (int) 1 << (sensorNumber - 1); // Shift register serial data input.
digitalWrite(latchPin, LOW); // Hold low for as long as you are transmitting data.
shiftOutBits(dataPin, clockPin, ~data); // Shift out the bits into the shift register; negate data. (0001 = 1000)
digitalWrite(latchPin, HIGH); // Ends transmission of data.
}
void shiftOutBits(int myDataPin, int myClockPin, byte myDataOut) {
// This shifts 8 bits out MSB first.
//Function Setup
int l = 0;
int pinState;
pinMode(myClockPin, OUTPUT);
pinMode(myDataPin, OUTPUT);
// Clear everything out in case to prepare shift register.
digitalWrite(myDataPin, 0);
digitalWrite(myClockPin, 0);
// For each bit in the byte myDataOut.
for (l = 7; l >= 0; l--) {
digitalWrite(myClockPin, 0);
//if the value passed to myDataOut and a bitmask result
// true then... so if we are at i=6 and our value is
// %11010100 it would the code compares it to %01000000
// and proceeds to set pinState to 1.
if ( myDataOut & (1 << l) ) {
pinState = 1;
//////Serial.print(pinState);
}
else {
pinState = 0;
//////Serial.print(pinState);
}
// Sets the pin to HIGH or LOW depending on pinState.
digitalWrite(myDataPin, pinState);
// Register shifts on HIGH of myClockPin.
digitalWrite(myClockPin, 1);
// Stop sending on myDataPin.
digitalWrite(myDataPin, 0);
}
// Stop shifting.
digitalWrite(myClockPin, 0);
}
// ================================================================
// === MAIN PROGRAM SETUP ===
// ================================================================
void setup() {
delay(2000);
Wire.begin(); // Join I2C bus. (I2Cdev library doesn't do this automatically.)
TWBR = 24; // Sets frequency of the clock (SCL) higher.
Serial.begin(115200); // Initialize serial communication with baud rate.
pinMode(latchPin, OUTPUT); // Tell the Arduino to send or recieve signals.
int dmpReadyCounter = 0; // Counts number of sensors ready.
for (int i = 1; i <= NUMBER_OF_SENSORS; i++) {
/* We read data from all sensors by switching addresses one by one, only reading from the first address (0x68).
* Therefore, we shift the addresses to the next sensor, and retrieve its value.
*/
switchSensor(i);
mpu.initialize(); // Intialize device.
devStatus = mpu.dmpInitialize(); // Load and configure the DMP. (Digital Motion Processor)
// Gyroscope offsets. (Change if necessary)
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788);
// Check success of DMP.
if (devStatus == 0) {
mpu.setDMPEnabled(true);
dmpReadyCounter += 1; // Add one to count number of ready sensors.
packetSize = mpu.dmpGetFIFOPacketSize(); // Get expected DMP packet size for later comparison
} else {
// Error!
Serial.print("Error on sensor " + String(i) + "\n");
}
/////////Serial.print(String(digitalRead(8)) + String(digitalRead(9)) + String(digitalRead(10)) + String(digitalRead(11)) + "\n");
}
if (dmpReadyCounter == 3) {
dmpReady = true; // Set DMP Ready flag. (Allows main loop to use the DMP.)
}
}
// ================================================================
// === MAIN PROGRAM LOOP ===
// ================================================================
void loop() {
final = ""; // Reset final to nothing.
// If DMP isn't ready...
if (!(dmpReady)) {
return;
}
for (int k = 1; k <= NUMBER_OF_SENSORS; k++) {
switchSensor(k);
// Check for overflow.
if (fifoCount == 1024) {
mpu.resetFIFO(); // Reset so we can continue cleanly.
} else {
fifoCount = mpu.getFIFOCount(); // Get current FIFO count.
// Wait for correct avaliable data length.
while (fifoCount < packetSize) {
fifoCount = mpu.getFIFOCount();
}
mpu.getFIFOBytes(fifoBuffer, packetSize); // Read a packet from FIFO
/* Track FIFO count in case there is more than 1 packet avaliable.
* (Read more without waiting for an interrupt.)
*/
fifoCount -= packetSize;
// Get values to process.
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
// Concatenate for outputting. (Displays in Euler Angles in degrees.)
finalParts[k - 1] = String(ypr[0] * 180 / M_PI) + "," + String(ypr[1] * 180 / M_PI) + "," + String(ypr[2] * 180 / M_PI) + ":";
final += finalParts[k - 1];
}
}
Serial.print(final);
Serial.print("\n");
}

View File

@ -0,0 +1,30 @@
int muxPins[3] = {5,6,7};
int dataPin = 13;
void setup() {
for(int i = 0; i < 3; i++) {
pinMode(muxPins[i],OUTPUT);
digitalWrite(muxPins[i],LOW);
}
pinMode(dataPin, OUTPUT);
digitalWrite(dataPin,HIGH);
Serial.begin(115200);
}
void loop() {
for(int i = 0; i < 8; i++) {
String bin = String(i,BIN);
while(bin.length() < 3) {
bin = "0" + bin;
}
for(int j = 0; j < 3; j++) {
digitalWrite(muxPins[j],bin.substring(bin.length()-j-1,bin.length()-j).toInt());
}
Serial.println(" ");
delay(50);
}
// put your main code here, to run repeatedly:
}

View File

@ -0,0 +1,75 @@
int inA1 = 8; // wire 1 (blue)
int inA2 = 9; // wire 2 (pink)
int inB1 = 10; // wire 3 (yellow)
int inB2 = 11; // wire 4 (orange)
int counter = 0;
//wire 5 (Red) is a VCC and should be put to 5V, with this setup it is not needed, but it is good to know if you make something like an 8 step spinup
int stepDelay = 2;
void setup() {
pinMode(inA1, OUTPUT);
pinMode(inA2, OUTPUT);
pinMode(inB1, OUTPUT);
pinMode(inB2, OUTPUT);
pinMode(A1,INPUT);
}
void loop(){
Serial.println(String(analogRead(A1)));
Serial.println("hi");
/*for(int i =0;i<1600;i++) {
step1();
step2();
step3();
step4();
}
stopMotor();
delay(500);
for(int j=0;j<1600;j++) {
step3();
step2();
step1();
step4();
}
stopMotor();
delay(500);*/
}
void step1() {
digitalWrite(inA1, LOW);
digitalWrite(inA2, HIGH);
digitalWrite(inB1, HIGH);
digitalWrite(inB2, LOW);
delay(stepDelay);
}
void step2() {
digitalWrite(inA1, LOW);
digitalWrite(inA2, HIGH);
digitalWrite(inB1, LOW);
digitalWrite(inB2, HIGH);
delay(stepDelay);
}
void step3() {
digitalWrite(inA1, HIGH);
digitalWrite(inA2, LOW);
digitalWrite(inB1, LOW);
digitalWrite(inB2, HIGH);
delay(stepDelay);
}
void step4() {
digitalWrite(inA1, HIGH);
digitalWrite(inA2, LOW);
digitalWrite(inB1, HIGH);
digitalWrite(inB2, LOW);
delay(stepDelay);
}
void stopMotor() {
digitalWrite(inA1, LOW);
digitalWrite(inA2, LOW);
digitalWrite(inB1, LOW);
digitalWrite(inB2, LOW);
}

View File

@ -0,0 +1,67 @@
const int latchPin = 8; //ST_CP or RCLK
const int clockPin = 12; //SH_CP or SRCLK
const int dataPin = 11; //DS or SER
byte seven_seg_digits[10][7] = {
{ 1,1,1,1,1,1,0 }, // = 0
{ 0,1,1,0,0,0,0 }, // = 1
{ 1,1,0,1,1,0,1 }, // = 2
{ 1,1,1,1,0,0,1 }, // = 3
{ 0,1,1,0,0,1,1 }, // = 4
{ 1,0,1,1,0,1,1 }, // = 5
{ 1,0,1,1,1,1,1 }, // = 6
{ 1,1,1,0,0,0,0 }, // = 7
{ 1,1,1,1,1,1,1 }, // = 8
{ 1,1,1,0,0,1,1 } // = 9
};
int digits[10] = {
126, 48, 109, 121, 51, 91, 95, 112, 127,
}
void writeNumber(int character) {
/* The shift register shifts out bits to the ADO lines. In binary:
* 1110 = Sensor on Q0(QA) ~(2^0) = ~1 = 1110 in binary
* 1101 = Sensor on Q1(QB) ~(2^1) = ~2 = 1101 in binary
* '~' = not
*/
//int data = (int) 1 << (sensorNumber - 1); // Shift register serial data input.
digitalWrite(latchPin, LOW); // Hold low for as long as you are transmitting data.
shiftOutBits(dataPin, clockPin, character); // Shift out the bits into the shift register; negate data. (0001 = 1000)
digitalWrite(latchPin, HIGH); // Ends transmission of data.
}
void shiftOutBits(int myDataPin, int myClockPin, int number) {
// This shifts 8 bits out MSB first.
// Setup Varaibles
int pinState;
pinMode(myClockPin, OUTPUT);
pinMode(myDataPin, OUTPUT);
// Clear everything out in case to prepare shift register.
digitalWrite(myDataPin, 0);
digitalWrite(myClockPin, 0);
// For each bit in the byte myDataOut.
for (int i = 7; i >= 0; i--) {
digitalWrite(myClockPin, 0);
pinState = 1 - seven_seg_digits[number][i];
digitalWrite(myDataPin, pinState); // Sets the pin to HIGH or LOW depending on pinState.
digitalWrite(myClockPin, 1); // Register shifts on HIGH of myClockPin.
digitalWrite(myDataPin, 0); // Stop sending on myDataPin.
}
digitalWrite(myClockPin, 0); // Stop shifting.
}
void setup() {
writeNumber(5);
}
void loop() {
return;
}

54
Arduino/Transfer.py Normal file
View File

@ -0,0 +1,54 @@
import serial
port = serial.Serial('COM6', 9600)
valuesBeforeAveraging = 6
numberOfSensors = 3
splitValues = []
averageSetLength = 0
nan = ['nan','na']
def getNewValue():
newRead = port.readline()[:-2].lower()
if nan[0] in newRead or nan[1] in newRead:
print "FIFO Buffer Overflow"
getNewValue()
else:
return newRead
def getNewAverage(newValue):
global splitValues
global averageSetLength
average = 0.0000
outputLine = ""
splitValues.append([]) # Append new trial
if averageSetLength > valuesBeforeAveraging:
del splitValues[0]
averageSetLength = len(splitValues)
for yprNum, ypr in enumerate(newValue.split(":")): # For each sensor/YPR set
splitValues[averageSetLength - 1].append([]) # Append new array, compensate for array start 0
for valueNum, value in enumerate(ypr.split(",")): # For each value in YPR set
splitValues[averageSetLength - 1][yprNum].append(float(value)) # Append value to array
for entryNum in xrange(0, averageSetLength): #For all trials
average += splitValues[entryNum][yprNum][valueNum] # Add all x,y,z values
average = average / (averageSetLength) # Divide by number of trials
outputLine = outputLine + str(average)[:str(average).find('.') + 4] + ","
average = 0.0000
outputLine = outputLine[:-1] + ":"
outputLine = outputLine[:-1]
return outputLine
while 1:
try:
newRead = getNewValue()
final = getNewAverage(newRead)
print final
file = open('Transfer.txt','w')
file.write(final)
file.close()
except: IOError

View File

@ -0,0 +1,86 @@
//**************************************************************//
// Name : shiftOutCode, Dual Binary Counters //
// Author : Carlyn Maw, Tom Igoe //
// Date : 25 Oct, 2006 //
// Version : 1.0 //
// Notes : Code for using a 74HC595 Shift Register //
// : to count from 0 to 255 //
//**************************************************************//
//Pin connected to ST_CP of 74HC595
int latchPin = 2;
//Pin connected to SH_CP of 74HC595
int clockPin = 1;
////Pin connected to DS of 74HC595
int dataPin = 3;
void setup() {
//Start Serial for debuging purposes
Serial.begin(9600);
//set pins to output because they are addressed in the main loop
pinMode(latchPin, OUTPUT);
digitalWrite(5, LOW);
}
void loop() {
//count up routine
//ground latchPin and hold low for as long as you are transmitting
digitalWrite(latchPin, 0);
//count down on RED LEDs
shiftOut(dataPin, clockPin, 2);
//return the latch pin high to signal chip that it
//no longer needs to listen for information
digitalWrite(latchPin, 1);
delay(1000);
Serial.print(String(digitalRead(8)) + String(digitalRead(9)) + String(digitalRead(10)) + String(digitalRead(11)) + "\n");
//Serial.print(digitalRead(8));
}
void shiftOut(int myDataPin, int myClockPin, byte myDataOut) {
// This shifts 8 bits out MSB first,
//on the rising edge of the clock,
//clock idles low
//internal function setup
int i=0;
int pinState;
pinMode(myClockPin, OUTPUT);
pinMode(myDataPin, OUTPUT);
//clear everything out just in case to
//prepare shift register for bit shifting
digitalWrite(myDataPin, 0);
digitalWrite(myClockPin, 0);
//for each bit in the byte myDataOut<75>
//NOTICE THAT WE ARE COUNTING DOWN in our for loop
//This means that %00000001 or "1" will go through such
//that it will be pin Q0 that lights.
for (i=7; i>=0; i--) {
digitalWrite(myClockPin, 0);
//if the value passed to myDataOut and a bitmask result
// true then... so if we are at i=6 and our value is
// %11010100 it would the code compares it to %01000000
// and proceeds to set pinState to 1.
if ( myDataOut & (1<<i) ) {
pinState= 1;
}
else {
pinState= 0;
}
//Sets the pin to HIGH or LOW depending on pinState
digitalWrite(myDataPin, pinState);
//register shifts bits on upstroke of clock pin
digitalWrite(myClockPin, 1);
//zero the data pin after shift to prevent bleed through
digitalWrite(myDataPin, 0);
}
//stop shifting
digitalWrite(myClockPin, 0);
}

BIN
Compression/3DBlocks.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

BIN
Compression/4DBlocks.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -0,0 +1,125 @@
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[margin=1in]{geometry}
\usepackage{amsfonts, amsmath, amssymb}
\usepackage[none]{hyphenat}
\usepackage{fancyhdr}
\usepackage{titling}
\usepackage{changepage}
\usepackage{graphicx}
\usepackage{wrapfig}
\renewcommand\maketitlehooka{\null\mbox{}\vfill}
\renewcommand\maketitlehookd{\vfill\null}
\pagestyle{fancy}
\fancyhead[L]{\slshape Algorithm Details}
\fancyhead[R]{Kenneth Jao}
\fancyfoot[C]{\thepage}
\title{Lossy Compression through Multi-dimensional Fourier Transforms }
\author{Kenneth Jao}
\date{February 2019}
\begin{document}
\maketitle
\pagebreak
\section{Introduction}
This document covers the details of the mathematics of the compression algorithm. Each main mathematical application will be explained in detail. Firstly an overview of the algorithm as a whole will be given.
\subsection{Notation}
\begin{adjustwidth}{.5in}{}
Data Matrix: \[ \left[ {\begin{array}{cccc} A & B & C & \dots \end{array}} \right] or \left[\ A,\ B,\ C,\ \dots \ \right]\]
Will mean that the dimensions 0, 1, 2, \dots will correspond to A, B, C, \dots and represents an array of data, not a mathematical matrix.\\
\newline
N-Dimensional Polynomial:\\
\begin{adjustwidth}{.5in}{}
$\vec{e_i}=(e_1,\ e_2,\ e_3, \dots e_n) \in \mathbb{N}^n$ will represent the i-th permutation of $\vec{e}$.\\
$\vec{x}^{\vec{e}} = x_1^{e_1}\cdot x_2^{e_2}\cdot \dots x_n^{e_n}$ where $\vec{x}=(x_1,\ x_2,\ x_3, \dots x_n) \in \mathbb{R}^n$\\
An arbitrary n-dimensional polynomial of degree m $P(\ x_1,\ x_2,\ x_3,\dots,\ x_n)$ will equal: \[ a_1x^{\vec{e_1}} + a_2x^{\vec{e_2}} + a_3x^{\vec{e_3}} + \dots + a_{max}x^{\vec{e_{max}}}\]
Where $max=(m+1)^n$
\end{adjustwidth}
\end{adjustwidth}
\subsection{Algorithm Overview}
\begin{adjustwidth}{.5in}{}
A set of video data in given in the format: \[ \left[ {\begin{array}{cccc} Frame & Y Pixel & X Pixel & RGB \end{array}} \right] \]
After receiving this video data, it will be processed into square blocks of dimension $N$, denoted by $Block_{w_1,w_2,w_3,...}$ where ${w_1, w_2, w_3,...}$ represents the sorting of $N$ dimensional blocks in a format that correspond to the location of the data in the ordered video. For instance, given a data in format $[\ x,\ y,\ z\ ]$, square blocks of dimension $N$ can be extracted such that the new data structure is now in the form $[\frac{x}{N},\frac{y}{N},\frac{z}{N}, N, N, N]$, and thus, in this case, $w_1 = \frac{x}{N},\ w_2 = \frac{y}{N},\ w_3 = \frac{z}{N}$. Afterwards, there are two possible steps.
\begin{enumerate}
\item The first option is to purely fit a polynomial to the data of each block. That is, find a polynomial of degree $N-1$ and of dimension $N$ where each $Block_{w_1,w_2,w_3,...}[\ x_1,\ x_2,\ x_3,\ \dots \ ]$ will correspond to some $P(\ x_1,\ x_2,\ x_3,\ \dots,\ x_n)$. The output will be the coefficients of the new polynomial.
\item The second option is to attempt to create a polynomial of degree $N-1$ and of dimension $N$ where its coefficients match first $N$ terms of the Taylor Polynomial of $cos(x_1+x_2+x_3+\dots +x_n)$. The coefficients of the new polynomial will attempt to as close to the Taylor Polynomial. The output will be the coefficients of the new polynomial along with a separate function, its usage explained later in the methodology.
\end{enumerate}
\noindent An $N$ dimensional Discrete Fourier Transform (DFT) will now be performed on these blocks. The output is the first $k$ coefficients, where $k$ is determined when the sum of a sufficient $k$ coefficients is greater than 90\% of the convergence of the $N$ dimensional Fourier Series. The 'first' components will be determined by a counting method discussed later.\\
\newline
Now, these coefficients corresponding to each $Block_{w_1,w_2,w_3,...}$ will be flattened to 2 dimensions. A Discrete Cosine Transform (DCT) will now be applied onto these coefficients, with the rounding threshold higher, for the sake of precision on coefficients.\\
\newline
The algorithm is now complete. The final products will include: a 2 dimensional DCT of the coefficients of an $N$ dimensional DFT, function for each block. The raveling process will be standard for a given dimensional size, so this needs not be stored.
\end{adjustwidth}
\pagebreak
\section{Changing Dimensions}
Firstly, the video is turned into a basic 3 dimensions. This is done by stacking the Frame and RGB dimensions and swapping the Y Pixel and X Pixel to make it more intuitively oriented. The new form becomes: \[ \left[ {\begin{array}{ccc} X Pixel & Y Pixel & Frame+RGB \end{array}} \right] \]
That is to say, $[\ x,\ y,\ 3f\ ]$, $[\ x,\ y,\ 3f+1\ ]$, $[\ x,\ y,\ 3f+2\ ]$ would lie in frame $f$, and represent the R, G, and B values, respectively. From here, we can start slicing the data in certain ways to obtain our desired block dimension.
\begin{figure}[h]
\centering
\includegraphics[width=2in]{images/RGB.png}
\caption{Visualization of flattened video data.}
\end{figure}
\subsection{3-Dimensional Block}
\begin{adjustwidth}{.5in}{}
To obtain 3-Dimensional blocks, in this case, we simply can just extract cubes from the already flattened video data. (See Figure 2.) This arises in a data format of $[\frac{x}{N},\frac{y}{N},\frac{z}{N}, N, N, N]$. In this algorithm, $N=6$ was chosen.
\begin{figure}[h]
\centering
\includegraphics[width=3in]{images/3DBlocks.png}
\caption{3D Blocked Video Data}
\end{figure}
\end{adjustwidth}
\subsection{4-Dimensional Block}
To obtain 4-Dimensional blocks, in this case, we extract a $NxNxN$ cube and then take $N$ cubes downward. (See Figure 3.) This arises in a data format of $[\frac{x}{N},\frac{y}{N^2},\frac{z}{N}, N, N, N, N]$. In this algorithm, $N=6$ was chosen.
\begin{figure}[h]
\centering
\includegraphics[width=3in]{images/4DBlocks.png}
\caption{4D Blocked Video Data}
\end{figure}
\section{Polynomial Fitting}
There are two potential outputs of polynomial fitting. FINISH
\subsection{Pure Polynomial Fitting}
The given data $D$ is in format: \[ \left[ \begin{array}{ccccc} x_1 & x_2 & x_3 & \dots & x_n \end{array} \right] \]
Where its size is $(m+1,\ m+1,\, m+1,\ \dots,\ m+1)$. To find an n-dimensional polynomial of degree m, all that is necessary is find a solution to this system of linear equations: \
\[ \left[ \begin{array}{ccccc}
\vec{e_1}^{\vec{e_1}} & \vec{e_1}^{\vec{e_2}} & \vec{e_1}^{\vec{e_3}} & \dots & \vec{e_1}^{\vec{e_{max}}}\\
\vec{e_2}^{\vec{e_1}} & \vec{e_2}^{\vec{e_2}} & \vec{e_2}^{\vec{e_3}} & \dots & \vec{e_2}^{\vec{e_{max}}}\\
\vec{e_3}^{\vec{e_1}} & \vec{e_3}^{\vec{e_2}} & \vec{e_3}^{\vec{e_3}} & \dots & \vec{e_3}^{\vec{e_{max}}}\\
\vdots & \vdots & \vdots & \vdots & \vdots \\
\vec{e_{max}}^{\vec{e_1}} & \vec{e_{max}}^{\vec{e_2}} & \vec{e_{max}}^{\vec{e_3}} & \dots & \vec{e_{max}}^{\vec{e_{max}}}
\end{array} \right]
\left[ \begin{array}{c} a_1 \\ a_2 \\ a_3 \\ \vdots \\ a_{max} \end{array} \right] =
\left[ \begin{array}{c} D[\vec{e_1}] \\ D[\vec{e_2}] \\ D[\vec{e_3}] \\ \vdots \\ D[\vec{e_{max}}] \end{array} \right]\]
Thus, to find the coefficients, all that is necessary is to compute:
\[ {\left[ \begin{array}{ccccc}
\vec{e_1}^{\vec{e_1}} & \vec{e_1}^{\vec{e_2}} & \vec{e_1}^{\vec{e_3}} & \dots & \vec{e_1}^{\vec{e_{max}}}\\
\vec{e_2}^{\vec{e_1}} & \vec{e_2}^{\vec{e_2}} & \vec{e_2}^{\vec{e_3}} & \dots & \vec{e_2}^{\vec{e_{max}}}\\
\vec{e_3}^{\vec{e_1}} & \vec{e_3}^{\vec{e_2}} & \vec{e_3}^{\vec{e_3}} & \dots & \vec{e_3}^{\vec{e_{max}}}\\
\vdots & \vdots & \vdots & \vdots & \vdots \\
\vec{e_{max}}^{\vec{e_1}} & \vec{e_{max}}^{\vec{e_2}} & \vec{e_{max}}^{\vec{e_3}} & \dots & \vec{e_{max}}^{\vec{e_{max}}}
\end{array} \right]}^{-1}
\left[ \begin{array}{c} D[\vec{e_1}] \\ D[\vec{e_2}] \\ D[\vec{e_3}] \\ \vdots \\ D[\vec{e_{max}}] \end{array} \right]\]
\noindent Thus, $a_1,\ a_2,\ a_3, \dots,\ a_3$ has now been found.
\subsection{Cosine Taylor Polynomial Fitting}
\end{document}

BIN
Compression/Blocks.blend Normal file

Binary file not shown.

BIN
Compression/RGB.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

263
Compression/compress.py Normal file
View File

@ -0,0 +1,263 @@
import math
import time
import sys
import cv2 as cv
import numpy as np
import scipy.linalg as spla
from functools import reduce
from os import system
from PIL import Image
def read_video(limit = -1):
video = []
cap = cv.VideoCapture('D:/Videos/test.mkv')
frame_num = 0
while cap.isOpened():
if limit == frame_num:
break
ret, frame = cap.read()
video.append(frame)
system('cls')
print("Loading video: {:.2f}%".format(100*frame_num/limit))
frame_num += 1
return video
def change_dimension(data, dim):
'''
Video data is in form [Frame][Y Pixel][X Pixel][RGB]
Maximum size should be 10, but smaller is better.
Eventually, all dimensional changing should be done in C, programmed
specifically for each dimension.
'''
def cubify(data, s):
c = tuple(np.array(data.shape)//s)
cubed = np.zeros(tuple(list(c)+[s]*len(c)), dtype=int)
trim = [slice(0,s)]
def permute(n, dim=[]):
if n == len(c):
slice_ind = [slice(dim[i]*s, (dim[i]+1)*s) for i in range(n)]
cubed[tuple(dim)] = data[tuple(slice_ind)]
else:
for x in range(c[n]):
permute(n+1, dim+[x])
permute(0)
return cubed
if dim not in [3,4]:
raise Exception("Not an available target dimension!")
if dim == 3:
# Outer Form: [X Block][Y Block][Z Block]
# Inner Form: [X Pixel][Y Pixel][Frame+RGB] Size: 6
s = 6
blocks = np.swapaxes(np.concatenate(data, axis=2), 0, 1)
blocks = cubify(blocks, s)
elif dim == 4:
# Outer Form [X Block][Y Block][Z Block]
# Inner Form [Psuedo-Y Block][X Pixel][Y Pizel][Frame+RGB] Size: 6
s = 6
blocks = change_dimension(data, 3)
c = list(blocks.shape)[:3]
c[2] = c[2]//s
cubed = np.zeros(tuple(c+[s]*4), dtype=int)
for x in range(c[0]):
for y in range(c[1]):
for z in range(c[2]):
arr = np.concatenate(blocks[x:x+1][0][y:y+1,s*z:s*(z+1)])
cubed[x,y,z] = arr
blocks = cubed
return blocks
def permutations(arr):
'''
The input array will carry possible permutations.
'''
final = []
def permute(n, items=[]):
if n == len(arr):
final.append(items)
else:
for x in arr[n]:
permute(n+1, items+[x])
permute(0)
return final
def pure_polynomial_fit(blocks, dim): # RESIZE TO 2 PI !!!!!!#
'''
A polynomial of P(n-1) is generated with an n dimensional block,
by calculating the a system of n equations, solved through LU
decomposition.
'''
def permute(c, n, dim=[]):
if n == len(c):
d = tuple(dim)
b = blocks[d].flatten()[::-1]
pb = p_inv.dot(b)
y = spla.solve_triangular(l, pb, lower=True, check_finite=False)
x = spla.solve_triangular(u, y, check_finite=False)
block_polynomials[d] = x
if np.sum(dim[1:]) == 0:
system('cls')
print("Fit Poly Block {:.2f}%".format(100*dim[0]/c[0]))
else:
for x in range(c[n]):
permute(c, n+1, dim+[x])
degree = blocks.shape[-1] - 1
deg_perm = permutations(dim*[list(reversed(range(degree+1)))])
# Scale the coordinate system from 0 to 2pi, for preparation of DFT.
block_coord_perm = (2*math.pi/(degree+1)) * deg_perm[:]
mat_size = int(math.pow(degree + 1, dim))
a = np.zeros((mat_size, mat_size), dtype=np.uint64)
for i,perm in enumerate(block_coord_perm):
a[i] = np.prod(np.power(perm, deg_perm), axis=1, dtype=np.uint64)
p, l, u = spla.lu(a)
p_inv = np.linalg.inv(p)
poly_shape = list(blocks.shape[:-dim]) + [mat_size]
block_polynomials = np.zeros(tuple(poly_shape), dtype=np.float64)
permute(blocks.shape[:-dim], 0)
return block_polynomials
def trig_taylor_fit(block):
'''
Assume the polynomial will be a n-dimensional taylor polynomial,
calculate a polynomial regression that will represent the inputs.
'''
pass
def nd_dft(poly, dim):
'''
This function calculates a N-dimensional Discrete Fourier Transform,
specifically where f(t) is a polynomial.
'''
def get_integral_mat(n, x, zero, imag):
'''
Matrix represents T: P -> K, where P,K have basis' respectively:
P: x^n, x^(n-1), x^(n-2), ..., c, ix^n, ix^(n-1), ix^(n-2), ..., ic
K: c, 1/k, 1/k^2, ..., 1/k^n, ic, i/k, i/k^2, ..., i/k^n
This function returns a matrix to calculate the one-dimensional
integral. If the argument zero is true, the function will return
a matrix assuming the definite integral is from 0.
'''
mat_rr, mat_ri, mat_ir, mat_ii = [], [], [], []
def get_mat_part(zero_row_cond, neg_cond):
mat_part = []
for row in range(n+1):
if row % 2 == zero_row_cond:
mat_part.append([0]*(n+1))
continue
else:
row_vec = []
for col in range(n+1):
x_pow = n-col-row
if x_pow < 0:
row_vec.append(0)
elif x_pow == 0 and zero:
row_vec.append(0)
else:
neg = -1 if (row - neg_cond) % 4 == 0 else 1
const = math.factorial(n-col)/math.factorial(n-col-row)
row_vec.append(neg*const*math.pow(x, n-col-row))
mat_part.append(row_vec)
return np.array(mat_part, dtype=np.float64)
mat_r = np.concatenate([get_mat_part(0, 3), get_mat_part(1, 2)])
if imag:
mat_i = np.concatenate([get_mat_part(1, 0), get_mat_part(0, 3)])
return np.concatenate([mat_r, mat_i], axis=1)
else:
return mat_r
def get_mono_mats(block):
rind, iind = np.nonzero(rblock), np.nonzero(iblock)
rmono_a, imono_a = rblock[rind], iblock[iind]
rmono_deg = np.subtract(deg, np.array(deg_perm)[rind].transpose())
imono_deg = np.subtract(2*deg+1, np.array(deg_perm)[iind].transpose())
mono_count = len(rmono_a) + len(imono_a)
mono_mats = np.zeros((dim, mono_count, 2*deg+2))
for x in range(dim):
mono_deg = np.concatenate([rmono_deg, imono_deg], axis=1)
mono_mat[x][np.arange(mono_count), mono_deg[x]]
return mono_mats
def precompute_integrals(k_max, dim, degree):
'''
This function returns all possible combinations of monomial
integrals precomputed for all 'k' until k_max excluding k = 0.
'''
integral_k = np.zeros((degree, k_max-1), dtype=np.float64)
mat = get_integral_mat(degree, 2*math.pi, True, False)
k_col = np.arange(1, k_max).reshape((k_max-1, 1))
k_pow = np.power(k_col, -np.arange(1, degree+2).astype('float64'))
k_b = np.concatenate([k_pow, 1j*k_pow], axis=1).transpose()
mono_integrals = mat.transpose().dot(k_b)
# Offset by degree for deg_perm to fit basis x^n, x^(n-1) ... 1.
deg_perm = permutations(dim*[list(range(degree+1))])
k_perm = magnitude_spiral(dim*[list(range(1, k_max))])
poly_integrals = []
for deg in deg_perm:
k_perm_prod = []
for k_vec in k_perm:
k_perm_prod.append(np.prod(mono_integrals[deg, k_vec]))
poly_integrals.append(k_perm_prod)
return np.array(poly_integrals)
def magnitude_spiral(dim, k_max):
k_perm = permutations(dim*[list(range(-k_max, k_max+1))])
k_vecs = {}
for k_vec in k_perm:
mag = np.linalg.norm(k_vec)
try:
k_vecs[mag].append(k_vec)
except KeyError:
k_vecs[mag] = [k_vec]
k_vec_sort = {}
for mag in list(sorted(k_vecs.keys())):
k_vec_sort[mag] = sorted(k_vecs[mag], key=lambda x: list(np.abs(x)))
return np.concatenate(list(k_vec_sort.values()))
deg = int(math.sqrt(poly.shape[-1], 1/dim)) - 1
# In form [Degree Permutation, K permutation]
poly_integrals = precompute_integrals(int(math.pow(1000, 1/deg)), dim, deg)
###### FINISH LOOP
def main():
C_DIM = 4
video = np.array(read_video(50))
vid_blocks = change_dimension(video, C_DIM)
block_poly = np.round(pure_polynomial_fit(vid_blocks, C_DIM), 5)
print(video_blocks[0])
c_k = []
#for poly in block_polynomials:
#img = Image.fromarray(video[1000])
#img.save('D:/Videos/test.png')
if __name__ == "__main__":
main()