Something More for Research

Explorer of Research #HEMBAD

Draw graphs using OpenCV

Posted by Hemprasad Y. Badgujar on January 20, 2015


 basic graphing library to allow plotting graphs on the screen or into an image using OpenCV. This can be very useful to view the contents of a numerical array, such as during testing of an algorithm.

This “library” is just a collection of functions that can be used to simply plot a graph of an array in its own window, or to overlay graphs into existing an IplImage. This makes it both easy to use and powerful enough for more complex uses such as combining multiple graphs into one.

Showing a simple graph of an array

Here is a simple example to see a graph of a float array in a new window, by calling:

showFloatGraph("Rotation Angle", floatArray, numFloats );


The same sort of graph could be shown from a std::vector of floats or ints or even a byte array:

showFloatGraph("Rotation Angle", &floatVector[0], floatVector.size());
showIntGraph("Rotation Angle", &intVector[0], intVector.size());
showUCharGraph("Pixel Values", pixelData, numPixels);

Note that the window will only stay active for half a second by default. To make it wait until the user hits a key, add “0” as the wait time, by calling:

showIntGraph("Rotation Angle", &intVector[0], intVector.size(), 0);

Drawing multiple graphs into an IplImage

It is also possible to draw a graph into an existing image of your own, such as to overlay a graph on top of your work, or to graph multiple values in the same graph:

IplImage *graphImg = drawFloatGraph(&floatVec1[0], floatVec1.size(), NULL,
	-25,25, 400,180, "X Angle (blue is truth, green is POSIT)" );
drawFloatGraph(&floatVec2[0], floatVec2.size(), graphImg, -25,25, 400,180);
cvSaveImage("my_graph.jpg", graphImg);
cvReleaseImage(&graphImg);

Overlaying graphs onto an existing IplImage

You can also plot the graphs onto existing images, as shown here:

IplImage *bgImg = cvLoadImage("my_background_photo.jpg");
int w = bgImg->width;
int h = bgImg->height;
drawFloatGraph(floatArray, numFloats, bgImg, -25,25, w, h, "Yaw (in degrees)");
showImage(bgImg, 0, "Rotation Angle");
cvReleaseImage(&bgImg);

Here is a more complex example, where 3 graphs are drawn over a larger photo:

IplImage *dstImage = cvLoadImage("my_background_photo.jpg");
int W = 400, H = 200;
float RANGE = 25.0f;
char *name;

name = "X Angle (blue is truth, green is POSIT)";
setGraphColor(0);	// Start with a blue graph
// Set the position of the graph within the image
CvRect region = cvRect(dstImage->width-1 - W-10, 10, W+20, H+20);
cvSetImageROI(dstImage, region);
drawFloatGraph(&vecX1[0], vecX1.size(), dstImage, -RANGE,+RANGE, W,H, name);
drawFloatGraph(&vecX2[0], vecX2.size(), dstImage, -RANGE,+RANGE, W,H);

name = "Y Angle (blue is truth, green is POSIT)";
setGraphColor(0);	// Start with a blue graph
// Set the position of the graph within the image
region.y += H+20;
cvSetImageROI(dstImage, region);
drawFloatGraph(&vecY1[0], vecY1.size(), dstImage, -RANGE,+RANGE, W,H, name);
drawFloatGraph(&vecY2[0], vecY2.size(), dstImage, -RANGE,+RANGE, W,H);

name = "Z Angle (blue is truth, green is POSIT)";
setGraphColor(0);	// Start with a blue graph
// Set the position of the graph within the image
region.y += H+20;
cvSetImageROI(dstImage, region);
drawFloatGraph(&vecZ1[0], vecZ1.size(), dstImage, -RANGE,+RANGE, W,H, name);
drawFloatGraph(&vecZ2[0], vecZ2.size(), dstImage, -RANGE,+RANGE, W,H);

cvResetImageROI(dstImage);

showImage(dstImage);
cvReleaseImage(&dstImage);

 


//------------------------------------------------------------------------------
"ImageUtils.cpp",
//------------------------------------------------------------------------------

#define USE_HIGHGUI // Enable this to display graph windows using OpenCV's HighGUI. (Supports Windows, Linux & Mac, but not iPhone).

#include <stdio.h>
#include <iostream>
//#include <tchar.h>

