So, here is my situation. I have created a object detection program which is based on color object detection. My program detects the color red and it works perfectly. But here is the problems i am facing:-
Whenever there are more than one red object in the surrounding, my program detects them and it cannot really track one object at that time(i.e it tracks other red objects of various sizes in the background. It shows me the error that "too much noise in the background". As you can see in the "threshold image" attached, it detects the round object (which is my tracking object) and my cap which is red in color. I want my program to detect only my tracking object("which is a round shaped coke cap"). How can i achieve that? Please help me out. I have my engineering design contest in few days and i have to demo my program infront of my lecturers. My program should only be able to detect and track the object which i want. Thanks
My code for the objectdetection program is a little long. So, i am hereby explaining the code as follows- I captured a frame from the webcam frame-converted it to HSV- used HSV Inrange filter to filter out the other colors but red- applied morphological operations on the filtered image. This all goes in my main function
I am using a frame resolution of 1280*720 for my webcam frame. It kind of slows down my program but it was a trade off which i had to do for performing gesture controlled operations. Anyways here is my drawobjectfunction and trackfilteredobjectfunction.
int H_MIN = 0;
int H_MAX = 256;
int S_MIN = 0;
int S_MAX = 256;
int V_MIN = 0;
int V_MAX = 256;
//default capture width and height
const int FRAME_WIDTH = 1280;
const int FRAME_HEIGHT = 720;
//max number of objects to be detected in frame
const int MAX_NUM_OBJECTS=50;
//minimum and maximum object area
const int MIN_OBJECT_AREA = 20*20;
const int MAX_OBJECT_AREA = FRAME_HEIGHT*FRAME_WIDTH/1.5;
void drawObject(int x, int y,Mat &frame){
circle(frame,Point(x,y),20,Scalar(0,255,0),2);
if(y-25>0)
line(frame,Point(x,y),Point(x,y-25),Scalar(0,255,0),2);
else line(frame,Point(x,y),Point(x,0),Scalar(0,255,0),2);
if(y+25<FRAME_HEIGHT)
line(frame,Point(x,y),Point(x,y+25),Scalar(0,255,0),2);
else line(frame,Point(x,y),Point(x,FRAME_HEIGHT),Scalar(0,255,0),2);
if(x-25>0)
line(frame,Point(x,y),Point(x-25,y),Scalar(0,255,0),2);
else line(frame,Point(x,y),Point(0,y),Scalar(0,255,0),2);
if(x+25<FRAME_WIDTH)
line(frame,Point(x,y),Point(x+25,y),Scalar(0,255,0),2);
else line(frame,Point(x,y),Point(FRAME_WIDTH,y),Scalar(0,255,0),2);
putText(frame,intToString(x)+","+intToString(y),Point(x,y+30),1,1,Scalar(0,255,0),2);
}
void trackFilteredObject(int &x, int &y, Mat threshold, Mat &cameraFeed){
Mat temp;
threshold.copyTo(temp);
//these two vectors needed for output of findContours
vector< vector<Point> > contours;
vector<Vec4i> hierarchy;
//find contours of filtered image using openCV findContours function
findContours(temp,contours,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE );
//use moments method to find our filtered object
double refArea = 0;
bool objectFound = false;
if (hierarchy.size() > 0) {
int numObjects = hierarchy.size();
//if number of objects greater than MAX_NUM_OBJECTS we have a noisy filter
if(numObjects<MAX_NUM_OBJECTS){
for (int index = 0; index >= 0; index = hierarchy[index][0]) {
Moments moment = moments((cv::Mat)contours[index]);
double area = moment.m00;
//if the area is less than 20 px by 20px then it is probably just noise
//if the area is the same as the 3/2 of the image size, probably just a bad filter
//we only want the object with the largest area so we safe a reference area each
//iteration and compare it to the area in the next iteration.
if(area>MIN_OBJECT_AREA && area<MAX_OBJECT_AREA && area>refArea){
x = moment.m10/area;
y = moment.m01/area;
objectFound = true;
refArea = area;
}else objectFound = false;
}
//let user know you found an object
if(objectFound ==true){
putText(cameraFeed,"Tracking Object",Point(0,50),2,1,Scalar(0,255,0),2);
//draw object location on screen
drawObject(x,y,cameraFeed);}
}else putText(cameraFeed,"TOO MUCH NOISE! ADJUST FILTER",Point(0,50),1,2,Scalar(0,0,255),2);
}
}
Here is the link of the image; as you can see it also detects the red hat in the background along with the red cap of the coke bottle.
My observations:- Here is what i think, to achieve my desired goal of not detecting objects of unknown sizes of red color. I think i have to edit the value of maximum object area which i declared in the above program as (const int MAX_OBJECT_AREA = FRAME_HEIGHT*FRAME_WIDTH/1.5;). I think i have to change this value, that might eliminate the detection of bigger continous red pictures. But also, there is another problem some objects are not completely red in color and they have patches of red and other colors. So, if the detected area is within the range specfied in my program then my program detects those red patches too. What i mean to say is i was wearing a tshirt which has mixed colors and when i tested my program by wearing that tshirt, my program was able to detect the red color out of the other colors. Now, how do i solve this issue?
I think you can try out the following procedure:
obtain a circular kernel having roughly the same area as your object of interest. You can do it like: Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(d, d));
where d is the diameter of the disk.
perform normalized-cross-correlation or convolution of the filtered regions image with this kernel (I think normalized-cross-correlation would be better. And add an empty boarder around the kernel).
the peak of the resulting image should give you the location of the circular region in your filtered image (if you are using normalized-cross-correlation, you'll have to add the shift).
To speed things up, you can perform this at a reduced resolution.
You can filter out non-circular shapes by detecting circles in your thresholded image. OpenCV provides a built-on method to detect circles using Hough transform, more info here. You can take advantage of this function to retain only circles that have a radius in a given range.
Another possibility is to implement connected component labeling (CCL) into your demo program.
I believe that it was removed at some point in verions 2.x of OpenCV, but a basic implementation of the two-pass version is straightforward from the Wikipedia page.
CCL will assign a unique ID for each object after thresholding. You then have to implement matching between the objects at frame (T-1) and objects in frame (T) (for example based on some nearest distance criterion) and possibly trajectory filtering or smoothing, but this would definitely give you some extra-points.
I'm making a sprite editor using JavaFX for use on desktops.
I'm attempting to implement zooming functionality, but I've run into a problem: I can't figure out how to disable image smoothing on a Canvas object.
I'm calling Canvas.setScaleX() and Canvas.setScaleY() as per every tutorial implementing Canvas zooming. But my image appears blurred when zoomed in.
I have some test code here to demonstrate.
As this is a sprite editor, it's important for me to have crisp edges to work with. The alternative to fixing image smoothing on the Canvas is to have a non-smoothing ImageView, and have a hidden Canvas to draw on, which I would rather avoid.
Help is appreciated.
(here's a link to a related question, but doesn't address my particular problem)
I was having the same issue with the blurring.
In my case, my computer has Retina Display. Retina Display causes a pixel to be rendered with sub-pixels. When drawing images to the canvas, the image would be drawn with antialiasing for the sub-pixels. I have not found a way to prevent this antialiasing from occurring (although it is possible with other canvas technologies such as HTML5's Canvas)
In the meantime, I have a work-around (albeit I'm concerned about performance):
public class ImageRenderer {
public void render(GraphicsContext context, Image image, int sx, int sy, int sw, int sh, int tx, int ty) {
PixelReader reader = image.getPixelReader();
PixelWriter writer = context.getPixelWriter();
for (int x = 0; x < sw; x++) {
for (int y = 0; y < sh; y++) {
Color color = reader.getColor(sx + x, sy + y);
if (color.isOpaque()) {
writer.setColor(tx + x, ty + y, color);
}
}
}
}
}
The PixelWriter bypasses the anti-aliasing that occurs when drawing the image.
I'm currently working on a top down game using MonoGame that uses tiles to indicate whether a position is walkable or not. Tiles have the size of 32x32 (which the images also have)
A grid of 200 x 200 is being made filled with wall tiles (and a random generator is supposed to create a path and rooms) but when I draw all the tiles on the screen a lot of tiles go missing. Below is an image where after position (x81 y183) the tiles are simply not drawn?
http://puu.sh/3JOUO.png
The code used to fill the array puts a wall tile on the grid and the position of the tile is it's array position multiplied by the tile size (32x32) the parent is used for the camera position
public override void Fill(IResourceContainer resourceContainer)
{
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
{
objectGrid[i, j] = new Wall(resourceContainer);
objectGrid[i, j].Parent = this;
objectGrid[i, j].Position = new Vector2(i * TileWidth, j * TileHeight);
}
}
When drawing I just loop through all tiles and draw them accordingly. This is what happends in the Game.Draw function
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Yellow);
// TODO: Add your drawing code here
spriteBatch.Begin();
map.Draw(gameTime, spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
The map.draw function calls this function which basically draws each tile. I tried putting a counter on how much times the draw call for each tile was hit and every update the draw function is called 40000 times which is the amount of tiles I use. So it draws them all but I still don't see them all on the screen
public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
{
if (objectGrid[i, j] != null)
{
objectGrid[i, j].Draw(gameTime, spriteBatch);
}
}
}
This is the code for drawing a tile. Where the current image is 0 at all times and the GlobalPosition is the position of a tile minus the camera position.
public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
if (visible)
spriteBatch.Draw(textures[currentImage], GlobalPosition, null, color, 0f, -Center, 1f, SpriteEffects.None, 0f);
}
My apologies for the wall of code. It all looks very simple to me yet I can't seem to find out why it is not drawing all of the tiles. For the not drawn tiles visible is still true and currentImage is 0 which it should be
The monogame spritebatch has still some bugs and errors when drawing a large number of 16-bit images. In my case around 200.000 this is not something you can easily solve. If you encounter the same problem make sure that every image you draw is on the screen and you will probably have no problems from this anymore.
The issue of programmatically drawing lines using XNA has been covered here. However, I want to allow a user to draw on a canvas as one would with a drawing app such as MS Paint.
This of course requires each x and/or y coordinate change in the mouse pointer position to result in another "dot" of the line being drawn on the canvas in the crayon color in real time.
In the mouse move event, what XNA API considerations come into play in order to draw the line point by point? Literally, of course, I'm not drawing a line as such, but rather a sequence of "dots". Each "dot" can, and probably should, be larger than a single pixel. Think of drawing with a felt tip pen.
The article you provided suggests a method of drawing lines with primitives; vector graphics, in other words. Applications like Paint are mostly pixel based (even though more advanced software like Photoshop has vector and rasterization features).
Bitmap editor
Since you want it to be "Paint-like" I would definitely go with the pixel based approach:
Create a grid of color values. (Extend the System.Drawing.Bitmap class or implement your own.)
Start the (game) loop:
Process input and update the color values in the grid accordingly.
Convert the Bitmap to a Texture2D.
Use a sprite batch or custom renderer to draw the texture to the screen.
Save the bitmap, if you want.
Drawing on the bitmap
I added a rough draft of the image class I am using here at the bottom of the answer. But the code should be quite self-explanatory anyways.
As mentioned before you also need to implement a method for converting the image to a Texture2D and draw it to the screen.
First we create a new 10x10 image and set all pixels to white.
var image = new Grid<Color>(10, 10);
image.Initilaize(() => Color.White);
Next we set up a brush. A brush is in essence just a function that is applied on the whole image. In this case the function should set all pixels inside the specified circle to a dark red color.
// Create a circular brush
float brushRadius = 2.5f;
int brushX = 4;
int brushY = 4;
Color brushColor = new Color(0.5f, 0, 0, 1); // dark red
Now we apply the brush. See this SO answer of mine on how to identify the pixels inside a circle.
You can use mouse input for the brush offsets and enable the user to actually draw on the bitmap.
double radiusSquared = brushRadius * brushRadius;
image.Modify((x, y, oldColor) =>
{
// Use the circle equation
int deltaX = x - brushX;
int deltaY = y - brushY;
double distanceSquared = Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2);
// Current pixel lies inside the circle
if (distanceSquared <= radiusSquared)
{
return brushColor;
}
return oldColor;
});
You could also interpolate between the brush color and the old pixel. For example, you can implement a "soft" brush by letting the blend amount depend on the distance between the brush center and the current pixel.
Drawing a line
In order to draw a freehand line simply apply the brush repeatedly, each time with a different offset (depending on the mouse movement):
Custom image class
I obviously skipped some necessary properties, methods and data validation, but you get the idea:
public class Image
{
public Color[,] Pixels { get; private set; }
public Image(int width, int height)
{
Pixels= new Color[width, height];
}
public void Initialize(Func<Color> createColor)
{
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
Pixels[x, y] = createColor();
}
}
}
public void Modify(Func<int, int, Color, Color> modifyColor)
{
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
Color current = Pixels[x, y];
Pixels[x, y] = modifyColor(x, y, current);
}
}
}
}
I am doing project in Image processing using opencv library. I want to track the motion of ball such that next frame hi-lights the position of ball in all the previous frames.
you probably want to hold your history in a container, std::deque is a possible solution:
std::deque<Rect> history;
const int HISTORY_SIZE = 10;
To draw the history, simply iterate through it inside your running loop:
Rect curr = yourTrackingAlgorithm();
history.push_front(curr);
for (int i = 0; i < history.size(); i++)
{
drawRectangle(history[i]);
}
if (history.size() > HISTORY_SIZE)
{
history.pop_back();
}