This page was written in 2018 when I was learning digital signal processing basics during my undergrad in electrical engineering. It is not an accurate representation of my current knowledge in the matter, please check newer blog posts to see what I'm up to nowadays.
The first algorithm simply performs a colour inversion in a given area and outputs the result. To understand the procedure it's important to understand how RGB digital images store colour information. When the sub-pixel matrix consists of 3 different independent colour channels (red, blue and green), a combination of intensity of those channels results in the perceived output colour.
Multiple different pixel format standards exist, but if your image is targetting the web it's sane to assume we have 8 bits of precision per colour channel. Which gives us 24 bits per pixel or 16777216 different possible colours.
The colour "white" is an equal combination of all colours, so in our system to output white we need maximum intensity in all three of our colour channels, which means white is (255, 255, 255) in decimal, or #FFFFFF in hexadecimal.
The inverse of a colour is simply the difference between absolute white and the colour itself, and since the pixel data is stored in binary we can perform a bitwise_not in order to invert the value of all the bits (equivalent of subtracting their original value from 255).
Here we can see our original image:
The following program receives the coordinates from the user, and then performs the inversion:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat img = imread(argv[1]);
if (!img.data) {
cout << "imagem nao carregou corretamente" << endl;
return(-1);
}
Mat img_out = Mat::zeros(img.rows, img.cols, CV_8UC3);
int pos_xi, pos_yi, pos_xf, pos_yf;
cout << "digite o pixel x inicial:" << endl;
cin >> pos_xi;
cout << "digite o pixel y inicial:" << endl;
cin >> pos_yi;
cout << "digite o pixel x final:" << endl;
cin >> pos_xf;
cout << "digite o pixel y final:" << endl;
cin >> pos_yf;
namedWindow("janela", WINDOW_AUTOSIZE);
imshow("janela", img);
waitKey();
for (int i = 0; i < img.rows; i++) {
for (int j = 0; j < img.cols; j++) {
if (i > pos_xi && i < pos_xf && j > pos_yi && j < pos_yf) {
bitwise_not(img.at(i, j), img_out.at(i, j));
}
else {
img_out.at(i, j) = img.at(i, j);
}
}
}
imshow("janela", img);
namedWindow("janelaout", WINDOW_AUTOSIZE);
imshow("janelaout", img_out);
waitKey();
return 0;
}
inverse.cpp
We can see the resulting image here:
An image can be divided into 4 quadrants, the following example aims to swap the position of those quadrants maintaining the original pixel values.
Here we can see the original quadrant configuration of any image:
As requested, the inversion of quadrants should be done in the following manner:
And finally, we can see the program that does that for us:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat img = imread(argv[1]);
if (!img.data) {
cout << "imagem nao carregou corretamente" << endl;
return(-1);
}
Mat img_out = Mat::zeros(img.rows, img.cols, CV_8UC3);
namedWindow("janela", WINDOW_AUTOSIZE);
imshow("janela", img);
waitKey();
for (int i = 0; i < img.rows; i++) {
for (int j = 0; j < img.cols; j++) {
if (i < img.rows / 2 && j < img.cols / 2) {
img_out.at(i + img.rows / 2, j + img.cols / 2) = img.at(i, j);
}
else if (i < img.rows / 2 && j > img.cols / 2) {
img_out.at(i + img.rows / 2, j - img.cols / 2) = img.at(i, j);
}
else if (i > img.rows / 2 && j < img.cols / 2) {
img_out.at(i - img.rows / 2, j + img.cols / 2) = img.at(i, j);
}
else if (i > img.rows / 2 && j > img.cols / 2) {
img_out.at(i - img.rows / 2, j - img.cols / 2) = img.at(i, j);
}
}
}
imshow("janela", img);
namedWindow("janelaout", WINDOW_AUTOSIZE);
imshow("janelaout", img_out);
waitKey();
return 0;
}
quadrants.cpp
First let's see how the original image looked like:
And how it looks now after we swapped its quadrants:
Now we'll learn how to count objects, in order to increase the complexity of the algorithm and its accuracy, it's important to differentiate objects with holes and objects without.
The following image will be the input of the program:
The very first thing the program does is delete all the objects that are touching the borders of the image. After that, the background is "painted" in a certain colour of choice in order to differentiate it from the holes. Then, the program will search for white pixels and paint the entire agglomerate of white pixels with a given colour. This colour changes each time the program finds another agglomerate of white pixels, which are the objects.
After all the objects are found and painted with a corresponding colour, the program will search for holes and paint those holes with the same colour it used to paint the object which this hole is inside of, which prevents the program from counting objects with 2 holes wrongly.
The program can be seen here:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat image = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
if (!image.data) {
cout << "imagem nao carregou corretamente" << endl;
return(-1);
}
long int obj_total = 0;
long int obj_with_holes = 0;
long int obj_found_colour = 2;
long int obj_colour_aux = 0;
CvPoint p;
namedWindow("Labelling", CV_WINDOW_KEEPRATIO);
imshow("Labelling", image);
waitKey();
for (int i = 0; i < image.rows; i++) {
for (int j = 0; j < image.cols; j++) {
if (i == 0 || j == 0 || i == image.rows - 1 || j == image.cols - 1) {
image.at(i, j) = 255;
}
}
}
p.x = 0; p.y = 0;
floodFill(image, p, 0); //Border touching holes are removed
imshow("Labelling", image);
waitKey();
floodFill(image, p, 1); //Background colour set
imshow("Labelling", image);
waitKey();
for (int i = 0; i < image.rows; i++) {
for (int j = 0; j < image.cols; j++) {
if (image.at(i, j) == 255) {
obj_total++; //Find the objects
p.x = j;
p.y = i;
floodFill(image, p, obj_found_colour);
obj_found_colour++;
}
}
}
imshow("Labelling", image);
waitKey();
for (int x = 0; x < image.rows; x++) {
for (int y = 0; y < image.cols; y++) {
if (image.at(x, y) == 0 && image.at(x, y - 1) != obj_colour_aux) {
obj_colour_aux = image.at(x, y - 1);
p.x = y;
p.y = x;
floodFill(image, p, obj_colour_aux);
obj_with_holes++; //Objects with holes are found
}
else if (image.at(x, y) == 0 && image.at(x, y - 1) == obj_colour_aux) {
p.x = y;
p.y = x;
floodFill(image, p, obj_colour_aux);
}
}
}
imshow("Labelling", image);
waitKey();
imwrite("labeling.png", image);
cout << "Number of elements without holes: " << obj_total - obj_with_holes << endl;
cout << "Number of elements with holes: " << obj_with_holes << endl;
waitKey();
return 0;
}
counting.cpp
Following the program, first we delete all the objects that touch the border:
Then, we count the total number of objects while painting them:
And then, we count the holes taking into consideration to colour of the object:
Finally, we can see the end result in our terminal:
To make sure the program wouldn't count objects with multiple holes wrongly, a modification was done in the input image:
And then we can go through the same sequence of images following the code again:
We can check how many objects and objects with holes were counted in our terminal output, it's important to notice the numbers didn't change:
Our aim is to equalise the histogram of a greyscale image, maximising its dynamic range to the highest possible value using the existing greyscale values.
This will be our input image:
The first thing we need to do is to load the image as a greyscale image, using a property of OpenCV itself to do it:
The program that calculates and plots the histograms of both the input image and its equalised output is shown below:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
Mat img, img_eql, hist, hist_eql;
img = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
equalizeHist(img, img_eql);
bool uniform = true;
bool acummulate = false;
int nbins = 128;
float range[] = { 0, 256 };
const float *histrange = { range };
int width = img.cols;
int height = img.rows;
int histw = nbins, histh = nbins / 4;
Mat hist_img(histh, histw, CV_8UC1, Scalar(0, 0, 0));
Mat hist_img_eql(histh, histw, CV_8UC1, Scalar(0, 0, 0));
calcHist(&img, 1, 0, Mat(), hist, 1, &nbins, &histrange, uniform, acummulate);
calcHist(&img_eql, 1, 0, Mat(), hist_eql, 1, &nbins, &histrange, uniform, acummulate);
normalize(hist, hist, 0, hist_img.rows, NORM_MINMAX, -1, Mat());
normalize(hist_eql, hist_eql, 0, hist_img.rows, NORM_MINMAX, -1, Mat());
hist_img.setTo(Scalar(0));
hist_img_eql.setTo(Scalar(0));
for (int i = 0; i<nbins; i++) {
line(hist_img, Point(i, histh), Point(i, cvRound(hist.at<float>(i))), Scalar(255, 255, 255), 1, 8, 0);
}
for (int i = 0; i<nbins; i++) {
line(hist_img_eql, Point(i, histh), Point(i, cvRound(hist_eql.at<float>(i))), Scalar(255, 255, 255), 1, 8, 0);
}
hist_img.copyTo(img(Rect(10, 10, nbins, histh)));
hist_img_eql.copyTo(img_eql(Rect(10, 10, nbins, histh)));
namedWindow("Original", WINDOW_AUTOSIZE);
namedWindow("Equalised", WINDOW_AUTOSIZE);
imshow("Original", img);
imshow("Equalised", img_eql);
waitKey();
return 0;
}
equalise.cpp
The first plot is simply the original image in greyscale plus its histogram:
And now we can see the same plot, but for the equalised image:
It's important to notice that the histogram plotted has its origin in the top left corner, the intensity axis goes from there to bottom and the axis that represents different values goes from left to right. The background of the histogram is white, and the actual histogram is what can be seen as black on top of this white background.
The code was originally designed to be used with still images, however with some modifications it can be used for videos (or camera inputs, which are also video streams).
In the following example, a video was used intead of a camera input because of technical limitations (lack of a camera). It can be used with a camera swapping "argv[1]" in "VideoCapture" to "0". "0" opens the camera instead.
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
Mat img, img_eql, hist, hist_eql;
VideoCapture cap(argv[1]);
while (true) {
cap >> img;
cvtColor(img, img, CV_BGR2GRAY);
equalizeHist(img, img_eql);
bool uniform = true;
bool acummulate = false;
int nbins = 128;
float range[] = { 0, 256 };
const float *histrange = { range };
int width = img.cols;
int height = img.rows;
int histw = nbins, histh = nbins / 4;
Mat hist_img(histh, histw, CV_8UC1, Scalar(0, 0, 0));
Mat hist_img_eql(histh, histw, CV_8UC1, Scalar(0, 0, 0));
calcHist(&img, 1, 0, Mat(), hist, 1, &nbins, &histrange, uniform, acummulate);
calcHist(&img_eql, 1, 0, Mat(), hist_eql, 1, &nbins, &histrange, uniform, acummulate);
normalize(hist, hist, 0, hist_img.rows, NORM_MINMAX, -1, Mat());
normalize(hist_eql, hist_eql, 0, hist_img.rows, NORM_MINMAX, -1, Mat());
hist_img.setTo(Scalar(0));
hist_img_eql.setTo(Scalar(0));
for (int i = 0; i<nbins; i++) {
line(hist_img, Point(i, histh), Point(i, cvRound(hist.at<float>(i))), Scalar(255, 255, 255), 1, 8, 0);
}
for (int i = 0; i<nbins; i++) {
line(hist_img_eql, Point(i, histh), Point(i, cvRound(hist_eql.at<float>(i))), Scalar(255, 255, 255), 1, 8, 0);
}
hist_img.copyTo(img(Rect(10, 10, nbins, histh)));
hist_img_eql.copyTo(img_eql(Rect(10, 10, nbins, histh)));
namedWindow("Original", WINDOW_AUTOSIZE);
namedWindow("Equalised", WINDOW_AUTOSIZE);
imshow("Original", img);
imshow("Equalised", img_eql);
if (waitKey(30) >= 0) break;
}
return 0;
}
equalise_video.cpp
A video can better demonstrate how the program works when it has a video stream as an input:
As we know, an image is a matrix of pixels, which are arrays of subpixels containing information. A greyscale image only has a single subpixel per pixel, and can only assume values between black and white. A RGB or a YUV image however, has 3 channels per pixel (3 sub-pixels) and can assume different colours. In either case, an image is a matrix.
A video, then, is simply a sequence of matrices. Each matrix in the sequence represents a frame, and the temporal change in values is perceived as motion.
Our objective is to be able to detect movements in a video stream, and we'll use our knowledge of what a video is in order to achieve it. Since a video is a series of matrices, in order to detect movement we can compare those matrices and see if their values differ in a significant way. If they do, it's evident that there was movement. If they don't, it's obvious that the image stayed still.
The program simply finds the difference between the pixels of the current frame and the previous frame, creating a summation of all those values and deciding whether there was movement or not depending on how high this value is.
The program also displays the difference between frames in the screen, and it's very easy to interpret the image since black values mean that the values of the frames didn't change and white values mean that those values indeed changed significantly (remember the difference between frame N and frame N - 1 is what's being displayed).
Without further ado, here is the program that detects movement:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
long frame_counter = 0, difference_total_value = 0;
Mat frame, frame_past, difference;
VideoCapture cap(argv[1]);
int width = cap.get(CV_CAP_PROP_FRAME_WIDTH);
int height = cap.get(CV_CAP_PROP_FRAME_HEIGHT);
difference = Mat::zeros(height, width, CV_8UC1);
while (true) {
cap >> frame;
cvtColor(frame, frame, CV_BGR2GRAY);
difference_total_value = 0;
for (int i = 0; i < frame.rows; i++) {
for (int j = 0; j < frame.cols; j++) {
if (frame_counter == 0) {
difference.at<uchar>(i, j) = 0;
} else {
difference.at<uchar>(i, j) = abs(frame.at<uchar>(i, j) - frame_past.at<uchar>(i, j));
}
difference_total_value += difference.at<uchar>(i, j);
}
}
frame_past = frame;
frame_counter++;
cout << "frame: " << frame_counter << " difference = " << difference_total_value << endl;
if (difference_total_value > 150000) {
cout << "THERE'S MOVEMENT!" << endl;
}
namedWindow("Difference", WINDOW_AUTOSIZE);
imshow("Difference", difference);
if (waitKey(30) >= 0) break;
}
return 0;
}
movement.cpp
For the reader, the most important part to understand here is how the difference summation is calculated.
Inside a loop, the program simply makes the difference between the current frame and the previous frame, if the frame_counter is still 0 the difference is also 0 since we don't have any previous frame yet. When frame_counter is higher than 0 though, the program will calculate the difference between each pixel in the current frame and its counterpart in the previous frame, and then it'll add this difference into a difference summation.
Here we can see what was just explained above:
difference_total_value = 0;
for (int i = 0; i < frame.rows; i++) {
for (int j = 0; j < frame.cols; j++) {
if (frame_counter == 0) {
difference.at(i, j) = 0;
} else {
difference.at(i, j) = abs(frame.at(i, j) - frame_past.at(i, j));
}
difference_total_value += difference.at(i, j);
}
}
And now an example of the program running, and the terminal shouting that there was movement:
We can see a grey parachutist in the image, while its background remains almost completely black. This happens because while the parachutist is moving in the original video, the sky (which is our background) remains still.
In this example the threshold between "no movement" and "movement" was a total difference summation of 150000. The video below shows how the program works when running:
The number of the current frame is printed at the terminal, and the total difference summation value between it and the previous frame is as well. If this value is higher than our threshold, the program also prints that there was movement. As already explained, what we see closer to black means that there wasn't movement in that area. And what we see closer to white, means that it's moving.
To filter in the spatial domain, we appropriate ourselves from a convolution property. The property tells us that a product in the frequency domain is the equivalent of a convolution in the time domain. In our case "time" is simply the value variation across an axis, and we have 2 axis creating a plane.
The program, as requested in the assignment should alternate between certain masks depending on the key pressed during execution. Starting from our original program filtroespatial.cpp, the objective is to add the option of doing a gaussian filtering before applying the laplacian one.
The modified program is shown below:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void printmask(Mat &m) {
for (int i = 0; i<m.rows; i++) {
for (int j = 0; j<m.cols; j++) {
cout << m.at<float>(i, j) << ",";
}
cout << endl;
}
}
void menu() {
cout << "\npressione a tecla para ativar o filtro: \n"
"a - calcular modulo\n"
"m - media\n"
"g - gauss\n"
"v - vertical\n"
"h - horizontal\n"
"l - laplaciano\n"
"esc - sair\n";
}
int main(int argvc, char** argv) {
VideoCapture video(argv[1]); //use this for videos
//cap = imread(argv[1]); //use this for images
float media[] = { 1,1,1,1,1,1,1,1,1 };
float gauss[] = { 1,2,1,2,4,2,1,2,1 };
float horizontal[] = { -1,0,1,-2,0,2,-1,0,1 };
float vertical[] = { -1,-2,-1,0,0,0,1,2,1 };
float laplacian[] = { 0,-1,0,-1,4,-1,0,-1,0 };
Mat cap, frame, frame32f, frameFiltered, frameFiltered2;
Mat mask(3, 3, CV_32F), mask_gauss(3, 3, CV_32F), mask_lap(3, 3, CV_32F);
Mat result, result1, result_lapgauss;
double width, height, min, max;
bool lap_gauss_aux = false, absolut = true;
char key;
width = video.get(CV_CAP_PROP_FRAME_WIDTH); //use this for videos
height = video.get(CV_CAP_PROP_FRAME_HEIGHT); //use this for videos
//width = cap.cols; //use this for images
//height = cap.rows; //use this for images
std::cout << "width = " << width << "\n";;
std::cout << "height = " << height << "\n";;
namedWindow("spatialfilter", 1);
mask_gauss = Mat(3, 3, CV_32F, gauss);
scaleAdd(mask_gauss, 1 / 16.0, Mat::zeros(3, 3, CV_32F), mask_gauss);
mask_lap = Mat(3, 3, CV_32F, laplacian);
mask = Mat(3, 3, CV_32F, media);
scaleAdd(mask, 1 / 9.0, Mat::zeros(3, 3, CV_32F), mask);
swap(mask, mask);
menu();
while (true) {
video >> cap; //comment this when using images as input
cvtColor(cap, frame, CV_BGR2GRAY);
imshow("original", frame);
frame.convertTo(frame32f, CV_32F);
key = (char)waitKey(10);
if (lap_gauss_aux) {
filter2D(frame32f, frameFiltered, frame32f.depth(), mask_gauss, Point(1, 1), 0);
filter2D(frameFiltered, frameFiltered2, frameFiltered.depth(), mask_lap, Point(1, 1), 0);
frameFiltered2 = abs(frameFiltered2);
frameFiltered2.convertTo(result_lapgauss, CV_8U);
imshow("spatialfilter", result_lapgauss);
} else {
filter2D(frame32f, frameFiltered, frame32f.depth(), mask, Point(1, 1), 0);
if (absolut) {
frameFiltered = abs(frameFiltered);
}
frameFiltered.convertTo(result, CV_8U);
imshow("spatialfilter", result);
}
if (key == 27) break; // esc pressed!
switch (key) {
case 'a':
menu();
absolut = !absolut;
lap_gauss_aux = false;
break;
case 'm':
menu();
mask = Mat(3, 3, CV_32F, media);
scaleAdd(mask, 1 / 9.0, Mat::zeros(3, 3, CV_32F), mask);
printmask(mask);
lap_gauss_aux = false;
break;
case 'g':
menu();
mask = mask_gauss;
printmask(mask_gauss);
lap_gauss_aux = false;
break;
case 'h':
menu();
mask = Mat(3, 3, CV_32F, horizontal);
printmask(mask);
lap_gauss_aux = false;
break;
case 'v':
menu();
mask = Mat(3, 3, CV_32F, vertical);
printmask(mask);
lap_gauss_aux = false;
break;
case 'l':
menu();
mask = Mat(3, 3, CV_32F, laplacian);
printmask(mask);
lap_gauss_aux = false;
break;
case 'q':
lap_gauss_aux = true;
break;
default:
break;
}
}
return 0;
}
spatialfilters.cpp
The program can be used with images or videos, simply alternating between a few set of lines already commented in the code itself. The image used as input is shown below:
The first thing done is the convertion of the original image to greyscale:
The user can alternate between different masks using the keyboard, the following list of options are available:
And we can see the result of all the filters:
Module:
Median:
Gauss:
Vertical:
Horizontal:
Laplacian:
Gauss + laplacian:
It's easy to see that applying a gaussian blur before applying the laplacian filter makes the output cleaner and smoother.
In this section we'll do a selective focus algorithm, as if we were filming with special lenses.
Let me first show the code, and then we'll learn what it does:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <math.h>
using namespace cv;
using namespace std;
int main(int argvc, char** argv) {
Mat img_in = imread(argv[1]);
Mat img_blurred = Mat::zeros(img_in.rows, img_in.cols, CV_32FC3);
Mat img_out = Mat::zeros(img_in.rows, img_in.cols, CV_32FC3);
Mat img_focused = Mat::zeros(img_in.rows, img_in.cols, CV_32FC3);
Mat img_unfocused = Mat::zeros(img_in.rows, img_in.cols, CV_32FC3);
Mat mask_focus = Mat(img_in.rows, img_in.cols, CV_32FC3, Scalar(0, 0, 0));
Mat mask_unfocus = Mat(img_in.rows, img_in.cols, CV_32FC3, Scalar(1, 1, 1));
imshow("input", img_in);
waitKey();
float unfocus_aux = 0.35; //Edit this to change how much of the original image will stay focused
int pos_i = unfocus_aux*img_in.rows;
int pos_f = img_in.rows - unfocus_aux*img_in.rows;
int decay = 60; //Edit this to change how fast it'll decay
Vec3f mask_function_value;
for (int i = 0; i < img_in.rows; i++) {
for (int j = 0; j < img_in.cols; j++) {
mask_function_value[0] = (tanh((float(i - pos_i) / decay)) - tanh((float(i - pos_f) / decay))) / 2;
mask_function_value[1] = (tanh((float(i - pos_i) / decay)) - tanh((float(i - pos_f) / decay))) / 2;
mask_function_value[2] = (tanh((float(i - pos_i) / decay)) - tanh((float(i - pos_f) / decay))) / 2;
mask_focus.at<Vec3f>(i, j) = mask_function_value;
}
}
mask_unfocus = mask_unfocus - mask_focus;
//This section shows the masks
imshow("focus mask", mask_focus);
imshow("unfocus mask", mask_unfocus);
waitKey();
img_in.convertTo(img_in, CV_32FC3);
GaussianBlur(img_in, img_blurred, Size(7, 7), 0, 0);
img_focused = img_in.mul(mask_focus);
img_unfocused = img_blurred.mul(mask_unfocus);
//This section only shows the blurred image
img_blurred.convertTo(img_blurred, CV_8UC3);
imshow("img_blurred", img_blurred);
waitKey();
img_out = img_focused + img_unfocused;
img_out.convertTo(img_out, CV_8UC3);
//This section only shows the components of the final image
img_focused.convertTo(img_focused, CV_8UC3);
img_unfocused.convertTo(img_unfocused, CV_8UC3);
imshow("img_focused", img_focused);
imshow("img_unfocused", img_unfocused);
waitKey();
imshow("output", img_out);
waitKey();
return 0;
}
tiltshift.cpp
Now let's understand what the program does. First it simply reads its input and creates matrices that will be used later.
The input image used is shown below:
The program then, generates an auxiliar image with hyperbolic sines with the same size as the input image, we'll call this the focus mask:
Using the image previously generated, we generate its inverse (the "unfocus mask") with a simple subtraction from a white matrix:
We then create a blurred version of the original image through a gaussian blur filter, this image will be used as our unfocused version of the original image:
The focus mask then multiplies the original image, and we get the focused portion of our final image:
The same is done with the blurred version and the unfocus mask:
Finally, to arrive at our intended result, we simply sum the focused image and the unfocused one:
We can see that, when compared to the original image, the output image's vertical extremities seem to be out of focus. However, the centre of it stayed sharp and clear.
We can utilise the same effect to create a video that looks like it was filmed from a miniature, and make it more convincing including a frame skip effect which is common on those videos to fake motion.
In order to be used with videos the code needs to be modified, and we can see the modified version below:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <math.h>
using namespace cv;
using namespace std;
int main(int argvc, char** argv) {
VideoCapture video(argv[1]);
Mat img_in, img_discard;
int cols = video.get(CV_CAP_PROP_FRAME_WIDTH);
int rows = video.get(CV_CAP_PROP_FRAME_HEIGHT);
Mat img_blurred = Mat::zeros(rows, cols, CV_32FC3);
Mat img_out = Mat::zeros(rows, cols, CV_32FC3);
Mat img_focused = Mat::zeros(rows, cols, CV_32FC3);
Mat img_unfocused = Mat::zeros(rows, cols, CV_32FC3);
Mat mask_focus = Mat(rows, cols, CV_32FC3, Scalar(0, 0, 0));
Mat mask_unfocus = Mat(rows, cols, CV_32FC3, Scalar(1, 1, 1));
int frame_skip = 2; //Sets how many frames to skip between frames
int pass_frame = 0;
float unfocus_aux = 0.35; //Edit this to change how much of the original image will stay focused
int pos_i = unfocus_aux*rows;
int pos_f = rows - unfocus_aux*rows;
int decay = 60; //Edit this to change how fast it'll decay
Vec3f mask_function_value;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
mask_function_value[0] = (tanh((float(i - pos_i) / decay)) - tanh((float(i - pos_f) / decay))) / 2;
mask_function_value[1] = (tanh((float(i - pos_i) / decay)) - tanh((float(i - pos_f) / decay))) / 2;
mask_function_value[2] = (tanh((float(i - pos_i) / decay)) - tanh((float(i - pos_f) / decay))) / 2;
mask_focus.at<Vec3f>(i, j) = mask_function_value;
}
}
mask_unfocus = mask_unfocus - mask_focus;
//This section shows the masks
imshow("focus mask", mask_focus);
imshow("unfocus mask", mask_unfocus);
while (true) {
if (pass_frame == 0) {
video >> img_in;
} else {
video >> img_discard;
}
img_in.convertTo(img_in, CV_32FC3);
GaussianBlur(img_in, img_blurred, Size(7, 7), 0, 0);
img_focused = img_in.mul(mask_focus);
img_unfocused = img_blurred.mul(mask_unfocus);
//This section only shows the blurred image
img_blurred.convertTo(img_blurred, CV_8UC3);
imshow("img_blurred", img_blurred);
//waitKey();
img_out = img_focused + img_unfocused;
img_out.convertTo(img_out, CV_8UC3);
//This section only shows the components of the final image
img_focused.convertTo(img_focused, CV_8UC3);
img_unfocused.convertTo(img_unfocused, CV_8UC3);
imshow("img_focused", img_focused);
imshow("img_unfocused", img_unfocused);
//waitKey();
if (pass_frame == 0) {
pass_frame = frame_skip;
}
else {
pass_frame -= 1;
}
imshow("output", img_out);
if (waitKey(30) >= 0) break;
}
return 0;
}
tiltshift_video.cpp
As we can see the skeleton of the code remains the same, and the divergence to the original code is related to acquiring frames and passing those frames to the matrices that will process the information and generate our intended result.
Since we'll now run the program with a video as its input, it's important to know how the original video looks like:
In this example, since the video's resolution is the same as the image's resolution in the previous example, our masks will look identical to those already shown before. The frame skipping logic was set to skip 2 frames every frame that's passed to the algorithm. This number needs to be tweaked in order to create a better miniature effect.
We can see the output video below. Due to the fact that we're throwing 2/3 of the frames away, but holdind the already passed frames until a new frame can be passed, it looks slower than the original:
We can however, "fix" the motion speed problem by encoding the video with a lower frame persistance time:
The homomorphic filtering simultaneously normalises brightness across an image and increases contrast, since illumination and reflectance combine multiplicatively, we can make those components additive by taking the logarithm of the image and then we can separate those in the frequency domain. Illumination variations can be seen as as multiplicative noise, which we can reduce through filtering.
In order to improve the image by making its illumination more even, the high-frequency components are increased and low-frequency components are decreased, because the high-frequency components are assumed to represent mostly the reflectance in the scene (the amount of light reflected off the object in the scene), whereas the low-frequency components are assumed to represent mostly the illumination in the scene. That is, high-pass filtering is used to suppress low frequencies and amplify high frequencies, in the log-intensity domain.
#include <iostream>
#include <opencv2/opencv.hpp>
#define RADIUS 20
using namespace cv;
using namespace std;
void deslocaDFT(Mat &image){
Mat tmp, A, B, C, D;
image = image(Rect(0, 0, image.cols & -2, image.rows & -2));
int cx = image.cols / 2;
int cy = image.rows / 2;
A = image(Rect(0, 0, cx, cy));
B = image(Rect(cx, 0, cx, cy));
C = image(Rect(0, cy, cx, cy));
D = image(Rect(cx, cy, cx, cy));
A.copyTo(tmp);
D.copyTo(A);
tmp.copyTo(D);
C.copyTo(tmp);
B.copyTo(C);
tmp.copyTo(B);
}
int main(int argc, char **argv){
Mat imaginaryInput, complexImage, multsp;
Mat padded, filter, mag;
Mat image, imagegray, tmp;
Mat_<float> realInput, zeros;
vector<Mat> planos;
image = imread(argv[1]);
float mean;
int dft_M, dft_N;
dft_M = getOptimalDFTSize(image.rows);
dft_N = getOptimalDFTSize(image.cols);
copyMakeBorder(image, padded, 0,
dft_M - image.rows, 0,
dft_N - image.cols,
BORDER_CONSTANT, Scalar::all(0));
zeros = Mat_<float>::zeros(padded.size());
complexImage = Mat(padded.size(), CV_32FC2, Scalar(0));
filter = complexImage.clone();
tmp = Mat(dft_M, dft_N, CV_32F);
float gama_h = 100;
float gama_l = 510;
float c = 10;
float D_0 = 10;
for (int i = 0; i < dft_M; i++){
for (int j = 0; j < dft_N; j++){
tmp.at<float>(i, j) = (gama_h - gama_l) *
(1.0 - exp(-1.0 * (float)c * ((((float)i - dft_M / 2.0) *
((float)i - dft_M / 2.0) + ((float)j - dft_N / 2.0) *
((float)j - dft_N / 2.0)) / (D_0 * D_0)))) + gama_l;
}
}
Mat comps[] = {tmp, tmp};
merge(comps, 2, filter);
cvtColor(image, imagegray, CV_BGR2GRAY);
imshow("original", imagegray);
copyMakeBorder(imagegray, padded, 0,
dft_M - image.rows, 0,
dft_N - image.cols,
BORDER_CONSTANT, Scalar::all(0));
planos.clear();
realInput = Mat_<float>(padded);
planos.push_back(realInput);
planos.push_back(zeros);
merge(planos, complexImage);
dft(complexImage, complexImage);
deslocaDFT(complexImage);
mulSpectrums(complexImage, filter, complexImage, 0);
planos.clear();
split(complexImage, planos);
mean = abs(planos[0].at<float>(dft_M / 2, dft_N / 2));
merge(planos, complexImage);
deslocaDFT(complexImage);
cout << complexImage.size().height << endl;
idft(complexImage, complexImage);
planos.clear();
split(complexImage, planos);
normalize(planos[0], planos[0], 0, 1, CV_MINMAX);
imshow("filtrada", planos[0]);
waitKey();
return 0;
}
homomorphic.cpp
The input image is shown below:
And its greyscale version:
We can see how the output image differs from the input below:
If we set our result as the original image's luma plane in a YUV colour-system, then equalise it, we arrive at this result:
The result of the filtering can be obvious when compared to the equalised version of the original image:
We can clearly see that in the last image, the equalised version of the original image, the details and luminance deviations aren't as clearly visible as in the previous image, which was filtered, then used as luma plane, then equalised.
The input image can be seen below:
You can use pontilhismo.cpp
to create pointillism art.
The generated art can be seen below:
We apply the canny edge detector in order to find edges in the original image, which then allows us to use the extra information to better reconstruct its characteristics in our pointillism art.
You can see the code below:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <fstream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <numeric>
#include <ctime>
#include <cstdlib>
using namespace std;
using namespace cv;
#define STEP 5
#define JITTER 3
#define RAIO 3
int top_slider = 10;
int top_slider_max = 200;
char TrackbarName[50];
Mat image, border, frame, points, tmp, result;
void on_trackbar_canny(int, void *){
Canny(image, border, top_slider, 3 * top_slider);
imshow("canny", border);
}
int main(int argc, char **argv){
vector<int> yrange;
vector<int> xrange;
int width, height;
int x, y;
int r, g, b;
image = imread(argv[1]);
srand(time(0));
if (!image.data){
cout << "nao abriu" << argv[1] << endl;
cout << argv[0] << " imagem.jpg";
exit(0);
}
width = image.size().width;
height = image.size().height;
xrange.resize(height / STEP);
yrange.resize(width / STEP);
iota(xrange.begin(), xrange.end(), 0);
iota(yrange.begin(), yrange.end(), 0);
for (uint i = 0; i < xrange.size(); i++){
xrange[i] = xrange[i] * STEP + STEP / 2;
}
for (uint i = 0; i < yrange.size(); i++){
yrange[i] = yrange[i] * STEP + STEP / 2;
}
random_shuffle(xrange.begin(), xrange.end());
points = Mat(height, width, CV_8UC3, Scalar(CV_RGB(255, 255, 255)));
for (auto i : xrange){
random_shuffle(yrange.begin(), yrange.end());
for (auto j : yrange){
x = i + rand() % (2 * JITTER) - JITTER + 1;
y = j + rand() % (2 * JITTER) - JITTER + 1;
r = image.at<Vec3b>(x, y)[2];
g = image.at<Vec3b>(x, y)[1];
b = image.at<Vec3b>(x, y)[0];
circle(points, cv::Point(y, x), RAIO, CV_RGB(r, g, b), -1, CV_AA);
}
}
namedWindow("pontos");
imshow("pontos", points);
for (int round = 0; round < 2; round++){
sprintf(TrackbarName, "Threshold inferior", top_slider_max);
namedWindow("canny");
createTrackbar(TrackbarName, "canny",
&top_slider,
top_slider_max,
on_trackbar_canny);
on_trackbar_canny(top_slider, 0);
waitKey();
Mat aux = Mat(height, width, CV_8U, Scalar(0));
for (int i = 0; i < height; i++){
for (int j = 0; j < width; j++){
if (border.at<uchar>(i, j) > 0 && aux.at<uchar>(i, j) == 0){
r = image.at<cv::Vec3b>(i, j)[2];
g = image.at<cv::Vec3b>(i, j)[1];
b = image.at<cv::Vec3b>(i, j)[0];
circle(points, cv::Point(j, i), RAIO - round, CV_RGB(r, g, b), -1, CV_AA);
circle(aux, cv::Point(j, i), 2 * (RAIO - round), CV_RGB(255, 255, 255), -1, CV_AA);
}
}
}
namedWindow("pontos");
imshow("pontos", points);
waitKey();
}
return 0;
}
canny_points.cpp
The algorithm follows a very simple logic, starting from the already shown previous pointillism art, it'll paint new circles 2 additional times as the user chooses 2 canny thresholds. The original circle radius is decreased by 1 each time, and the idea is that we choose a higher threshold in the second run so we can paint smaller circles for more important lines, which will make those lines more visible.
To prevent the algorithm from paiting circles on top of one another inside an iteration (note that it can, and should, paint new circles on top of circles painted on previous iterations), we also paint doubled radius circles on an auxiliary matrix and use it as a rule that prevents new circles from being painted if the equivalent position in it has been painted before. A doubled radius is used to prevent overlapping.
You can see the first threshold used, and the art after the new circles were painted:
And you can also see the second threshold used, and the even smaller circles:
The end result is a pointillism art that better shows transitions and more accurately draw the original lines. Note that the result can be controlled with the thresholds chosen. Since the first threshold was fairly generous, the end result looks incredibly filled with almost no blank white-space.
You can see all the steps done to the original image in order in the following GIF:
K-means clustering is a unsurpervised data-mining machine learning algorithm used to classify elements. The algorithm has several characteristics, the first of them is that it'll always find the specified number of classes regardless of the topography of the data-set (the optimal number of classes can be discovered through the elbow method). The second prominent characteristic, and the most important one for us in this demonstration, is that since our centroids (which classify elements) have their starting locations randomised, each run will tend to result in slightly different final centroid locations and therefore slightly different element classifications.
With this brief introduction, now we can start learning how the algorithm actually works. The first thing done is the creation of the initial centroids,
the most common starting method is to simply randomly throw them anywhere (we'll understand why later) and simply let the algorithm itself converge to a
sane state in the end. Usually, you'll want to run the program multiple times and pick your best run though.
Since the initialisation is important, there are multiple different initialisation methods that aim to be more accurate than simply throwing the centroids anywhere
randomly. In this demonstration our aim is to show how different our results can be, so we'll randomly initialise our centroids.
With the creation of the centroids done, now we need to understand what centroids even are and what they do. This is very simple and straightforward, a centroid is simply a point in our data-set which all the elements will get their distances measured against and classified with a label that identifies which of the centroids is closer to it. So, in other words, we classify each element in the class represented by the closest centroid.
Once we finish classifying all the elements, for each centroid we calculate the summation of all the distances between this given centroid and all the elements
that were classified in its class. This median will result in a vector that points exactly to the middle of all the elements classified by a given centroid.
Then, we move each centroid to the new calculated position (in the middle of the elements that were classified by the centroid), and redo all the classification
process all over again. We repeat this step until it reaches the iteration count limit, or when there aren't any classification differences between consecutive
iterations anymore.
All those steps can be easily seen in the following GIF:
The code can be seen below:
#include <opencv2/opencv.hpp>
#include <cstdlib>
using namespace cv;
int main(int argc, char **argv) {
int nClusters = 12;
Mat rotulos;
int nRodadas = 1;
Mat centros;
Mat img = imread(argv[1]);
Mat samples(img.rows * img.cols, 3, CV_32F);
for (int i = 0; i < 10; i++) {
for (int y = 0; y < img.rows; y++) {
for (int x = 0; x < img.cols; x++) {
for (int z = 0; z < 3; z++) {
samples.at<float>(y + x * img.rows, z) = img.at<Vec3b>(y, x)[z];
}
}
}
kmeans(samples,
nClusters,
rotulos,
TermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 10000, 0.0001),
nRodadas,
KMEANS_RANDOM_CENTERS,
centros);
Mat rotulada(img.size(), img.type());
for (int y = 0; y < img.rows; y++) {
for (int x = 0; x < img.cols; x++) {
int indice = rotulos.at<int>(y + x * img.rows, 0);
rotulada.at<Vec3b>(y, x)[0] = (uchar)centros.at<float>(indice, 0);
rotulada.at<Vec3b>(y, x)[1] = (uchar)centros.at<float>(indice, 1);
rotulada.at<Vec3b>(y, x)[2] = (uchar)centros.at<float>(indice, 2);
}
}
imshow("clustered image", rotulada);
waitKey();
}
return 0;
}
kmeans.cpp
Without further ado, we'll run the k-means algorithm in an image and switch all the colour values to their final classifications. You can see the input image used below:
Since the k-means machine learning algorithm has a random centroid starting location, each run will reach a relatively different result. We can see 10 runs of the program below:


Reliable Veterinary Appointment Scheduling Software Online
Grow your business with more bookings today
Best Veterinary Appointment Scheduling Software Online
Starting to grow your small business of veterinary service and running mad scheduling appointments? AppointMantic is here to help solve your problem. That’s right. With our free veterinary appointment scheduling software, you can now automate all your appointments. While you’re away on appointments with customers, AppointMantic will do all the scheduling of new clients, rescheduling and cancellation for you. And the best part is you’ll be able to track all of these activities as they happen, online.
More Reasons Why AppointMantic is the Best Veterinary Software
Time Management
Our software automatically reschedules to complete all your time slots so you never have a no-show or empty time slot again.
Convenient
You can access your appointment calendar from anywhere with any device and view your schedule at a single glance.
User Friendly
All your appointment scheduling information are on the dashboard where you can navigate with just a few clicks of a mouse button.
Reporting
You can get reporting of worked hours, earnings, and staff schedules in real time so you’re always on top of your veterinary business.
Organization
Organize yourself or your staff by assigning tasks and appointments and never let your staff sit idle again.
Track Income
Keep a track of your earnings as you complete appointments, right there on your dashboard.


--- comments: true layout: page title: Convex Optimization Based State Estimation against Sparse Integrity Attacks ---
Duo Han, Yilin Mo and Lihua Xie
Automatica, In Review
Link to the paper
If you want to leave any comments, you can annotate the pdf. I will try to be responsive. You can also annotate this page or leave comments below.
We consider the problem of resilient state estimation in the presence of integrity attacks. There are \(m\) sensors monitoring the state and \(p\) of them are under attack. The sensory data collected by the compromised sensors can be manipulated arbitrarily by the attacker. The classical estimators such as the least squares estimator may not provide a reliable estimate under the so-called $(p,m)$-sparse attack. In this work, we are not restricting our efforts in studying whether any specific estimator is resilient to the attack or not, but instead we aim to present some generic sufficient and necessary conditions for resilience by considering a general class of convex optimization based estimators. The sufficient and necessary conditions are shown to be tight, with a trivial gap. We further specialize our result to scalar sensor measurements case. Experimental results tested on the IEEE 14-bus test system validate the theoretical analysis.