// OpenCV
#include <cv.h>
#include <cxcore.h>
#ifdef USE_HIGHGUI
#include <highgui.h>
#endif

#ifndef UCHAR
typedef unsigned char UCHAR;
#endif

#include "GraphUtils.h"

//------------------------------------------------------------------------------
// Graphing functions
//------------------------------------------------------------------------------
const CvScalar BLACK = CV_RGB(0,0,0);
const CvScalar WHITE = CV_RGB(255,255,255);
const CvScalar GREY = CV_RGB(150,150,150);

int countGraph = 0; // Used by 'getGraphColor()'
CvScalar customGraphColor;
int usingCustomGraphColor = 0;

// Get a new color to draw graphs. Will use the latest custom color, or change between blue, green, red, dark-blue, dark-green and dark-red until a new image is created.
CvScalar getGraphColor(void)
{
if (usingCustomGraphColor) {
usingCustomGraphColor = 0;
return customGraphColor;
}

countGraph++;
switch (countGraph) {
case 1: return CV_RGB(60,60,255); // light-blue
case 2: return CV_RGB(60,255,60); // light-green
case 3: return CV_RGB(255,60,40); // light-red
case 4: return CV_RGB(0,210,210); // blue-green
case 5: return CV_RGB(180,210,0); // red-green
case 6: return CV_RGB(210,0,180); // red-blue
case 7: return CV_RGB(0,0,185); // dark-blue
case 8: return CV_RGB(0,185,0); // dark-green
case 9: return CV_RGB(185,0,0); // dark-red
default:
countGraph = 0; // start rotating through colors again.
return CV_RGB(200,200,200); // grey
}
}
// Call 'setGraphColor()' to reset the colors that will be used for graphs.
void setGraphColor(int index)
{
countGraph = index;
usingCustomGraphColor = 0; // dont use a custom color.
}
// Specify the exact color that the next graph should be drawn as.
void setCustomGraphColor(int R, int B, int G)
{
customGraphColor = CV_RGB(R, G, B);
usingCustomGraphColor = 1; // show that it will be used.
}

// Draw the graph of an array of floats into imageDst or a new image, between minV & maxV if given.
// Remember to free the newly created image if imageDst is not given.
IplImage* drawFloatGraph(const float *arraySrc, int nArrayLength, IplImage *imageDst, float minV, float maxV, int width, int height, char *graphLabel, bool showScale)
{
int w = width;
int h = height;
int b = 10; // border around graph within the image
if (w <= 20)
w = nArrayLength + b*2; // width of the image
if (h <= 20)
h = 220;

int s = h - b*2;// size of graph height
float xscale = 1.0;
if (nArrayLength > 1)
xscale = (w - b*2) / (float)(nArrayLength-1); // horizontal scale
IplImage *imageGraph; // output image

// Get the desired image to draw into.
if (!imageDst) {
// Create an RGB image for graphing the data
imageGraph = cvCreateImage(cvSize(w,h), 8, 3);

// Clear the image
cvSet(imageGraph, WHITE);
}
else {
// Draw onto the given image.
imageGraph = imageDst;
}
if (!imageGraph) {
std::cerr << "ERROR in drawFloatGraph(): Couldn't create image of " << w << " x " << h << std::endl;
exit(1);
}
CvScalar colorGraph = getGraphColor(); // use a different color each time.

// If the user didnt supply min & mav values, find them from the data, so we can draw it at full scale.
if (fabs(minV) < 0.0000001f && fabs(maxV) < 0.0000001f) {
for (int i=0; i
float v = (float)arraySrc[i];
if (v < minV)
minV = v;
if (v > maxV)
maxV = v;
}
}
float diffV = maxV - minV;
if (diffV == 0)
diffV = 0.00000001f; // Stop a divide-by-zero error
float fscale = (float)s / diffV;

// Draw the horizontal & vertical axis
int y0 = cvRound(minV*fscale);
cvLine(imageGraph, cvPoint(b,h-(b-y0)), cvPoint(w-b, h-(b-y0)), BLACK);
cvLine(imageGraph, cvPoint(b,h-(b)), cvPoint(b, h-(b+s)), BLACK);

// Write the scale of the y axis
CvFont font;
cvInitFont(&font,CV_FONT_HERSHEY_PLAIN,0.55,0.7, 0,1,CV_AA); // For OpenCV 1.1
if (showScale) {
//cvInitFont(&font,CV_FONT_HERSHEY_PLAIN,0.5,0.6, 0,1, CV_AA); // For OpenCV 2.0
CvScalar clr = GREY;
char text[16];
sprintf_s(text, sizeof(text)-1, "%.1f", maxV);
cvPutText(imageGraph, text, cvPoint(1, b+4), &font, clr);
// Write the scale of the x axis
sprintf_s(text, sizeof(text)-1, "%d", (nArrayLength-1) );
cvPutText(imageGraph, text, cvPoint(w-b+4-5*strlen(text), (h/2)+10), &font, clr);
}

// Draw the values
CvPoint ptPrev = cvPoint(b,h-(b-y0)); // Start the lines at the 1st point.
for (int i=0; i
int y = cvRound((arraySrc[i] - minV) * fscale); // Get the values at a bigger scale
int x = cvRound(i * xscale);
CvPoint ptNew = cvPoint(b+x, h-(b+y));
cvLine(imageGraph, ptPrev, ptNew, colorGraph, 1, CV_AA); // Draw a line from the previous point to the new point
ptPrev = ptNew;
}

// Write the graph label, if desired
if (graphLabel != NULL && strlen(graphLabel) > 0) {
//cvInitFont(&font,CV_FONT_HERSHEY_PLAIN, 0.5,0.7, 0,1,CV_AA);
cvPutText(imageGraph, graphLabel, cvPoint(30, 10), &font, CV_RGB(0,0,0)); // black text
}

return imageGraph;
}

