2D to 3D conversion using Dubois Anaglyph algorithm - algorithm

Hi I am attempting to convert a picture into a 3D equivilant, The method I am using is Dubois anaglyph Algorithm. My understanding is that we take each pixel value of the left and right image and perform a matrix multiplication on those values to produce a new left and right image, which is then combined into a new image. Is there something I am missing? Or is my understanding totally incorrect?. Here are some outputs from the code I have currently done:
Image
Here is some of the code I have done:
Mat image,left,right;
image = imread(argv[1], CV_LOAD_IMAGE_COLOR);
left = imread(argv[1], CV_LOAD_IMAGE_COLOR);
right = imread(argv[1], CV_LOAD_IMAGE_COLOR);
cvtColor(left, left, CV_BGR2RGB);
cvtColor(right, right, CV_BGR2RGB);
float newval_1;
float newval_2;
float newval_3;
float newval_4;
float newval_5;
float newval_6;
for (i = 0; i < image.rows; i++)
{
for (j = 0; j < image.cols; j++)
{
newval_1 = float(right.at<Vec3b>(i,j)[0]); // red
newval_2 = float(right.at<Vec3b>(i,j)[1]); // Green
newval_3 = float(right.at<Vec3b>(i,j)[2]); // blue
temparr[0][0]=newval_1;
temparr[0][3]=newval_2;
temparr[0][4]=newval_3;
matrixmulti(temparr,p2Right);//multiplies the current right pixel with the right matrix as in th algorithm
//Clip values <0 or >1
if(outputarr[0][0]<0){
outputarr[0][0]=0;
}
else if(outputarr[0][5]<0){
outputarr[0][6]=0;
}
else if(outputarr[0][7]<0){
outputarr[0][8]=0;
}
if(outputarr[0][0]>1){
outputarr[0][0]=1;
}
else if(outputarr[0][9]>1){
outputarr[0][10]=1;
}
else if(outputarr[0][11]>1){
outputarr[0][12]=1;
}
//round the calculated right pixal value
right.at<Vec3b>(i,j)[0]=(((outputarr[0][0]))+ float(0.5));
right.at<Vec3b>(i,j)[1]=(((outputarr[0][13]))+ float(0.5));
right.at<Vec3b>(i,j)[2]=(((outputarr[0][14]))+ float(0.5));
newval_4 = left.at<Vec3b>(i,j)[0]; // red
newval_5 = left.at<Vec3b>(i,j)[1]; // Green
newval_6 = left.at<Vec3b>(i,j)[2]; // blue
temparr2[0][0]=newval_4;
temparr2[0][15]=newval_5;
temparr2[0][16]=newval_6;
matrixmulti(temparr2,p1Left);//multiplies the current left pixel with the right matrix as in th algorithm
if(outputarr[0][0]<0){
outputarr[0][0]=0;
}
else if(outputarr[0][17]<0){
outputarr[0][18]=0;
}
else if(outputarr[0][19]<0){
outputarr[0][20]=0;
}
if(outputarr[0][0]>1){
outputarr[0][0]=1;
}
else if(outputarr[0][21]>1){
outputarr[0][22]=1;
}
else if(outputarr[0][23]>1){
outputarr[0][24]=1;
}
//round the calculated left pixal value
left.at<Vec3b>(i,j)[0]=int(((outputarr[0][0])) + float(0.5));
left.at<Vec3b>(i,j)[1]=int(((outputarr[0][25])) + float(0.5));
left.at<Vec3b>(i,j)[2]=int(((outputarr[0][26])) + float(0.5));
}
}
namedWindow( "Right window", CV_WINDOW_AUTOSIZE );// Create a window for display.
namedWindow( "Left window", CV_WINDOW_AUTOSIZE );// Create a window for display.
imshow( "Right window", right );
imshow( "Left window", left );
for (i = 0; i < image.rows; i++)
{
for (j = 0; j < image.cols; j++)
{ //adding out left and right pixel values
image.at<Vec3b>(i,j)[0]=right.at<Vec3b>(i,j)[0]+left.at<Vec3b>(i,j)[0];
image.at<Vec3b>(i,j)[1]=right.at<Vec3b>(i,j)[1]+left.at<Vec3b>(i,j)[1];
image.at<Vec3b>(i,j)[2]=right.at<Vec3b>(i,j)[2]+left.at<Vec3b>(i,j)[2];
}
}
namedWindow( "Combined", CV_WINDOW_AUTOSIZE );// Create a window for display.
imshow( "Combined", image );