- id - SOAP::SOAPLong
- selector - AdWords::V201008::ReportDefinitionService::Selector
- reportName - SOAP::SOAPString
- reportType - AdWords::V201008::ReportDefinitionService::ReportDefinitionReportType
- hasAttachment - SOAP::SOAPBoolean
- dateRangeType - AdWords::V201008::ReportDefinitionService::ReportDefinitionDateRangeType
- downloadFormat - AdWords::V201008::ReportDefinitionService::DownloadFormat
- creationTime - SOAP::SOAPString
Methods
- N
Attributes
| [RW] | id | |
| [RW] | selector | |
| [RW] | reportName | |
| [RW] | reportType | |
| [RW] | hasAttachment | |
| [RW] | dateRangeType | |
| [RW] | downloadFormat | |
| [RW] | creationTime |
Class Public methods
new(id = nil, selector = nil, reportName = nil, reportType = nil, hasAttachment = nil, dateRangeType = nil, downloadFormat = nil, creationTime = nil)
Source: show
# File lib/adwords4r/v201008/ReportDefinitionService.rb, line 644 def initialize(id = nil, selector = nil, reportName = nil, reportType = nil, hasAttachment = nil, dateRangeType = nil, downloadFormat = nil, creationTime = nil) @id = id @selector = selector @reportName = reportName @reportType = reportType @hasAttachment = hasAttachment @dateRangeType = dateRangeType @downloadFormat = downloadFormat @creationTime = creationTime end