// Draw the graph of an array of ints into imageDst or a new image, between minV & maxV if given.
// Remember to free the newly created image if imageDst is not given.
IplImage* drawIntGraph(const int *arraySrc, int nArrayLength, IplImage *imageDst, int minV, int maxV, int width, int height, char *graphLabel, bool showScale)
{
int w = width;
int h = height;
int b = 10; // border around graph within the image
if (w <= 20)
w = nArrayLength + b*2; // width of the image
if (h <= 20)
h = 220;

int s = h - b*2;// size of graph height
float xscale = 1.0;
if (nArrayLength > 1)
xscale = (w - b*2) / (float)(nArrayLength-1); // horizontal scale
IplImage *imageGraph; // output image

// Get the desired image to draw into.
if (!imageDst) {
// Create an RGB image for graphing the data
imageGraph = cvCreateImage(cvSize(w,h), 8, 3);

// Clear the image
cvSet(imageGraph, WHITE);
}
else {
// Draw onto the given image.
imageGraph = imageDst;
}
if (!imageGraph) {
std::cerr << "ERROR in drawIntGraph(): Couldn't create image of " << w << " x " << h << std::endl;
exit(1);
}
CvScalar colorGraph = getGraphColor(); // use a different color each time.

// If the user didnt supply min & mav values, find them from the data, so we can draw it at full scale.
if (minV == 0 && maxV == 0) {
for (int i=0; i
int v = arraySrc[i];
if (v < minV)
minV = v;
if (v > maxV)
maxV = v;
}
}
int diffV = maxV - minV;
if (diffV == 0)
diffV = 1; // Stop a divide-by-zero error
float fscale = (float)s / (float)diffV;

// Draw the horizontal & vertical axis
int y0 = cvRound(minV*fscale);
cvLine(imageGraph, cvPoint(b,h-(b-y0)), cvPoint(w-b, h-(b-y0)), BLACK);
cvLine(imageGraph, cvPoint(b,h-(b)), cvPoint(b, h-(b+s)), BLACK);

// Write the scale of the y axis
CvFont font;
cvInitFont(&font,CV_FONT_HERSHEY_PLAIN,0.55,0.7, 0,1,CV_AA); // For OpenCV 1.1
if (showScale) {
//cvInitFont(&font,CV_FONT_HERSHEY_PLAIN,0.5,0.6, 0,1, CV_AA); // For OpenCV 2.0
CvScalar clr = GREY;
char text[16];
sprintf_s(text, sizeof(text)-1, "%.1f", maxV);
cvPutText(imageGraph, text, cvPoint(1, b+4), &font, clr);
// Write the scale of the x axis
sprintf_s(text, sizeof(text)-1, "%d", (nArrayLength-1) );
cvPutText(imageGraph, text, cvPoint(w-b+4-5*strlen(text), (h/2)+10), &font, clr);
}

// Draw the values
CvPoint ptPrev = cvPoint(b,h-(b-y0)); // Start the lines at the 1st point.
for (int i=0; i
int y = cvRound((arraySrc[i] - minV) * fscale); // Get the values at a bigger scale
int x = cvRound(i * xscale);
CvPoint ptNew = cvPoint(b+x, h-(b+y));
cvLine(imageGraph, ptPrev, ptNew, colorGraph, 1, CV_AA); // Draw a line from the previous point to the new point
ptPrev = ptNew;
}

// Write the graph label, if desired
if (graphLabel != NULL && strlen(graphLabel) > 0) {
//cvInitFont(&font,CV_FONT_HERSHEY_PLAIN, 0.5,0.7, 0,1,CV_AA);
cvPutText(imageGraph, graphLabel, cvPoint(30, 10), &font, CV_RGB(0,0,0)); // black text
}

return imageGraph;
}