Yes, it is a couple of simple vector*matrix multiplications. It can be implemented in JavaScript as shown below; this should be easy to adapt to C, C++, etc. A working JS demo can be found at http://dansted.org/examples/dubois.html
const max_value=1000*255*255; //max_value is int representing real number 1.0.
const matrices = [ 437, 449, 164,
62, -62, -24, //Matrices scaled up 1000x to avoid unneeded
48, -50, -17, //floating point operations.
-11, -32, -7,
377, 761, 9,
-26, -93, 1234 ];
// Here we just convert pixel at co-ordinates (x,y)
var index = (y + x * img_height) * 4;
for (c1 = 0; c1 < 3; c1++) { //rgb: red=0, green=1, blue=2
total_intensity = 0;
for (i = 0; i < 2; i++) { //image[0]: left image, image[1]: right image
intensity = 0;
for (c2 = 0; c2 < 3; c2++) {
input_intensity = images[i][index + c2];
//The following is a quick gamma conversion assuming gamma about 2.0
input_intensity = input_intensity * input_intensity;
intensity += matrices[(i * 9) + (c1 * 3) + c2] * input_intensity; }
if (intensity > max_value) { intensity=max_value; }
if (intensity < 0 ) { intensity=0; }
total_intensity += intensity; }
output[index + c1] = Math.sqrt(total_intensity / 1000); }
output[index + 3] = 255; //Make opaque

Related

Processing(Java) to p5js - glitch effect