This is my thoughts about the new iPhone that Apple just introduced today. I’m writing this as I watch the keynote. (it was written yesterday at night so I decided to publish it today. I haven’t reviewed or modified it.)
iPhone 5
The name
Seriously Apple? I really loved that you called the iPad 3 the new iPad, which is a lot better for you and for the people since now most of what you’re going to do is just do some iterations like you did from the iPhone 4 to the iPhone 4S. Calling it the iPhone 5 makes no sense.
The bigger screen
That looked like a nice addition in the beginning, but it will become a hell. A lot of apps are going to have those crappy bars, could you guys do what Android does and just re-align the pixels? Most of the apps really have no reason to “take advantage” of that bigger screen. Also that aspect ratio is just the worst one ever. It will also start a little more fragmentation to the platform.
 It is just so ugly. I would prefer if it was made all of the same color/material which would make a lot more sense.
Stock Apps
Shared Photo Stream
Haha, I had to laugh when I saw this. That’s really the worst thing ever. They forgot that today our friends have different devices, usually they are half iOS users and half Android users, also Instagram does this a lot better and in a lot better and more social way.
Siri
Nice new additions to Siri (I’m talking about the sports stuff).  The new Mac app UI is really great. I loved the clean interface.
iPod
The nano
The new iPod nano is really awesome and a lot better than the current generation. If I didn’t used my phone as my music player I would buy it for sure. By the way I still can’t understand why someone would use the iPod nano to watch a movie or look at photos.
 It’s really thin and light, but the only market for that is kind of device is for children that their parents doesn’t want to give them a smartphone.