// Draw the graph of an array of uchars into imageDst or a new image, between minV & maxV if given..
// Remember to free the newly created image if imageDst is not given.
IplImage* drawUCharGraph(const uchar *arraySrc, int nArrayLength, IplImage *imageDst, int minV, int maxV, int width, int height, char *graphLabel, bool showScale)
{
int w = width;
int h = height;
int b = 10; // border around graph within the image
if (w <= 20)
w = nArrayLength + b*2; // width of the image
if (h <= 20)
h = 220;

int s = h - b*2;// size of graph height
float xscale = 1.0;
if (nArrayLength > 1)
xscale = (w - b*2) / (float)(nArrayLength-1); // horizontal scale
IplImage *imageGraph; // output image

// Get the desired image to draw into.
if (!imageDst) {
// Create an RGB image for graphing the data
imageGraph = cvCreateImage(cvSize(w,h), 8, 3);

// Clear the image
cvSet(imageGraph, WHITE);
}
else {
// Draw onto the given image.
imageGraph = imageDst;
}
if (!imageGraph) {
std::cerr << "ERROR in drawUCharGraph(): Couldn't create image of " << w << " x " << h << std::endl;
exit(1);
}
CvScalar colorGraph = getGraphColor(); // use a different color each time.

// If the user didnt supply min & mav values, find them from the data, so we can draw it at full scale.
if (minV == 0 && maxV == 0) {
for (int i=0; i<nArrayLength; i++) {
int v = arraySrc[i];
if (v < minV)
minV = v;
if (v > maxV)
maxV = v;
}
}
int diffV = maxV - minV;
if (diffV == 0)
diffV = 1; // Stop a divide-by-zero error
float fscale = (float)s / (float)diffV;

// Draw the horizontal & vertical axis
int y0 = cvRound(minV*fscale);
cvLine(imageGraph, cvPoint(b,h-(b-y0)), cvPoint(w-b, h-(b-y0)), BLACK);
cvLine(imageGraph, cvPoint(b,h-(b)), cvPoint(b, h-(b+s)), BLACK);

// Write the scale of the y axis
CvFont font;
cvInitFont(&font,CV_FONT_HERSHEY_PLAIN,0.55,0.7, 0,1,CV_AA); // For OpenCV 1.1
if (showScale) {
//cvInitFont(&font,CV_FONT_HERSHEY_PLAIN,0.5,0.6, 0,1, CV_AA); // For OpenCV 2.0
CvScalar clr = GREY;
char text[16];
sprintf_s(text, sizeof(text)-1, "%.1f", maxV);
cvPutText(imageGraph, text, cvPoint(1, b+4), &font, clr);
// Write the scale of the x axis
sprintf_s(text, sizeof(text)-1, "%d", (nArrayLength-1) );
cvPutText(imageGraph, text, cvPoint(w-b+4-5*strlen(text), (h/2)+10), &font, clr);
}

// Draw the values
CvPoint ptPrev = cvPoint(b,h-(b-y0)); // Start the lines at the 1st point.
for (int i=0; i<nArrayLength; i++) {
int y = cvRound((arraySrc[i] - minV) * fscale); // Get the values at a bigger scale
int x = cvRound(i * xscale);
CvPoint ptNew = cvPoint(b+x, h-(b+y));
cvLine(imageGraph, ptPrev, ptNew, colorGraph, 1, CV_AA); // Draw a line from the previous point to the new point
ptPrev = ptNew;
}

// Write the graph label, if desired
if (graphLabel != NULL && strlen(graphLabel) > 0) {
//cvInitFont(&font,CV_FONT_HERSHEY_PLAIN, 0.5,0.7, 0,1,CV_AA);
cvPutText(imageGraph, graphLabel, cvPoint(30, 10), &font, CV_RGB(0,0,0)); // black text
}

return imageGraph;
}