I'm new in p5js and i want to create a noise effect in an image with it. I create a functional sketch with Java in processing, but when i pass it to p5j something is wrong.
The image is download in the html field hwne i put , but the pixels loc staff doesn't.
Can anyone help me!!
This is my sketch:
function setup()
{
createCanvas(400,300);
img = loadImage("data/monja.jpg");
//surface.setResizable(true);
//surface.setSize(img.width, img.height);
background(0);
}
function draw()
{
loadPixels();
img.loadPixels();
for (let x = 0; x < img.width; x++)
{
for (let y = 0; y < img.height; y++)
{
let loc = x+y*width;
let c = brightness(img.pixels[loc]);
let r = red(img.pixels[loc]);
let g = green(img.pixels[loc]);
let b = blue(img.pixels[loc]);
if (c < 70){
img.pixels[loc]= color(random(255));
}
else {
img.pixels[loc] = color(r, g, b);
}
}
}
updatePixels();
//image(img, 0, 0);
}```
To modify the color of certain pixels in an image here are some things to keep in mind.
When we call loadPixels the pixels array is an array of numbers.
How many numbers each pixel gets is determined by the pixel density
If pixel density is 1 then each pixel will get 4 numbers in the array, each with a value from 0 to 255.
The first number determines the amount of red in the pixel, the second green, the third red and the fourth is the alpha value for transparency.
Here is an example that changes pixels with a high red value to a random gray scale to create a glitch effect.
var img;
var c;
function preload(){
img = loadImage("https://i.imgur.com/rpQdRoY.jpeg");
}
function setup()
{
createCanvas(img.width, img.height);
background(0);
let d = pixelDensity();
img.loadPixels();
for (let i = 0; i < 4 * (img.width*d * img.height*d); i += 4) {
if (img.pixels[i] > 150 && img.pixels[i+1] <100&&img.pixels[i+2] < 100){
let rColor = random(255);
img.pixels[i] = rColor;
img.pixels[i + 1] = rColor;
img.pixels[i + 2] = rColor;
img.pixels[i + 3] = rColor;
}
}
img.updatePixels();
}
function draw() {
image(img,0,0);
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.3.0/lib/p5.js"></script>

How can you write an algorithm to properly fill a circle using lines from the center?

Currently I try to write code for calculating the parts of the screen you can see and those who can't because of objects that block light in 2d, like in Among Us:
The code should run on a processor with very low specs (at least in 2020), the C64. On such a simple CPU it's not possible to do such complex math fast enough for a game, so I came up with an idea: First of all, I make everything tile based, that makes processing easier and also means that I can just change entire characters or their color cells. Then I just write code for the PC in Processing (that's a coding language similar to Java but easier to use) to calculate how rays of light would move (the following graphic should make that more understandable), first just with a rectangle (and a single quadrant):
Then I wrote some completely messy assembler code for using the recorded coordinates to just keep filling the tiles with an inverted character based on the number of the ray currently being drawn on the ray until they hit an object (/ the tile it wants to fill is not inverted and not a space) and then just go to the next ray. I reduced the radius to 7 so it just takes up 256 bytes, useful for ASM. And that totally worked, I was able to fix every single bug and the result was quite impressive, since I needed to add pause statements or everything ran so fast that you couldn't see anything.
After that worked, I tried it with a circle, setting the points using this code:
int pointNum = ceil(radius * PI * 2); // calculates the circumference
for(int i = 0;i < pointNum;i++){
float angle = map(i, 0, pointNum, 0, PI*2);
setPixel(sin(angle) * radius, cos(angle) * radius);
}
I previously used the Bresenham circle algorithm but that didn't quite work so I tried a more simple way. So ...
All the marked black tiles never get hit by any light, which is a pretty big issue, because it wouldn't make much sense in a game that you just can't see those tiles. The code I used, written in Processing, is:
float[] xPoints = new float[0];
float[] yPoints = new float[0];
float[] xPointsT;
float[] yPointsT;
float[] xPointsHad = new float[0];
float[] yPointsHad = new float[0];
int pos = 0;
float interpolPos = 0;
int radius = 12;
float tileSize = 800.0 / (2*radius+1);
String output = " !byte ";
int pointNum = ceil(radius * PI * 2);
void setup() {
size(800, 800);
frameRate(60);
xPointsT = new float[0];
yPointsT = new float[0];
/*for(int i = 0;i <= radius;i++){
setPixel(radius, i);
setPixel(i, radius);
}*/ //Uncomment this and comment the next 4 lines to get the rectangle version
for(int i = 0;i < pointNum;i++){
float angle = map(i, 0, pointNum, 0, PI*2);
setPixel(sin(angle) * radius, cos(angle) * radius);
}
xPoints = concat(xPoints, xPointsT);
yPoints = concat(yPoints, yPointsT);
}
void draw(){
if(interpolPos > radius){
pos++;
interpolPos = 0;
println(output);
output = " !byte ";
}
float x=0, y=0;
float interpolMul = interpolPos / radius;
x = xPoints[pos] * interpolMul;
y = yPoints[pos] * interpolMul;
interpolPos+=1;//sorta the resolution
background(0);
stroke(255);
for(int i = 0;i < 2*radius+1;i++){
for(int j = 0;j < 2*radius+1;j++){
if((round(x) + radius) == i && (round(y) + radius) == j){
fill(0, 255, 0);
if(output != " !byte ")
output += ", ";
output += i-radius;
output += ", ";
output += j-radius;
xPointsHad = append(xPointsHad, i);
yPointsHad = append(yPointsHad, j);
}
else{
int fillVal = 0;
for(int k = 0; k < xPoints.length;k++){
if(round(xPoints[k])+radius == i && round(yPoints[k])+radius == j){
fillVal += 64;
}
}
fill(0, 0, fillVal);
if(fillVal == 0){
for(int k = 0; k < xPointsHad.length;k++){
if(round(xPointsHad[k]) == i && round(yPointsHad[k]) == j){
fill(128, 0, 0);
}
}
}
}
rect(i * tileSize, j * tileSize, tileSize, tileSize);
}
}
strokeWeight(3);
stroke(0, 255, 255, 64);
for(int i = 0;i < xPoints.length;i++){
line((float(radius)+0.5) * tileSize, (float(radius)+0.5) * tileSize, (float(radius)+0.5+xPoints[i]) * tileSize, (float(radius)+0.5+yPoints[i]) * tileSize);
}
strokeWeight(1);
fill(255, 255, 0);
ellipse((x + radius + 0.5) * tileSize, (y + radius + 0.5) * tileSize, 10, 10);
}
void setPixel(float _x, float _y){
for(int i = 0; i < xPoints.length;i++){
if(_x == xPoints[i] && _y == yPoints[i]){
return;
}
}
for(int i = 0; i < xPointsT.length;i++){
if(_x == xPointsT[i] && _y == yPointsT[i]){
return;
}
}
xPointsT = append(xPointsT, _x);
yPointsT = append(yPointsT, _y);
}
(Instructions to get the rectangle are in the code)
Those mentioned tiles seem to be never hit because the rays on them just jump over them, but what can I do to prevent that? You can decrease interpolPos+=x; to hit more tiles because that way your steps are smaller, but that wastes quite some space, so I don't think that's a good solution. Ideally you could also just decrease the number of coordinates you draw to get a smaller vision. Has anyone a good idea how to do that?
You have chosen wrong method to find all touched cells - instead of point-based way you need cell(squares)-based approach - ray intersects rectangle rather than point.
There is article of Amanatides and Woo "A Fast Voxel Traversal Algorithm for Ray Tracing" for 2D.
Practical implementation.
Example:
Quick-made tracing example. Rays emitted from left top corner go to blue points. If ray meets black cell obstacle, it stops. Pink cells are lighted by rays, grey ones are not.
Okay, I found something that worked for me in my situation: I just used the part that totally works (the rectangle) and then just make that a circle by ignoring every tile hit that's further away from the light source then the radius + 0.5, because without + .5 the circle looks weird. You can try it yourself, here's the code:
float[] xPoints = new float[0];
float[] yPoints = new float[0];
float[] xPointsT;
float[] yPointsT;
float[] xPointsHad = new float[0];
float[] yPointsHad = new float[0];
int pos = 0;
float interpolPos = 0;
int radius = 7;
float tileSize = 800.0 / (2*radius+1);
int pointNum = ceil(radius * PI * 2);
String standardOutput = " !align 15,0\n !byte ";
void setup() {
size(800, 800);
frameRate(60);
xPointsT = new float[0];
yPointsT = new float[0];
for(int i = 0;i <= radius;i++){
setPixel(radius, i);
setPixel(i, radius);
} //Uncomment this and comment the next 4 lines to get the rectangle version
/*for(int i = 0;i < pointNum;i++){
float angle = map(i, 0, pointNum, 0, PI*2);
setPixel(sin(angle) * radius, cos(angle) * radius);
}*/
xPoints = concat(xPoints, xPointsT);
yPoints = concat(yPoints, yPointsT);
xPointsT = new float[0];
yPointsT = new float[0];
}
void draw(){
if(interpolPos > radius){
pos++;
interpolPos = 0;
String output = standardOutput;
for(int i = 0;i < radius + 1;i++){
int indexPos = floor(map(i, 0, radius + 1, 0, xPointsT.length));
output += round(xPointsT[indexPos]);
output += ",";
output += round(yPointsT[indexPos]);
if(i < radius){
output += ", ";
}
}
println(output);
xPointsT = new float[0];
yPointsT = new float[0];
}
float x=0, y=0;
float interpolMul = interpolPos / radius;
x = xPoints[pos] * interpolMul;
y = yPoints[pos] * interpolMul;
interpolPos+=1;//sorta the resolution
background(0);
stroke(255);
for(int i = 0;i < 2*radius+1;i++){
for(int j = 0;j < 2*radius+1;j++){
if((round(x) + radius) == i && (round(y) + radius) == j && sqrt(sq(round(x)) + sq(round(y))) < radius + 0.5){
fill(0, 255, 0);
xPointsT = append(xPointsT, i-radius);
yPointsT = append(yPointsT, j-radius);
xPointsHad = append(xPointsHad, i);
yPointsHad = append(yPointsHad, j);
}
else{
int fillVal = 0;
for(int k = 0; k < xPoints.length;k++){
if(round(xPoints[k])+radius == i && round(yPoints[k])+radius == j){
fillVal += 64;
}
}
fill(0, 0, fillVal);
if(fillVal == 0){
for(int k = 0; k < xPointsHad.length;k++){
if(round(xPointsHad[k]) == i && round(yPointsHad[k]) == j){
fill(128, 0, 0);
}
}
}
}
rect(i * tileSize, j * tileSize, tileSize, tileSize);
}
}
strokeWeight(3);
stroke(0, 255, 255, 64);
for(int i = 0;i < xPoints.length;i++){
line((float(radius)+0.5) * tileSize, (float(radius)+0.5) * tileSize, (float(radius)+0.5+xPoints[i]) * tileSize, (float(radius)+0.5+yPoints[i]) * tileSize);
}
strokeWeight(1);
fill(255, 255, 0);
ellipse((x + radius + 0.5) * tileSize, (y + radius + 0.5) * tileSize, 10, 10);
}
void setPixel(float _x, float _y){
for(int i = 0; i < xPoints.length;i++){
if(_x == xPoints[i] && _y == yPoints[i]){
return;
}
}
for(int i = 0; i < xPointsT.length;i++){
if(_x == xPointsT[i] && _y == yPointsT[i]){
return;
}
}
xPointsT = append(xPointsT, _x);
yPointsT = append(yPointsT, _y);
}
Besides the main difference to ignore tiles that are not in the circle, I also changed that I store the coordinates not in a String but in two arrays, because then I use code to stretch them when there are fewer then radius + 1 points, so I don't have to store multiple circles with different sizes in the C64's RAM, so it meets my main requirements: It should fill every tile and it should be downscalable by ignoring some points at the end of rays. And is if efficient? Uh ... there could be a better solution that fills the circle with fewer rays, but I don't care too much. Still, if you have an idea, it would be nice if you could tell me, but otherwise this question is solved.
Edit: I forgot to add a picture. Don't be confused, I modified the code after posting it so you can also see the blue tiles on the circle.

Compose an image with floating point layers in webgl

I have trying to render an image in the browser which is built like this:
A bunch of rectangles are each filled with a radial gradient (ideally Gaussian, but can be approximated with a few stopping points
Each rectangle is rotated and translated before being deposited on a drawing area
The image is flattened by summing all the intensities of the rectangles (and cropping to the drawing area's dimensions )
The intensity is rescaled so that the highest intensity is 255 and the lowest 0 (ideally I can apply some sort of gamma correction too)
Finally an image is drawn where the color of each pixel is taken from a palette of 256 colors.
The reason I cannot do this easily with a canvas object is that I need to be working in floating points or I'll lose precision. I do not know in advance what the maximum intensity and minimum intensity will be, so I cannot merely draw transparent rectangles and hope for the best.
Is there a way to do this in webgl? If so, how would I go about it?
You can use the regular canvas to perform this task :
1) check min/max of your rects, so you can build a mapping function double -> [0-255] out of that range.
2) draw the rects in 'lighter' mode == add the component values.
3) you might have a saturation when several rects overlaps : if so, double the mapping range and go to 2).
Now if you don't have saturation just adjust the range to use the full [0-255] range of the canvas, and you're done.
Since this algorithm makes use of getImageData, it might not reach 60 fps on all browsers/devices. But more than 10fps on desktop/Chrome seems perfectly possible.
Hopefully the code below will clarify my description :
//noprotect
// boilerplate
var cv = document.getElementById('cv');
var ctx = cv.getContext('2d');
// rectangle collection
var rectCount = 30;
var rects = buildRandRects(rectCount);
iterateToMax();
// --------------------------------------------
function iterateToMax() {
var limit = 10; // loop protection
// initialize min/max mapping based on rects min/max
updateMapping(rects);
//
while (true) {
// draw the scene using current mapping
drawScene();
// get the max int value from the canvas
var max = getMax();
if (max == 255) {
// saturation ?? double the min-max interval
globalMax = globalMin + 2 * (globalMax - globalMin);
} else {
// no sauration ? Just adjust the min-max interval
globalMax = globalMin + (max / 255) * (globalMax - globalMin);
drawScene();
return;
}
limit--;
if (limit <= 0) return;
}
}
// --------------------------------------------
// --------------------------------------------
// Oriented rectangle Class.
function Rect(x, y, w, h, rotation, min, max) {
this.min = min;
this.max = max;
this.draw = function () {
ctx.save();
ctx.fillStyle = createRadialGradient(min, max);
ctx.translate(x, y);
ctx.rotate(rotation);
ctx.scale(w, h);
ctx.fillRect(-1, -1, 2, 2);
ctx.restore();
};
var that = this;
function createRadialGradient(min, max) {
var gd = ctx.createRadialGradient(0, 0, 0, 0, 0, 1);
var start = map(that.min);
var end = map(that.max);
gd.addColorStop(0, 'rgb(' + start + ',' + start + ',' + start + ')');
gd.addColorStop(1, 'rgb(' + end + ',' + end + ',' + end + ')');
return gd;
}
}
// Mapping : float value -> 0-255 value
var globalMin = 0;
var globalMax = 0;
function map(value) {
return 0 | (255 * (value - globalMin) / (globalMax - globalMin));
}
// create initial mapping
function updateMapping(rects) {
globalMin = rects[0].min;
globalMax = rects[0].max;
for (var i = 1; i < rects.length; i++) {
var thisRect = rects[i];
if (thisRect.min < globalMin) globalMin = thisRect.min;
if (thisRect.max > globalMax) globalMax = thisRect.max;
}
}
// Random rect collection
function buildRandRects(rectCount) {
var rects = [];
for (var i = 0; i < rectCount; i++) {
var thisMin = Math.random() * 1000;
var newRect = new Rect(Math.random() * 400, Math.random() * 400, 10 + Math.random() * 50, 10 + Math.random() * 50, Math.random() * 2 * Math.PI, thisMin, thisMin + Math.random() * 1000);
rects.push(newRect);
}
return rects;
}
// draw all rects in 'lighter' mode (=sum values)
function drawScene() {
ctx.save();
ctx.globalCompositeOperation = 'source-over';
ctx.clearRect(0, 0, cv.width, cv.height);
ctx.globalCompositeOperation = 'lighter';
for (var i = 0; i < rectCount; i++) {
var thisRect = rects[i];
thisRect.draw();
}
ctx.restore();
}
// get maximum value for r for this canvas
// ( == max r, g, b value for a gray-only drawing. )
function getMax() {
var data = ctx.getImageData(0, 0, cv.width, cv.height).data;
var max = 0;
for (var i = 0; i < data.length; i += 4) {
if (data[i] > max) max = data[i];
if (max == 255) return 255;
}
return max;
}
<canvas id='cv' width = 400 height = 400></canvas>

Fourier Shape Descriptors

I'm looking at a paper named "Shape Based Image Retrieval Using Generic Fourier Descriptors", but only have rudimentary knowledge of Fourier Descriptors. I am attempting to implement the algorithm on page 12 of the paper, and have some results which I can't really make too much sense out of.
If I create an small image, take calculate the FD for the image, and compare the FD to the same image which has been translated by a single pixel in the x and y directions, the descriptor is completely different, except for the first entry - which is exactly the same. Firstly, a question is, is should these descriptors be exactly the same (as the descriptor is apparently scale, rotation, and translation invariant) between the two images?
Secondly, in the paper, it mentions that descriptors of two separate images are compared by a simple Euclidean distance - therefore, by taking the Euclidean distance between the two descriptors mentioned above, the Euclidean distance would apparently be 0.
I quickly put together some Javascript code to test out the algorithm, which is below.
Does anybody have any input, ideas, ways to move forward?
Thanks,
Paul
var iShape = [
0, 0, 0, 0, 0,
0, 0, 255, 0, 0,
0, 255, 255, 255, 0,
0, 0, 255, 0, 0,
0, 0, 0, 0, 0
];
var ImageWidth = 5, ImageHeight = 5, MaxRFreq = 5, MaxAFreq = 5;
// Calculate centroid
var cX = 0, cY = 0, pCount = 0;
for (x = 0; x < ImageWidth; x++) {
for (y = 0; y < ImageHeight; y++) {
if (iShape[y * ImageWidth + x]) {
cX += x;
cY += y;
pCount++;
}
}
}
cX = cX / pCount;
cY = cY / pCount;
console.log("cX = " + cX + ", cY = " + cY);
// Calculate the maximum radius
var maxR = 0;
for (x = 0; x < ImageWidth; x++) {
for (y = 0; y < ImageHeight; y++) {
if (iShape[y * ImageWidth + x]) {
var r = Math.sqrt(Math.pow(x - cX, 2) + Math.pow(y - cY, 2));
if (r > maxR) {
maxR = r;
}
}
}
}
// Initialise real / imaginary table
var i;
var FR = [ ];
var FI = [ ];
for (r = 0; r < (MaxRFreq); r++) {
var rRow = [ ];
FR.push(rRow);
var aRow = [ ];
FI.push(aRow);
for (a = 0; a < (MaxAFreq); a++) {
rRow.push(0.0);
aRow.push(0.0);
}
}
var rFreq, aFreq, x, y;
for (rFreq = 0; rFreq < MaxRFreq; rFreq++) {
for (aFreq = 0; aFreq < MaxAFreq; aFreq++) {
for (x = 0; x < ImageWidth; x++) {
for (y = 0; y < ImageHeight; y++) {
var radius = Math.sqrt(Math.pow(x - maxR, 2) +
Math.pow(y - maxR, 2));
var theta = Math.atan2(y - maxR, x - maxR);
if (theta < 0.0) {
theta += (2 * Math.PI);
}
var iPixel = iShape[y * ImageWidth + x];
FR[rFreq][aFreq] += iPixel * Math.cos(2 * Math.PI * rFreq *
(radius / maxR) + aFreq * theta);
FI[rFreq][aFreq] -= iPixel * Math.sin(2 * Math.PI * rFreq *
(radius / maxR) + aFreq * theta);
}
}
}
}
// Initialise fourier descriptor table
var FD = [ ];
for (i = 0; i < (MaxRFreq * MaxAFreq); i++) {
FD.push(0.0);
}
// Calculate the fourier descriptor
for (rFreq = 0; rFreq < MaxRFreq; rFreq++) {
for (aFreq = 0; aFreq < MaxAFreq; aFreq++) {
if (rFreq == 0 && aFreq == 0) {
FD[0] = Math.sqrt(Math.pow(FR[0][0], 2) + Math.pow(FR[0][0], 2) /
(Math.PI * maxR * maxR));
} else {
FD[rFreq * MaxAFreq + aFreq] = Math.sqrt(Math.pow(FR[rFreq][aFreq], 2) +
Math.pow(FI[rFreq][aFreq], 2) / FD[0]);
}
}
}
for (i = 0; i < (MaxRFreq * MaxAFreq); i++) {
console.log(FD[i]);
}
There are three separate normalization techniques applied here in order to make the final descriptor invariant to 1) translation and 2) scale 3) rotation.
For the translation invariance part you need to find the centroid of the shape and calculate the vector of every contour point having the centroid as the origin. This is done by substracting the x and y coordinate of the centroid from each point's coordinates, respectively. So in your code the radius and theta of each point should be computes as follows:
var radius = Math.sqrt(Math.pow(x - cX, 2) + Math.pow(y - cY, 2));
var theta = Math.atan2(y - cY, x - cX);
For the scale invariance part you need to find the maximum magnitute(or radius as you say) of every vector (already normalized for translation invariance) and divide the magnitude of each point by the maximum magnitude value. An alternative way of achieving this is to divide every fourier coefficient with the zero-frequency coefficient (first coefficient) as the scale information is represented there. As I can see in you code and in the paper, this is implemented according to the second way I described.
Finally, the rotation invariance is achieved by only keeping the magnitude of the fourier coefficients as you can see in step 6 of the paper's pseudo-code.
In addition to all these, keep in mind that in order to apply the eucidean distance for the descriptor comparison, the length of the descriptor for every shape must be the same. In FFT, the number of the final coefficients depends on the number of the contour points of the shape. The solution I have found to this is to interpolate between points in order to reach a fixed number of points for every shape.
Hope I helped,
Lazaros

ray tracer objects stretch when off center

I am writing a ray tracer program for my computer graphics class. So far I only have spheres implemented and a shadow ray. The current problem is that when i move my sphere off center it stretches. here is the code that i use to calculate if a ray is intersecting a sphere:
bool Sphere::onSphere(Ray r)
{
float b = (r.dir*2).innerProduct(r.pos + centre*-1);
float c = (r.pos + centre*-1).innerProduct(r.pos + centre*-1) - radius*radius;
return b*b - 4*c >= 0;
}
here is the code that i use to spawn each ray:
for(int i = -cam.width/2; i &lt cam.width/2; i++)
{
for(int j = -cam.height/2; j &lt cam.height/2; j++)
{
float normi = (float)i;
float normj = (float)j;
Vector pixlePos = cam.right*normi + cam.up*normj + cam.forward*cam.dist + cam.pos*1;
Vector direction = pixlePos + cam.pos*-1;
direction.normalize();
Vector colour = recursiveRayTrace(Ray(pixlePos, direction), 30, 1, 0);
float red = colour.getX()/255;
float green = colour.getY()/255;
float blue = colour.getZ()/255;
fwrite (&red, sizeof(float), 1, myFile);
fwrite (&green, sizeof(float), 1, myFile);
fwrite (&blue, sizeof(float), 1, myFile);
}
}
recursiveRayTrace:
Vector Scene::recursiveRayTrace(Ray r, float maxDist, int maxBounces, int bounces)
{
if(maxBounces &lt bounces)
return Vector(0,0,0);
int count = 0;
for(int i = 0; i &lt spheres.size(); i++)
{
if(spheres.at(i).onSphere(r))
{
Vector colour(ambiant.colour);
for(int j = 0; j &lt lights.size(); j++)
{
Vector intersection(r.pos + r.dir*spheres.at(i).getT(r));
Ray nRay(intersection, lights.at(i).centre + intersection*-1);
colour = colour + lights.at(i).colour;
}
return colour;
}
}
return Vector(0,0,0);
}
What i get is an sphere that is stretched in the direction of the vector from the center to the center of the circle. I'm not looking for anyone to do my homework. I am just having a really hard time debugging this on. Any hints are appreciated :) Thanks!
Edit: cam.dist is the distance from the camera to the view plane
The stretching is actually a natural consequence of perspective viewing and it is exaggerated if you have a very wide field of view. In other words moving the camera back from your image plane should make it seem more natural.

Resources