Conclusion
Sadly Apple haven’t got me back. I’ll continue to develop for BlackBerry, I think they are doing a better work to innovate with BlackBerry 10.


NOTICE: The below invoice is automatically generated, and will be voided if you amend your registration before payment, or if discounts or products contained in the invoice become unavailable. The items and discounts are only reserved until the invoice due time.
{% url "invoice_access" invoice.user.attendee.access_code as access_url %} {% url "invoice" invoice.id invoice.user.attendee.access_code as invoice_url %}You can send the following links to your accounts department to pay for your registration:


| MATLAB 文件帮助: cv.DualTVL1OpticalFlow | 查看 cv.DualTVL1OpticalFlow 的代码 | Index |
DualTVL1OpticalFlow "Dual TV L1" Optical Flow Algorithm
The class implements the "Dual TV L1" optical flow algorithm described in [Zach2007] and [Javier2012]. Here are important members of the class that control the algorithm, which you can set after constructing the class instance:
Tau
Time step of the numerical scheme.Lambda
Weight parameter for the data term, attachment
parameter. This is the most relevant parameter, which determines the
smoothness of the output. The smaller this parameter is, the smoother
the solutions we obtain. It depends on the range of motions of the
images, so its value should be adapted to each image sequence.Theta
Weight parameter for (u - v)^2
, tightness
parameter. It serves as a link between the attachment and the
regularization terms. In theory, it should have a small value in order
to maintain both parts in correspondence. The method is stable for a
large range of values of this parameter.ScalesNumber
Number of scales used to create the pyramid
of images.WarpingsNumber
Number of warpings per scale. Represents
the number of times that I1(x+u0)
and grad(I1(x+u0))
are computed
per scale. This is a parameter that assures the stability of the
method. It also affects the running time, so it is a compromise
between speed and accuracy.Epsilon
Stopping criterion threshold used in the
numerical scheme, which is a trade-off between precision and running
time. A small value will yield more accurate solutions at the expense
of a slower convergence.OuterIterations
Stopping criterion iterations number used
in the numerical scheme.[Zach2007]:
Christopher Zach, Thomas Pock, and Horst Bischof. "A Duality Based Approach for Realtime TV-L1 Optical Flow". In Pattern Recognition, pages 214-223. Springer, 2007.
[Javier2012]:
Javier Sanchez Perez, Enric Meinhardt-Llopis, and Gabriele Facciolo. "TV-L1 Optical Flow Estimation". 2012.
| Superclasses | handle |
| Sealed | false |
| Construct on load | false |
| DualTVL1OpticalFlow | Creates instance of DenseOpticalFlow |
| Epsilon | Stopping criterion threshold used in the numerical scheme, which is |
| Gamma | Coefficient for additional illumination variation term. default 0.0 |
| InnerIterations | Inner iterations (between outlier filtering) used in the numerical |
| Lambda | Weight parameter for the data term, attachment parameter. |
| MedianFiltering | Median filter kernel size (1 = no filter) (3 or 5). default 5 |
| OuterIterations | Outer iterations (number of inner loops) used in the numerical |
| ScaleStep | Step between scales (`<1`). default 0.8 |
| ScalesNumber | Number of scales used to create the pyramid of images. default 5 |
| Tau | Time step of the numerical scheme. default 0.25 |
| Theta | Weight parameter for `(u - v)^2`, tightness parameter. default 0.3 |
| UseInitialFlow | Use initial flow. default false |
| WarpingsNumber | Number of warpings per scale. default 5 |
| id | Object ID |
| addlistener | Add listener for event. | |
| calc | CALL Calculates an optical flow | |
| collectGarbage | Releases all inner buffers | |
| delete | Destructor | |
| eq | == (EQ) Test handle equality. | |
| findobj | Find objects matching specified conditions. | |
| findprop | Find property of MATLAB handle object. | |
| ge | >= (GE) Greater than or equal relation for handles. | |
| gt | > (GT) Greater than relation for handles. | |
| Sealed | isvalid | Test handle validity. |
| le | <= (LE) Less than or equal relation for handles. | |
| lt | < (LT) Less than relation for handles. | |
| ne | ~= (NE) Not equal relation for handles. | |
| notify | Notify listeners of event. |
| ObjectBeingDestroyed | Notifies listeners that a particular object has been destroyed. |


|
{{user.name}} Total: {{personalTotal(user) | currency}} Balance: {{getBalance(user) | currency}} |
{{member.name}} Total: {{personalTotal(member) | currency}} Balance: {{getBalance(member) | currency}} |
|---|---|
No payments
|
No payments
|
{{currentTrans.description}}No description.