// Display a graph of the given float array.
// If background is provided, it will be drawn into, for combining multiple graphs using drawFloatGraph().
// Set delay_ms to 0 if you want to wait forever until a keypress, or set it to 1 if you want it to delay just 1 millisecond.
void showFloatGraph(const char *name, const float *arraySrc, int nArrayLength, int delay_ms, IplImage *background)
{
#ifdef USE_HIGHGUI
// Draw the graph
IplImage *imageGraph = drawFloatGraph(arraySrc, nArrayLength, background);

// Display the graph into a window
cvNamedWindow( name );
cvShowImage( name, imageGraph );

cvWaitKey( 10 ); // Note that cvWaitKey() is required for the OpenCV window to show!
cvWaitKey( delay_ms ); // Wait longer to make sure the user has seen the graph

cvReleaseImage(&imageGraph);
#endif
}

// Display a graph of the given int array.
// If background is provided, it will be drawn into, for combining multiple graphs using drawIntGraph().
// Set delay_ms to 0 if you want to wait forever until a keypress, or set it to 1 if you want it to delay just 1 millisecond.
void showIntGraph(const char *name, const int *arraySrc, int nArrayLength, int delay_ms, IplImage *background)
{
#ifdef USE_HIGHGUI
// Draw the graph
IplImage *imageGraph = drawIntGraph(arraySrc, nArrayLength, background);

// Display the graph into a window
cvNamedWindow( name );
cvShowImage( name, imageGraph );

cvWaitKey( 10 ); // Note that cvWaitKey() is required for the OpenCV window to show!
cvWaitKey( delay_ms ); // Wait longer to make sure the user has seen the graph

cvReleaseImage(&imageGraph);
#endif
}

// Display a graph of the given unsigned char array.
// If background is provided, it will be drawn into, for combining multiple graphs using drawUCharGraph().
// Set delay_ms to 0 if you want to wait forever until a keypress, or set it to 1 if you want it to delay just 1 millisecond.
void showUCharGraph(const char *name, const uchar *arraySrc, int nArrayLength, int delay_ms, IplImage *background)
{
#ifdef USE_HIGHGUI
// Draw the graph
IplImage *imageGraph = drawUCharGraph(arraySrc, nArrayLength, background);

// Display the graph into a window
cvNamedWindow( name );
cvShowImage( name, imageGraph );

cvWaitKey( 10 ); // Note that cvWaitKey() is required for the OpenCV window to show!
cvWaitKey( delay_ms ); // Wait longer to make sure the user has seen the graph

cvReleaseImage(&imageGraph);
#endif
}

// Simple helper function to easily view an image, with an optional pause.
void showImage(const IplImage *img, int delay_ms, char *name)
{
#ifdef USE_HIGHGUI
if (!name)
name = "Image";
cvNamedWindow(name, CV_WINDOW_AUTOSIZE);
cvShowImage(name, img);
cvWaitKey(delay_ms);
#endif
}
<pre>

Header File

//------------------------------------------------------------------------------
// Graphing functions for OpenCV. Part of "ImageUtils.h
//------------------------------------------------------------------------------

#ifndef GRAPH_UTILS_H
#define GRAPH_UTILS_H