Plugins
The PiTiVi plugin system will help keep the core to a minimum codebase required for normal editing. The plugins will add various kind of functionalities/interfaces for new features, special hardwares, online services, ....
Use Cases for Plugins
This is a list of use cases for plugins so we can figure out what are the various categories of plugins we will need.
- Istanbul controller : To be able to do screen-grabs that can then be edited within pitivi
- YouTube uploader : uploading projects to YouTube
- 3rd party effects : provides higher-level effects re-using existing GStreamer elements. (optional) can provide a specific UI.
- UI-only plugins : provide a different user interface to manipulate the timeline for specific tasks (compositing-specific view for example)
- Specific hardware integration
- image sequence loader : So you can load a sequence of image files and use that as a source in the timeline.
- Put your ideas here...
Case Studies
Here we analyze some pluggable architecture implemented by other opensource projects in order to focus their strength/weak points in relation to pitivi's needs. The list here does not pretend at all to be exaustive but is only meant to provide a cheap guided tour of the neighbourhood.
The Deluge way
Deluge is a bittorrent client written in python+gtk (http://www.deluge-torrent.org), it provides one of the most simple implementation of a pluggable architecture:
Plugins are stored in subdirectories of the ./plugin path. Each subdirectory takes the name of the contained plugin. Each plugin should at least provide an __init__.py file which defines the custom namespace and provides the access points for the plugin.
Inside the __init__.py are stored:
- Description fields (plugin's name, author, version and description)
- deluge_init() function is called to initialize the plugin
- enable() function is called when the user enables the plugin
The plugin manager scans the plugin directory for new items and when a plugin is found, it imports the namespace into the list of plugins and calls the deluge_init() function to initialize the plugin.
This implementation has the major advantage of being lightweight.
It does not create a class hierarchy for plugins: this can be both an advantage and an handicap since changes in the common base plugin class would potentially break plugin's compatibility but having a common ancestor can simplify usual operation by defining them inside the ancestor.
It is not possible to categorize plugins, even if a simple “category” field inside the __init__.py would be enough.
This plugin architecture is useful for extending the application with functionality not considered in the core objects, since the plugin has full control over the application and can thus extend any aspect of the host.
The Jokosher way
Jokosher is an audio production software that uses python+gtk+gstreamer (http://www.jokosher.org)
Its pluggable architecture places plugins into the ./extensions directory. Plugins can be stored as source .py files or packed into python eggs.
Extension API are provided for inserting menu items inside the main jokosher menu, as well as functions to control playback, instruments and to add new output formats.
Plugin preferences are stored in the ./extension-config path, with one config file for each plugin named as the plugin itself. Preferences are serialized by pickle into a dictionary of objects; standard methods to store and retrieve keys from the dictionary are available to the plugin writer.
The plugin manager install/remove plugins taking care of possible conflicts, it also manages the loading/unloading/configuration processes for each plugin, ensuring each plugin is loaded only once (plugins are treated as singletons).
Each plugin must contain:
- some descriptive fields identifying the name, description and version
- startup(api) function is called when the plugin is activated, passing the whole API set to the the plugin.
- shutdown() function is called when the plugin is deactivated, it takes care of the cleanings.
This approach defines a clear set of API that the plugin can use as preferred way to interact with the main application, API are pushed into the plugin when it is activated and a reference to them is usually kept inside the plugin though all its lifetime.
API for UI integration allow to insert new menu items, leaving to the plugin the responsibility to remove the inserted items when it is unloaded. The same logic is applied when additional output formats are provided by the plugin, those must be removed from available output format by the plugin itself when it is deactivated.
Strength points of the Jokosher approach are a well designed and lightweight plugin manager; the possibility to store plugins in python eggs that simplifies a lot the deployment of new plugins; a clean set of API the plugin can use to interact with the host application; the possibility to add new output formats; the possibility to save plugin's preferences without having the plugin care about serialization procedures.
Weak points of this architecture are a limited UI integration because plugins which uses only the API can insert menu items only under the “plugin” submenu; the creation of a preferences file for each plugin available could lead to a pollution of config files; leaving to plugins the duty of removing UI enhancements they inserted could lead to waste of memory if the plugin writer does not make a good job, a defaulf approach for cleaning would be preferable.
The Trac way
Trac is an enhanced wiki and issue tracking system for software development projects (http://trac.edgewall.org/), it has a consistent pluggable architecture that it strikingly rensembles the one of the Eclipse framework:
The main application exposes some entry points where plugin can plug into. each entry point is characterized by a contract that both the plugin and the main application must subscrive in order to interact; this contract takes the concrete form of an interface declared by the main application and implemented by the plugin.
Each plugin can implement multiple interfaces, in this way it extends multiple aspects of the main application.
Each entry point can be plugged by multiple plugins, thus the same feature of the main application can be extended in multiple ways.
Plugins can expose entry points themself, allowing them to be extended by other plugins.
Trac plugins inherits from the “Component” class and are deployed as python eggs.
The whole application is designed to be modular and plugins can also be created to replace built-in components.
Technically, the most of the work is done in the core.py file which declares the following structures:
- Interface class, defines the ancestor of all interfaces implemented by plugins using the implements() function
- ExtensionPoint class, defines each single extension point slot, characterized by an Interface that every component who wants to plug must conform.
- Component class, defines the generical component boundled to an exinsing component manager
- ComponentManager class, manages all the components available to the application, switching them on/off and taking care each component is a singleton.
Trac approach intruces a simple implementation for interfaces in python, an example code of how this architecture is used for creating plugins is reported in the following example (taken from Trac documentation):
from trac.core import *
class ITodoObserver(Interface):
def todo_added(name, description):
"""Called when a to-do item is added."""
class TodoList(Component):
observers = ExtensionPoint(ITodoObserver)
def __init__(self):
self.todos = {}
def add(self, name, description):
assert not name in self.todos, 'To-do already in list'
self.todos[name] = description
for observer in self.observers:
observer.todo_added(name, description)
class TodoPrinter(Component):
implements(ITodoObserver)
def todo_added(self, name, description):
print 'TODO:', name
print ' ', description
ITodoObserver interface provides a todo_added() callback to trigger plugin's functionalities.
The class TodoList is declared as expandable by declaring an extension point, used by plugins that implements the ITodoObserver interface.
Finally an example plugin TodoPrinter is written that extends the TodoList by implementing the interface ITodoObserver via the implements() function.
The Zope way
Since version 3.0 the CMS engine Zope (http://www.zope.org) introduced a full fledged interface implementation in python, as core system for its pluggable architecture.
In python the concept of interfaces, intended as a formal way to define relationships between items, does not yet exist; instead duck typing is the generally utilized way, so if an object hasattr(foo, 'bar') then foo.bar(), but this approach goes so far, since it is not possible to determine if an attribute is callable or has constrains to its possible values.
In fact plugin developers must rely over documentation to check what methods their classes have to provide to implement a certain feature, and this approach is very prone to broke plugins compatibility if documentation is not kept perfectly synchronized with the code.
Zope interfaces are designed to be applied not only to classes, but also to modules, objects and functions, since they follow the golden rule that “specification should make no assumption about implementation”; this approach leaves complete freedom to the plugin developer to organize its code in the way it prefers, as long the intefrace requirements are fulfilled; in this way duck typing gets formalized without adding a big overhead.
from zope.interface import Interface, implements, providedBy
class ISing(Interface):
def sing():
"""Returns a melody"""
class Sinatra(object):
implements(ISing)
def sing(self):
return "In singing in the rain..."
frank = Sinatra()
ISing.providedBy(frank) <-- True
Interfaces fully supports inheritance from other interfaces (with the security check that children's methods conforms to ancestor's one if overridden), implementer of an interface hierarchy can limit the implementation only to a certain child interface in the lineage; the status of interface implementer can be attached at runtime to items that do not explicitly implemented the interface in their definition; as well the implementer status can be removed from an item at runtime (useful for example in containers that implement an interface by delegating implementation to contained objects).
The other big feature of Zope interfaces is that they can be used to create adapters in a very efficient way, providing also a centralized registry for adapters.
Considerations
These case studies exemplify two different approaches for extending the host application:
In the first two cases (Deluge and Jokosher) the host application basically defines a plugin manager that takes care of activating and deactivating plugins; when activated, the plugin is allowed to access the core objects and modify the application as it wants.
This approach has the advantage to leave complete freedom to plugins at the cost of anarchy, since there isn't a standardized way for the plugin to interact with the host application. Jokosher tries to overcome this situation by defining some API that can be optionally used by plugins to accomplish common tasks. The other big advantage is that plugins can be created to extend aspect of the application not considered originally by the author to be extendable.
In the third case study (Trac), as opposite, plugins can only extend specific aspects of the host application defined as extension points, whose declare a clear contract the plugin must subscrive if it wants to extend the host application.
This approach enforces a neat definition of rights and duties for the partiecipants to the extension process at the cost of some freedom. In this way plugins do not “put” their features into the application, but “offer” instead some extended features the application can use.
The last case study (Zope) further improves the concept of interfaces, adding the possibility to define attributes/constrains inside interfaces, providing functions to check interface implementation/definition, adding a complete facility to provide adapters; all this is done in the pythonic way of leaving the developer the most freedom.
The main advantage of using interfaces over class inheritance is that the former defines relationships between items and keeps the system highly decoupled, while the latter constrains thee partecipants to a gree an “is a” relationship, thus creating strong coupling between classes.
Pluggable architecture proposal
In the light of needed use cases and analyzed case studies the following hybrid pluggable architecture is proposed for Pitivi.
Nomeclature
From here on, we will use the term “extension point” to define an aspect of the main application which can be extended by plugins. In respect to extension point we'll define “host” the component (usually pitivi but also plugins can be extended) that exposes the extension point, and “extender” the component that plugs into the extension point in order to provide additional features to the host component. Every extension point is characterized by a contract that both the parts must subscrive in order to interact.
Plugin deployment
Since simple plugins may consist of a single python file, while complex plugins may comprise glade interfaces, code written in other languages, graphics etc.. etc.. then plugins needs to be packed.
A convenient method is to take advantage of python eggs (http://peak.telecommunity.com/DevCenter/PythonEggs) and create an .egg file for each plugin. Eggs are similar to java's jar files, they make very simple the shipping and installation of new plugins since all you need is to drag the .egg file inside the ./plugins directory. Eggs also ships a standard method for exposing plugin's entry points and dependencies, making easy to allow cooperation/subordination among plugins.
we can expect pitivi to be shipped with some default plugins available for all the users and meant to be uninstalled by the system administrator only; while it is also auspicable to let users install custom plugins and encourage them in writing new ones.
Default plugins could be placed inside the pitivi installation path (i.e /usr/local/pitivi/plugins), these plugins are usually installed by the linux distribution facility and only users with root privileges can modify them directly.
User plugins could be placed inside the user home directory (i.e. in /home/my_user/.pitivi/plugins) and can be installed/deleted/modified by the user.
Pitivi should check for both the locations when searching for available plugins, and should use the user home to store plugin configuration parameters (i.e /home/my_user/.pitivi/plugin-settings).
The extension act
The relationship that exists between extension points and plugins is schematized in this cartoon:
- Each plugin may act as extender and as host by exposing extension point itself.
- Each host may have many extension points.
- Each extension point may be extended by multiple extenders.
- Each extender may extend multiple extension points.
In order to plug into the extension point the extender component must conform a protocol declared by the host component; this intefrace specifies function signatures for the duties that host component is planned to demand to plugin, like callbacks for occurred events, widgets to integrate into the user interface or implementation of common operations over new file formats.
Each extension point should be able to keep a list of extenders plugged in, providing to the host component a convenient way for trasversing extenders.
Direct core objects access
For those plugin which wants to extend aspects of the application not exposing extension points, core objects of the host application can be manipulated directly by the plugin writer. Since in python classes does not encforce information hiding concepts, the plugin writer can easily access them and inject the new features.
In order to allow the plugin this alternative extension approach, the plugin writer will insert new features when the plugin is activated by the plugin manager, and provide a complete cleaning of plugged features when the plugin is deactivated.
Plugin manager
The plugin manager is the orchestra director, its duties comprise:
- Keep track of available plugins
- Install/Remove plugins
- Activate/deactivate plugins
- Provide facilities to store and retrieve plugin's preferences
- Retrieve plugins from the pools on a category search criterium
- Resolve versioning conflicts and incompatibilities among plugins
Use cases for plugin manager
Install a new plugin
For a generic plugin the user:
- Open the plugins list window
- Drags&drop the new plugin's package inside the list of plugins
For an effect/transition the user:
- Selects the effects list
- Drag&drop the effect plugin inside the list of available effects
The plugin manager backend:
- Catch the drop of items inside the plugin list
- Check the item dropped is really a plugin compatible with the current version of the application
- Check if the plugin requires other plugins to be installed, stop if those are not present
- Copy the package inside the user plugin directory
- register the plugin among the pool of available plugins
- If there are plugins requiring the installed one to work, make them activable by the user
- Update the plugin/effect/transition list
Uninstall a custom plugin
The user:
- Drag&drop the plugin from the list of plugins to the trash
The plugin manager backend:
- Catch the drop of a plugin from the list to the trash
- Check for plugins that depends upon this one, and ask the user if he wants to cancel the operation or proceed anyway
- Deactivate the plugin if active
- Unregister the plugin from the pool of available plugins
- Remove the plugin package
- Disable plugins whose require the trashed plugin to be activated
- Make plugin whose require the trashed plugin unactivable by the user
- Update the plugin/effect/transition list
Upgrade an existent plugin
The user:
- Drag&drop the new version of the plugin into the list of plugins
The plugin manager:
- Check the plugin pool for duplicates of the plugin to be installed
- Ensure the version of the dropped plugin is newer than the current's, otherwise prompt the user for confirmation
- Remove the old plugin, preserving configuration items
- Install the new plugin
Configure a plugin
The user:
- Open the plugin manager window
- Select the plugin to configure
- Press the “Configure” button to show the configuration dialog.
The plugin manager:
- Ask the plugin for the configuration dialog.
- Retrieve from the plugin configuration file the dictionary of config values belonging to the plugin
- Pass the plugin the dictionary
- The plugin updates the dictionary passed by the plugin manager
- Save the dictionary in the file containing plugin configuration
Enable/Disable a plugin
The user:
- Open the plugin manager window
- Check/Unckeck the plugin
The plugin manager:
- Load/unload the plugin
- Refresh the user interface if plugin extended it
Design
UI integration
Extending the user interface could be achieved in basically two ways in GTK:
The first is writing custom code to integrate new widgets provided by the plugin into the current user interface design.
The second way is taking advantage of the UImanager component provided by gtk, this facility allow the developer to define UI widgets as xml then the UImanager can be told to merge the xml code into the existing GUI. Even thought this approach requires the plugin writer to learn UImanager xml syntax, the process clearly separate the UI creation part from the definition of actions triggered by the new UI piece, so it'll be preferred to the former approach when possible.
Despite of the type of widget integrated in the main user interface the following interface definitions will provide methods with a “UI” suffix meant to provide to the UImanager the xml code to integrate inside the main user interface, and other methods with the suffix “Action” meant to provide to the main application actions to bind to the main UI widgets.
Plugins must conform to the following protocol if they want to extend pitivi UI, in change pitivi is obliged to integrate the widgets provided by plugins in the way plugins define, with the freedom to choose when it's time to integrate them (usually when the main user interface is built at application startup or when user choose to activate the plugin).
Menu / Toolbar integration
Placing a menu item or a tool button is the simplest way a plugin can interact with the user, the following interfaces provide convenient ways for the plugin to integrate widgets in the main user interface:
Class IMenuProvider(Interface):
def getUI():
"""Return the xml code defining the user interface enhancement"""
def getActions():
"""Return the list of actions triggered by new menuitems"""
Additional source providers
This kind of plugins provides items user can drag into the timeline such as videoclips, effects, transitions. Pitivi inserts a new tab in the sources notebook displayed in the top-left portion of the main window. Plugins that want to be source providers must implement the following interface:
Class ISourceProvider(Interface):
def getName():
"""Return the name to be displayed in the notebook tab title"""
def getWidget():
"""Return the source view widget, usually a gtk box"""
def getCallbacks():
"""Return a tuple of (cb_name, signal_name) in the order they must be connected"
Note on signals handling: the plugin may provide callbacks to observe application signals, they're retrieved from the plugin with the getCallbacks() method, the returned tuple is coonnected to host application signals in the order specified by the plugin, and disconnected in reverse order when the plugin is deactivated.
Recording devices
Recording devices such as webcams, microphones but also unconventional items like screencast share the same requirements to be used by Pitivi:
class IRecordingDevice(Interface):
__gsignals__ =
{
"recording-started": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_VOID, ()),
"recording-finished": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_PYOBJECT, ())
}
"""recording-started informs observers the device started recording.
recording-finished signals the main application that a new clip is produced by the device,
the clip uri is returned by the signal in order to be added among
sources """
def getName():
"""Return the name of the device"""
def hasVideo():
"""Return true if device can provide video"""
def hasAudio():
"""Return true if device can provide audio"""
def record():
"""Start recording"""
def stop():
"""Stop recording"""
def isRecording():
"""Returns true if the device is currently recording"""
Effects/Transitions
This kind of items represent an exception to the general concept of plugin, which are usually meant to be singletons by definition (only a single object for each plugin is created by the application), since they're made to be applied more than one time over the timeline.
Effects/Transitions are collected and exposed to the user as a list, grouped into functional categories. This task will be accomplished by a specialized SourceProvider plugin called Effect(Transition)Provider, exposing entry points where Effects can be inserted. The Provider takes also care of creating a new effect object every time the user drags the effect to the timeline, thus implementing the Factory design pattern.
Every Effect object life-cycle starts in the moment the user drags it into the timeline and ends when its is removed, during its lifetime the object stores its custom preferencies in memory, while default preferences are stored in the same way of preferences of other plugins.
Detailed discussion of Effects/Transitions architecture will be discussed in a separate design document.
Links
Various links for inspiration...
Other python plugin systems
- http://trac.edgewall.org/wiki/TracDev/ComponentArchitecture
- http://trac-hacks.org/wiki/EggCookingTutorial/BasicEggCooking
- http://live.gnome.org/Gedit/PythonPluginHowTo
- http://jokosher.python-hosting.com/wiki/ExtensionSystem
- http://twistedmatrix.com/projects/core/documentation/howto/plugin.html
- http://termie.pbwiki.com/SprinklesPy
- http://docs.turbogears.org/1.0/TemplatePlugins
- http://www.zope.org
Other non-python plugin systems
- http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html
- http://www.codeproject.com/csharp/C__Plugin_Architecture.asp
- http://www.codeproject.com/dotnet/PluginManagerClassBrk.asp
- http://www.objectplanet.com/opinio/userguide/index.htm?page=plugin_architecture.html
- http://developer.mozilla.org/en/docs/Plugin_Architecture
- http://sync4jmozilla.sourceforge.net/Mozilla%20PIM%20Plugin%20Design%20Document.pdf
- http://www.gnome.org/projects/epiphany/documentation/extensions/
The results of the search are


Created on {{post.create_time}} by {{users[post.user_id].username}}
{{post.description}}
Created on {{comment.create_time}}


Benjamin Mua
Senior Linguist, Educator, Website Software Engineering student
Email<EMAIL_ADDRESS>Portfolio
Education
Higher Institute of Hospitality Management and Technology, Bamenda
H.N.D Tourism and Travel Agency Management, Hons
Professional Experiences
Subject Matter Expert / Online Instructor
Coursera, Mountain View, CA
March 2020 - Present
- Create interactive online IT courses
- Teach students worldwide
Founder, Software Engineer
skyRoute66, Los Angeles, CA
January 2019 - Present
- Author and publish mobile apps, reusable iOS UI modules
- Independent computer service contractor


Faced with the gaps between an incomplete, noisy, and shaky data base, and a host of scientific questions, hypotheses, and policies, a panoply of tools has been developed by social scientists, statisticians, and risk assessors in order to cope with the limitations and uncertainties (e.g., extrapolation models, statistical inference, regression and structural equation modeling). Philosophers of science who immerse themselves in practice, however, have often discovered that the methods and models routinely used to these ends are mired in confusion, controversy, and unquestioned assumptions, often along with a reluctance among practitioners to tinker with already accepted methods. Yet when methodological disputes emerge in the sciences themselves, the scientific community seldom looks to philosophy for help in their resolution. Examples abound: disputes over hypothesis-testing in psychology; disputes over the proper uses of regression in applied statistics; disputes over does-response curves in estimating risks, disputes over the use of individual case studies versus aggregated comparisons of groups in cognitive neuropsychology, and so on.
Drawing upon their research in a variety of fields, each of the contributors of our symposium will offer distinct, yet interconnected, insights into:
1) the roles that philosophers of science can (and occasionally do) play in evaluating and instructing scientific methodology in practice;
2) the interrelationships between science policy disputes and disputes about the acceptability of methodological value judgments and the validity of the evidence;
3) the relevance of understanding and resolving “practical” methodological problems for problems in philosophy of science (about data, experiment, causal inference, model selection, and values in scientific evidence).
Emphasizing the education, fashions and disciplinary habits of philosophers, I will discuss some of the reasons why very little work in philosophy of science proves of value to the actual conduct of science, and give examples where it does or has.
Increased reliance on statistical models in the social sciences has not been accompanied by attention to testing the assumptions underlying these models - Misspecification (M-S) testing. Routinely used M-S tests poorly detect obvious violations in assumptions, and endorse fallacious ways of accommodating violations when found. Debates over M-S tests often revolve around philosophical issues, but philosophers are rarely involved in resolving them. Ironically, philosophers of science often appeal to model selection techniques in their work, overlooking questions about model assumptions. A key task is to distinguish reliable from unreliable methods of 'data snooping' and 'data mining' in the area of M-S testing.
For the last two years, members of the International Commission on Radiological Protection (ICRP) have debated the merits of alternative hypotheses about the shape of the dose-response curve for biological effects of ionizing radiation. (The author is a member of a major ICRP committee that, within the next year, will make recommendations to international regulators about the scientific merits of various hypotheses.) The ICRP controversy is fueled, on the one hand, by fears of nuclear power and of a repetition of the Chernobyl accident and, on the other hand, by desires to cut costs in weapons clean-up and reactor decommissioning. In response to this conflict, many people argue that uncertain science -- or controversial policies based on science -- can be clarified primarily by greater attention to the social values influencing the science and the policy. This paper argues that while such clarification may be necessary, it is neither a sufficient condition, nor even the primary means, by which to achieve better science and better policy. Using a case study involving the current, highly politicized controversy over the shape of dose-response curves for biological effects of ionizing radiation, the paper argues that the conflict could be resolved largely through metascience, by getting the science right. Getting the science right, in this biological case, would mean taking adequate account of (1) internal and external inconsistencies, (2) poor predictive power, and (3) false inferences about measurement uncertainties, associated with one (rather than its main alternative) hypothesis about the proposed dose-response curve.
Archaeologists deal with one of the noisiest and shakiest of data bases, and they are acutely aware of this; they actively debate the basic assumptions, as well as more technical problems, associated with standard techniques for stabilizing evidence. Periodically philosophy of science has been an important resource in addressing these issues, with mixed results. I argue that, if we are to play a useful role in adjudicating normative questions about the limitations and uncertainties of specific research methods, we must be prepared to transgress the distinctions between epistemic and non-epistemic considerations that structure much philosophical analysis of science.


FANCI'S PERSUASION A film review by Max Hoffmann Copyright 1995 Max Hoffmann
Touted as a chaotic fairy tale for the queer, urban world, FANCI'S PERSUASION might more aptly be retitled GET OUT OF TOWN! Because that's exactly what you'd be doing if San Francisco even slightly resembled the unwatchable mish mash captured by Director/Writer/Producer Charles Herman-Wurmfeld. If you're part of the politically correct set that can't possibly criticize a queer film, and just have to support it no matter what--my advice; go to this flick stoned.
The press kit claims this film celebrates the visual styles of Pedro Almodovar and John Waters with a story loosely based on A MIDSUMMER NIGHT'S DREAM. The only thing missing is wit, humor and talent. As near as we can tell, a few cardboard cutouts of a flamingos constitute the tribute to John Waters, and Almodovar is honored with a few garishly painted walls in some Mission District flats and intricate hair-dos.
The film falls several yards short of being high camp enough to entertain on an unintended level. Frequent black-and-white dream segments might have caused a few titters, if they weren't so damned embarrassing. Most of the performances invoke the same type of painful embarrassment you feel for a neighbor's talentless child who's goaded into an impromptu recital.
Cinematographer David Rush Morrison spins gaudy images that resemble a film student's homage to Andy Warhol. However unsteadily the camera may be held, it's an irresistible attraction for the actors, who are more than just a little aware of its presence.
Drag superstar Justin Bond is utterly wasted in the role of the vicious mother to lesbian bride wanna-be, Fanci (Jessica Patton). The title character is surely the most annoying screen presence this side of Pippi Longstocking. Fanci's intended, Lorretta (Boa) is yet another screen version of a black lesbian that can't let "butch" and talent coexist in the same frame.
The plot: A mysterious spell falls over San Francisco, causing power outages, and unlikely sexual liaisons. Will the marriage vows be spoken? asks the press kit. The answer is a resounding: who cares!
A butt-numbing 80 minutes of incomprehensible sound and image can be yours at San Francisco's Red Vic Theatre October 5 & 6. FANCI'S PERSUASION will crop up regularly at Midnight showings around the country.
Maxwell Hoffmann film critic, OutNOW!
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


{% extends "base.html" %} {% load i18n static %} {% block metatitle %} {% trans "Password Reset" %} {% endblock %} {% block title %}
{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}
{% else %}{% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}
{% endif %} {% endblock %}


|
mbed TLS v2.16.11
|
Portable interface to timeouts and to the CPU cycle counter. More...
Go to the source code of this file.
Data Structures | |
| struct | mbedtls_timing_hr_time |
| timer structure More... | |
| struct | mbedtls_timing_delay_context |
| Context for mbedtls_timing_set/get_delay() More... | |
Typedefs | |
| typedef struct mbedtls_timing_delay_context | mbedtls_timing_delay_context |
| Context for mbedtls_timing_set/get_delay() More... | |
Functions | |
| unsigned long | mbedtls_timing_hardclock (void) |
| Return the CPU cycle counter value. More... | |
| unsigned long | mbedtls_timing_get_timer (struct mbedtls_timing_hr_time *val, int reset) |
| Return the elapsed time in milliseconds. More... | |
| void | mbedtls_set_alarm (int seconds) |
| Setup an alarm clock. More... | |
| void | mbedtls_timing_set_delay (void *data, uint32_t int_ms, uint32_t fin_ms) |
Set a pair of delays to watch (See mbedtls_timing_get_delay() .) More... | |
| int | mbedtls_timing_get_delay (void *data) |
| Get the status of delays (Memory helper: number of delays passed.) More... | |
| int | mbedtls_timing_self_test (int verbose) |
| Checkup routine. More... | |
Variables | |
| volatile int | mbedtls_timing_alarmed |
Portable interface to timeouts and to the CPU cycle counter.
Definition in file timing.h.
| typedef struct mbedtls_timing_delay_context mbedtls_timing_delay_context |
Context for mbedtls_timing_set/get_delay()
| void mbedtls_set_alarm | ( | int | seconds | ) |
Setup an alarm clock.
| seconds | delay before the "mbedtls_timing_alarmed" flag is set (must be >=0) |
| int mbedtls_timing_get_delay | ( | void * | data | ) |
Get the status of delays (Memory helper: number of delays passed.)
| data | Pointer to timing data Must point to a valid mbedtls_timing_delay_context struct. |
| unsigned long mbedtls_timing_get_timer | ( | struct mbedtls_timing_hr_time * | val, |
| int | reset | ||
| ) |
Return the elapsed time in milliseconds.
| val | points to a timer structure |
| reset | If 0, query the elapsed time. Otherwise (re)start the timer. |
Determining the elapsed time and resetting the timer is not atomic on all platforms, so after the sequence `{ get_timer(1); ...; time1 = get_timer(1); ...; time2 = get_timer(0) }` the value time1+time2 is only approximately the delay since the first reset.
| unsigned long mbedtls_timing_hardclock | ( | void | ) |
Return the CPU cycle counter value.
| int mbedtls_timing_self_test | ( | int | verbose | ) |
Checkup routine.
| void mbedtls_timing_set_delay | ( | void * | data, |
| uint32_t | int_ms, | ||
| uint32_t | fin_ms | ||
| ) |
Set a pair of delays to watch (See mbedtls_timing_get_delay()
.)
| data | Pointer to timing data. Must point to a valid mbedtls_timing_delay_context struct. |
| int_ms | First (intermediate) delay in milliseconds. The effect if int_ms > fin_ms is unspecified. |
| fin_ms | Second (final) delay in milliseconds. Pass 0 to cancel the current delay. |
mbedtls_timing_set_timer
directly or use this function with int_ms == fin_ms. | volatile int mbedtls_timing_alarmed |


Unity WebGL Player |
%UNITY_WEB_NAME%
Enter VR
Reset View
Fullscreen
%UNITY_WEB_NAME%
« created with
Unity
»
%UNITY_WEBGL_LOADER_GLUE%


Anti-Spyware
Home
NYCHope
About
Contact
FAQ
Username:
Password:
Remember Me
Invalid credentials. Try again!
SIGN IN
Forget Your Password?
Don't have an account?
Sign up here!


It is a long established fact
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic
Lorem Ipsum is simply dummy text of the printing text
January 3rd, 2013 , Posted by H. Rackham
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue;
There are many variations of passages
march 26th, 2013 , Posted by J. Smith
Contrary to popular belief, Lorem Ipsum is not simply random text.  Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem IpsumOn the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment,
The standard chunk of Lorem Ipsum used since
Auguest 3rd, 2013 , Posted by H. Rackham
Contrary to popular belief, Lorem Ipsum is not simply random text.  Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem IpsumOn the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment,


handleEvent method
- @override
Override this method to handle pointer events that hit this render object.
For RenderBox
objects, the entry
argument is a BoxHitTestEntry
. From this
object you can determine the PointerDownEvent
's position in local coordinates.
(This is useful because PointerEvent.position
is in global coordinates.)
If you override this, consider calling debugHandleEvent as follows, so
that you can support debugPaintPointersEnabled
:
@override
void handleEvent(PointerEvent event, HitTestEntry entry) {
assert(debugHandleEvent(event, entry));
// ... handle the event ...
}
Implementation
@override
void handleEvent(PointerEvent event, _SpriteBoxHitTestEntry entry) {
if (!attached)
return;
if (event is PointerDownEvent) {
// Build list of event targets
if (_eventTargets == null) {
_eventTargets = <Node>[];
_addEventTargets(_rootNode, _eventTargets);
}
// Find the once that are hit by the pointer
List<Node> nodeTargets = <Node>[];
for (int i = _eventTargets.length - 1; i >= 0; i--) {
Node node = _eventTargets[i];
// Check if the node is ready to handle a pointer
if (node.handleMultiplePointers || node._handlingPointer == null) {
// Do the hit test
Offset posInNodeSpace = node.convertPointToNodeSpace(entry.localPosition);
if (node.isPointInside(posInNodeSpace)) {
nodeTargets.add(node);
node._handlingPointer = event.pointer;
}
}
}
entry.nodeTargets = nodeTargets;
}
// Pass the event down to nodes that were hit by the pointerdown
List<Node> targets = entry.nodeTargets;
for (Node node in targets) {
// Check if this event should be dispatched
if (node.handleMultiplePointers || event.pointer == node._handlingPointer) {
// Dispatch event
bool consumedEvent = node.handleEvent(new SpriteBoxEvent(globalToLocal(event.position), event.runtimeType, event.pointer));
if (consumedEvent == null || consumedEvent)
break;
}
}
// De-register pointer for nodes that doesn't handle multiple pointers
for (Node node in targets) {
if (event is PointerUpEvent || event is PointerCancelEvent)
node._handlingPointer = null;
}
}


{{> components/products/product-details}}
The International Federation of Aromatherapists does not recommend that Essential Oils be taken internally unless under the supervision of a Medical Doctor who is also qualified in clinical Aromatherapy. All cautions listed for individual oils do not include those cautions from ingestion.
This statement has not been evaluated by the Food and Drug Administration. This product is not intended to diagnose, treat, cure, or prevent any disease.
Bottles are filled by volume. Some bottle sizes may not be filled to the top, but do contain the volume of oil specified.
{{!#if product.description}}
{{!/if}}
{{#if product.warranty}}
{{{product.warranty}}}
{{/if}}
{{#all product.custom_fields theme_settings.show_custom_fields_tabs}}
{{#each product.custom_fields}}
- {{name}}:
- {{{value}}}
{{/each}}
{{/all}}
{{#all settings.show_product_reviews theme_settings.show_product_reviews}}
{{> components/products/reviews reviews=product.reviews product=product urls=urls}}
{{/all}}


Advanced Web Design
Projects
bash letter
git letter
git hub letter
niccage- esquisite corpse project
sprites
faux company - SNUBER
In-Class Exercises
boiling semantics
git milk
gut hubba
emmet
sass demo
sprite demo
sass boilerplate
Assigments
about me
GitHub Account
Love Letter to Bash
View Site
Love Letter to Git
View Site
Love Letter to GitHub
View Site
Equisite Corpse-Nic Cage
View Site
Sprites
View Site
Snuber
View Site


NX3 - Fatal Error Occurred
>#_ERROR_MSG_#< Contact the website administrator for more information.
>#_ERROR_DETAILS_#<


---
layout: post
title: 4/28 progress
date: '2014-04-28T22:44:00.000-06:00'
author: Jacob .
tags:
- progress-reports
modified_time: '2014-04-28T22:45:01.864-06:00'
blogger_id: tag:blogger.com,1999:blog-6400085873105072120.post-2837247761554526565
blogger_orig_url: http://www.svlark.com/2014/04/428-progress.html
---
April did an awesome job this weekend packing the girls' clothes bins. All the girl clothes left in the house are those going with us. 50 gallons of kid clothes went out. Short post, but huge progress.
50 days left.


Thiago Silva slams Lionel Messi after Barca star tells Brazil coach to 'shut it'
Thiago Silva has slammed Lionel Messi for his conduct during Argentina’s victory over Brazil.
The Barcelona superstar scored the only goal of the prestige friendly in Saudi Arabia, reacting fastest in the 14th minute, converting the rebound after Liverpool goalkeeper Alisson Becker had saved Messi’s penalty.
Messi, making his return from a three-month international ban having accused tournament organisers of fixing the Copa America in Brazil’s favour, was also involved in the game’s most controversial moment when he told Selecao coach, Tite, to ‘shut it’.
PSG defender Silva accused Messi of showing signs of disrespect and believes the 32-year-old is guilty of trying to referee matches on his own and place too much pressure on the officials.
‘He wanted to rule over the game,’ said Silva. ‘He kicked two people and the referee did nothing.
Advertisement Advertisement
‘I argued with the ref and he kept laughing. You have to put admiration to one side.
‘He always looks to force the referee to give them free-kicks in dangerous areas, he always acts in that way.
‘We spoke with some players who play in Spain and the same thing happens, he looks to control the game and the referee’s decisions.
‘In the Champions League he doesn’t have that advantage because the referees are tougher.
‘You don’t see him trying to rule so much. There are referees that, because of their admiration for him, start to weigh in on his side. That we didn’t have Neymar on our side was a disadvantage.
‘It’s hard to understand when we talk about respect on the pitch, and one of the most admired players in the world does that, you don’t do that to an older person but being that it was a coach.
‘As much as there is rivalry, respect has to come first.’


Do you ever get frustrated that a problem in your town or neighborhood goes ages without being resolved?
You might have road issues (potholes, dangerous areas..), overgrown plant issues, or anything else that makes living where you live a little less pleasant.
With Co-make you can make your voice heard on the issues you would like to see resolved in your community.
Get involved with decision making in your community! In this process you can upovote issues you'd like to see taken care of or even add suggestions of you own. You'll be engaging with your community, getting to know the problems impacting your neighbors and begin effecting change at home.
Register with your community to start helping make decisions today. Find Issues that affect you or add issues of your own and get them to the top of the list. As well as seeing real change in your community you get rewards for being a positive community member in the form of points, freebies and local recognition.
Don't just think about problems with your local government or neighborhood, think about ways to bring your community together. Always wanted to meet those neighbors with super cool landscaping or the big fluffy dog? Suggest neighborhood meetups or even new green spaces.


Welcome to PyTorch Tutorials¶
To get started with learning PyTorch, start with our Beginner Tutorials. The 60-minute blitz is the most common starting point, and gives you a quick introduction to PyTorch. If you like learning by examples, you will like the tutorial Learning PyTorch with Examples
If you would like to do the tutorials interactively via IPython / Jupyter, each tutorial has a download link for a Jupyter Notebook and Python source code.
We also provide a lot of high-quality examples covering image classification, unsupervised learning, reinforcement learning, machine translation and many other applications at https://github.com/pytorch/examples/
You can find reference documentation for PyTorch’s API and layers at http://docs.pytorch.org or via inline help. If you would like the tutorials section improved, please open a github issue here with your feedback: https://github.com/pytorch/tutorials


Please enter your name:
Please enter your email address:
May we contact you?
What are your favourite colours?
What vehicles do you have a license to operate?
Please enter your message:


Displays the functionality of Promises and await/asynch method to create a synchronous environment on the asynchronous event loop.
Required for file IO. Used to create file directories.
Creates a directory with all users read/write/execute permissions. Permissions are granted to allow the user to delete the folder after executing this script.
(string)
The relative/absolute directory.
any
:
A Promise object to create the directory and modify the permissions.
Displays using the returned Promise from the makeDir method using the Promise object syntax. Creates a 'promises/' folder with 3 sub-directories.
Displays using the returned Promise from the makeDir method using await/async. Creates an 'async/' folder with 3 sub-directories.
Error handler for all exceptions caught.
(exception)
The exception being thrown.