#ifdef __cplusplus
extern "C"
{
#endif

// Allow 'bool' variables in both C and C++ code.
#ifdef __cplusplus
#else
typedef int bool;
#define true (1)
#define false (0)
#endif

#ifdef __cplusplus
#define DEFAULT(val) = val
#else
#define DEFAULT(val)
#endif

//------------------------------------------------------------------------------
// Graphing functions
//------------------------------------------------------------------------------

// Draw the graph of an array of floats into imageDst or a new image, between minV & maxV if given.
// Remember to free the newly created image if imageDst is not given.
IplImage* drawFloatGraph(const float *arraySrc, int nArrayLength, IplImage *imageDst DEFAULT(0), float minV DEFAULT(0.0), float maxV DEFAULT(0.0), int width DEFAULT(0), int height DEFAULT(0), char *graphLabel DEFAULT(0), bool showScale DEFAULT(true));

// Draw the graph of an array of ints into imageDst or a new image, between minV & maxV if given.
// Remember to free the newly created image if imageDst is not given.
IplImage* drawIntGraph(const int *arraySrc, int nArrayLength, IplImage *imageDst DEFAULT(0), int minV DEFAULT(0), int maxV DEFAULT(0), int width DEFAULT(0), int height DEFAULT(0), char *graphLabel DEFAULT(0), bool showScale DEFAULT(true));

// Draw the graph of an array of uchars into imageDst or a new image, between minV & maxV if given.
// Remember to free the newly created image if imageDst is not given.
IplImage* drawUCharGraph(const uchar *arraySrc, int nArrayLength, IplImage *imageDst DEFAULT(0), int minV DEFAULT(0), int maxV DEFAULT(0), int width DEFAULT(0), int height DEFAULT(0), char *graphLabel DEFAULT(0), bool showScale DEFAULT(true));

// Display a graph of the given float array.
// If background is provided, it will be drawn into, for combining multiple graphs using drawFloatGraph().
// Set delay_ms to 0 if you want to wait forever until a keypress, or set it to 1 if you want it to delay just 1 millisecond.
void showFloatGraph(const char *name, const float *arraySrc, int nArrayLength, int delay_ms DEFAULT(500), IplImage *background DEFAULT(0));

// Display a graph of the given int array.
// If background is provided, it will be drawn into, for combining multiple graphs using drawIntGraph().
// Set delay_ms to 0 if you want to wait forever until a keypress, or set it to 1 if you want it to delay just 1 millisecond.
void showIntGraph(const char *name, const int *arraySrc, int nArrayLength, int delay_ms DEFAULT(500), IplImage *background DEFAULT(0));

// Display a graph of the given unsigned char array.
// If background is provided, it will be drawn into, for combining multiple graphs using drawUCharGraph().
// Set delay_ms to 0 if you want to wait forever until a keypress, or set it to 1 if you want it to delay just 1 millisecond.
void showUCharGraph(const char *name, const uchar *arraySrc, int nArrayLength, int delay_ms DEFAULT(500), IplImage *background DEFAULT(0));

// Simple helper function to easily view an image, with an optional pause.
void showImage(const IplImage *img, int delay_ms DEFAULT(0), char *name DEFAULT(0));

// Call 'setGraphColor(0)' to reset the colors that will be used for graphs.
void setGraphColor(int index DEFAULT(0));
// Specify the exact color that the next graph should be drawn as.
void setCustomGraphColor(int R, int B, int G);

#if defined (__cplusplus)
}
#endif

#endif //end GRAPH_UTILS
<pre>
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Extracts from a Personal Diary

dedicated to the life of a silent girl who eventually learnt to open up

Num3ri v 2.0

I miei numeri - seconda versione

ThuyDX

Just another WordPress.com site

Algunos Intereses de Abraham Zamudio Chauca

Matematica, Linux , Programacion Serial , Programacion Paralela (CPU - GPU) , Cluster de Computadores , Software Cientifico

josephdung

thoughts...

Tech_Raj

A great WordPress.com site

Travel tips

Travel tips

Experience the real life.....!!!

Shurwaat achi honi chahiye ...

Ronzii's Blog

Just your average geek's blog

Karan Jitendra Thakkar

Everything I think. Everything I do. Right here.

VentureBeat

News About Tech, Money and Innovation

Chetan Solanki

Helpful to u, if u need it.....

ScreenCrush

Explorer of Research #HEMBAD

managedCUDA

Explorer of Research #HEMBAD

siddheshsathe

A great WordPress.com site

Ari's

This is My Space so Dont Mess With IT !!

%d bloggers like this: