How to remove border components in Python 2.7 using Opencv - python-2.7

I want to remove the components which are touching the border of the image.
I'm using OpenCV 2.4.10 and Python 2.7.
I have done HSV conversion and THRESHOLD_BINARY of the image, next I want to remove the components (objects) which are touching to border of the image.
It was explained in Matlab here - http://blogs.mathworks.com/steve/2007/09/04/clearing-border-components/
but I want to do in Python using OpenCV.
Please explain me the code.

There is no direct method in openCV to do that. You can write a function using the method floodFill and loop over for border pixels as seed points.
floodFill(dstImg,seed,Scalar (0));
where:
dstImg : Output with border removed.
seed : [(x,y) points] All the border co-ordinates
Scalar(0) : The color to be filled if a connected region towards a seed point is found. Hence (0) as your case is to fill it as black.
Sample:
int totalRows = srcImg.rows;
int totalCols = srcImg.cols;
int strt = 0, flg = 0;
int iRows = 0, jCols = 0;
while (iRows < srcImg.rows)
{
if (flg ==1)
totalRows = -1;
Point seed(strt,iRows);
iRows++;
floodFill(dstImg,seed,Scalar (0));
if (iRows == totalRows)
{
flg++;
iRows = 0;
strt = totalCols - 1;
}
}
Similarly do modify it for columns.
Hope It helps.

Not very elegant, but you could enclose each contour in a bounding rectangle and test whether the coordinates of this rectangle fall on or outside the boundary of the image (im)
for c in contours:
include = True
# omit this contour if it touches the edge of the image
x,y,w,h = cv2.boundingRect(c)
if x <= 1 or y <=1:
include = False
if x+w+1 >= im.shape[1] or y+h+1 >= im.shape[0]:
include = False
# draw the contour
if include == True:
cv2.drawContours(im, [c], -1, (255, 0, 255), 2)

Related

OpenCV drawCircle and draw Rectangles on the circle line

I want to draw circles with different radius and then I want to draw rectangles on this circle.
It should look like this:
]
I have tried it with the formula for the circle
y_Circle = Center_Circle.y + sqrt(pow(Radius, 2) - pow(x_Circle - Center_Circle.x, 2));
but this is just for the lower part of the circle. For the upper part I need this formula, but with a "-" after Center_Circly.y.
The Problem is, that i´m not getting the rectangles in the position like in the image above. It looks like this:
In this image I draw the rectangles on a circle with the formula above. For a better unterstanding I have drawn two circles by hand to show the problem.
You can see, that there is space between the rectangles and in the lower part there is no space between the rectangles. Is there another possibility to do that in an easier way? Maybe like this: Draw a circle with openCV, get access to the coordinates of the circle line and draw the rectangles of this circle line. But I don´t know how to get acces to the coordinates of the circle.
Here is my code-snippet:
for (int Radius = Rect_size; Radius < MaxRadius;)
{
x_Circle = MaxRadius - Radius;
circumference_half = 2 * 3.1415 * Radius / 2;
Rectangle_count = circumference_half / Rect_size;
for (int i = 0; i < Rectangle_count - 1; i++)
{
y_Circle = Center_Circle.y + sqrt(pow(Radius, 2) - pow(x_Circle - Center_Circle.x, 2));
if (y_Circle <= FRAME_Heigth && x_Circle <= FRAME_WIDTH && x_Circle >=0)
{
test = Rect(x_Circle, y_Circle, Rect_size, Rect_size);
rectangle(RectangePic, test, Scalar(0, 255, 255), 1, 8);
imshow("testee", RectangePic);
waitKey();
}
x_Circle += Rect_size;
}
Radius += Rect_size;
}
Try this script for these results:
import cv2, numpy as np, math
# Parameters for the window
hw = 789
# Parameters for the circle
circle_center = hw/2, hw/2
radius = hw/2.25
circle_thickness = 2
circle_color = (255,0,255)
# Parameters for the boxes
num_boxes = 50
box_size = 30
box_color = (0,255,0)
box_thickness = 2
# Create background image
bg = np.zeros((hw, hw, 3), np.uint8)
# Draw circle
cv2.circle(bg, tuple(np.array(circle_center, int)), int(radius), circle_color, circle_thickness)
# Time to draw some boxes!
for index in range(num_boxes):
# Compute the angle around the circle
angle = 2 * math.pi * index / num_boxes
# Compute the center of the box
x, y = circle_center[0] + math.sin(angle)*radius, circle_center[1] + math.cos(angle)*radius
# Compute the corners of the
pt1 = x-box_size/2, y-box_size/2
pt2 = x+box_size/2, y+box_size/2
# Draw Box
cv2.rectangle(bg, tuple(np.array(pt1, int)),tuple(np.array(pt2, int)), box_color, box_thickness)
cv2.imshow('img', bg)
cv2.waitKey(0)
cv2.destroyAllWindows()

how can I select object from image and replace it with other one by using c++ and opencv

enter image description here
for example if i need to select the Ipad and I need to replace it by other thing with the same size
Let's say, we want to put an Android tablet from another image over the iPad.
Knowing corner coordinates of both objects on both images, you can use OpenCV getPerspectiveTransform function to create the transformation matrix. Make an empty mask, use fillPoly to draw a quadrangle on it, corresponding to the Android corner points, fill it with 1-s, it's going to be the binary mask. Apply the perspective transform, calculated earlier, to both the mask and the android image (warpPerspective). Copy the transformed Android image over the original iPad image with copyTo function, using the transformed mask. Done.
Here's a 'proof of concept' Python implementation, just because I did something not so different not so long ago. Click all the Android corners in order, then the iPad corners in the same order, press A key to apply the transform. Don't expect miracles from it, of course - it's not going to paint missing edges for you, etc.
import cv2
import numpy as np
def on_mouse_click_from(event, x, y, flags, param):
global image_from, points_from
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(image_from, (x, y), 2, (255, 0, 0), cv2.FILLED)
points_from.append([x, y])
cv2.imshow("Image1", image_from)
def on_mouse_click_to(event, x, y, flags, param):
global image_to, points_to
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(image_to, (x, y), 2, (255, 0, 0), cv2.FILLED)
points_to.append([x, y])
cv2.imshow("Image2", image_to)
points_from = []
points_to = []
image_from = cv2.imread("android.jpg")
image_to = cv2.imread("ipad.jpg")
max_dim = [max(x, y) for x, y in zip(image_from.shape[:2], image_to.shape[:2])][::-1]
max_dim = tuple(max_dim)
image_from = cv2.resize(image_from, max_dim)
image_to = cv2.resize(image_to, max_dim)
clone_from = image_from.copy()
cv2.namedWindow("Image1")
cv2.setMouseCallback("Image1", on_mouse_click_from)
clone_to = image_to.copy()
cv2.namedWindow("Image2")
cv2.setMouseCallback("Image2", on_mouse_click_to)
image_res = None
cv2.namedWindow("Result")
while True:
cv2.imshow("Image1", image_from)
cv2.imshow("Image2", image_to)
key = cv2.waitKey(1) & 0xFF
if key == ord("r"):
image_from = clone_from.copy()
image_to = clone_to.copy()
points_from = []
points_to = []
elif key == ord("a"):
trans = cv2.getPerspectiveTransform(np.array(points_from, dtype='f4'), np.array(points_to, dtype='f4'))
height, width, n_colors = clone_from.shape
stencil = np.zeros((height, width, n_colors))
contours = [np.array(points_from)]
color = [1, 1, 1]
cv2.fillPoly(stencil, contours, color)
stencil = cv2.warpPerspective(stencil, trans, (width, height))
img_from_transformed = cv2.warpPerspective(clone_from, trans, (width, height))
cnd = (stencil != 0)
image_res = clone_to.copy()
image_res[cnd] = img_from_transformed[cnd]
cv2.imwrite("result.jpg", image_res)
cv2.imshow("Result", image_res)
elif key == ord("q"):
break
cv2.destroyAllWindows()
Follow these steps:
1) First, select the object to be replaced using Mousecallback fuction from opencv. You can find a simple demo here
2) When selecting the object, save the 4 coordinates of the object being selected. Find the size of the rectangle by using these coordinates.
3) Use Resize function to resize the image(with which you want to replace the original)
4) Now You can simply use copyTo function to do the replacement. Check here
Hope this helps!

Only drawing contours that exist over several frames to remove flickering

I've been researching here and the rest of the web for over a week now and am unable to come up with anything.
I'm coding using C++ and opencv on linux.
I have this video in black and white of a cloud chamber (http://youtu.be/40wnB8ukI7s). I want to draw contours around the moving particle tracks. Currently I'm using findContours and drawContours; however, it draws contours around all of the white pixels, including the ones that quickly appear and disappear. I don't want to draw contours around my background, the flickering white pixels.
My problem is that the background is also moving so background subtraction doesn't work. Is there a way to:
a) only draw a contour if it exists roughly in the same location over several frames
b) remove a white pixel if it doesn't exist for multiple frames (probably at least 4 or 5 frames)
Thank you for any help you can provide.
Edit: Code for comparing two frames (firstFrame and secondFrame)
Vec3b frameColour;
Vec3b frameColour2;
for (int x = 0; x < firstFrame.cols; x++){
for (int y = 0; y < firstFrame.rows; y++){
frameColour = firstFrame.at<Vec3b>(Point(x, y));
frameColour2 = secondFrame.at<Vec3b>(Point(x, y));
if(frameColour == white && frameColour2 == white){
secondFrameAfter.at<Vec3b>(Point(x, y)) = white;
}else{
secondFrameAfter.at<Vec3b>(Point(x, y)) = black;
}
}
}
You could implement your idea:
For each frame do:
For each white pixel do:
If the pixels in the neigbourhood of the last N frames are *mostly* white
Set the current pixel to white
Else
Set the current pixel to black
The neigbourhood can be defined as a 3x3 mask around the pixel.
Mostly refers to an appropriate threshold, let's say 80% of the N frames should support (be white) the pixel position.
The red pixel is the current pixel (x,y) and the green pixels are its neigbourhood.
Comparing the neigbouring pixel of a pixel (x,y) can be achieved as follows:
const int MASK_SIZE = 3;
int numberOfSupportingFrames = 0;
for(int k = 0; k < N; k++)
{
Mat currentPreviousFrame = previousFrames.at(k);
bool whitePixelAvailable = false;
for(int i = x-(MASK_SIZE/2); i < x+(MASK_SIZE/2) && !whitePixelAvailable; i++)
{
for(int j = y-(MASK_SIZE/2); j < y+(MASK_SIZE/2) && !whitePixelAvailable; j++)
{
if(currentPreviousFrame.at<Vec3b>(Point(i, j)) == white)
{
whitePixelAvailable = true;
numberOfSupportingFrames++;
}
}
}
}
if((float)numberOfSupportingFrames / (float)N > 0.8)
secondFrameAfter.at<Vec3b>(Point(x, y)) = white;
else
secondFrameAfter.at<Vec3b>(Point(x, y)) = black;
The previous frames are stored inside std::vector previousFrames.
The algorithm checks the spatio-temporal neigbourhood of the pixel (x,y). The outer loop iterates over the neigbouring frames (temporal neigbourhood), while the inner two loops iterate over the neigbouring eight pixels (spatial neighbourhood). If there is a white pixel in the current spatial neighbourhood, this previous frame supports the current pixel (x,y). At the end it is checked if there are enough frames supporting the current pixel (80% of the previous frames should contain at least on white pixel in the 8-neigbourhood).
This code should be nested inside your two for-loops with some modifications (variable names, border handling).

How to remove elongated structures(contours) from the binary image

I am trying to remove elongated contours from my binary image but I am still getting most of them. I have tried to remove them using but considering compactness and eccentricity factors but that didn't work in my case.
im=cv2.imread('thresh.png')
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
cv2.imshow('thres',gray)
gray2 = gray.copy()
mask = np.zeros(gray.shape,np.uint8)
contours, hier = cv2.findContours(gray2,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
area=cv2.contourArea(cnt)
if area>=5:
ellipse = cv2.fitEllipse(cnt)
# center, axis_length and orientation of ellipse
(center,axes,orientation) = ellipse
# length of MAJOR and minor axis
majoraxis_length = max(axes)
minoraxis_length = min(axes)
eccentricity = np.sqrt(1-(minoraxis_length/majoraxis_length)**2)
#############compactness################
area=cv2.contourArea(cnt)
equi_diameter = np.sqrt(4*area/np.pi)
compactness=equi_diameter/majoraxis_length
########################################
if(eccentricity<=0.6 or eccentricity >1) or (compactness <0.8):
cv2.drawContours(gray2,[cnt],0,(0,0,0),1)
cv2.drawContours(mask,[cnt],0,255,-1)
cv2.imshow('final',mask)
Can anyone suggest me some method for removing these elongated contours.
One option i can think of, is to calculate each object area and max length, than set a threshold to area/length.

C++/CLI Visual C++ 2010 Express - Drawing multiple ellipses

I want to draw multiple filled ellipses on/in some panel. Drawing single one isnt problem, i am using:
Color aColor = Color::FromArgb( 255, 0, 0 );
SolidBrush^ aBrush = gcnew SolidBrush(aColor);
Rectangle rect = Rectangle(x, y, 10, 10);
e->Graphics->FillEllipse(aBrush, rect);
It draws red ellipse bordered by rectangle, and fills it with red color. (assuming i will give x and y). The problem i met, is when I want to draw multiple ellipses like that, in RANDOM places. So i need to pass random x and y (using rand() % somenumber) but i am not sure, how can i pass these variables into the panel1_paint function and draw them when both numbers are randomized. Also, ofc i dont want the last ellipse to disappear when drawing new one. The only way is using global variables?
Any ideas?
Well, i tried as suggested, to use loop inside panel and i got that:
for(int i=0; i<ile_przeszkod; i++){
int x = rand() % 690; int y = rand() % 690;
Color aColor = Color::FromArgb( 255, 0, 0 );
SolidBrush^ aBrush = gcnew SolidBrush(aColor);
Rectangle rect = Rectangle(x, y, 10, 10);
e->Graphics->FillEllipse(aBrush, rect);
MessageBox::Show("x: "+x+ " y: " +y);
}
ile_przeszkod means how many of them i want to be drawn, and message box showes me what numbers it randomized so i am sure ellipses dont overlap. The problem is, after "invalidating" panel1 i see only 1 ellipse. :/ What should i do to see both of them?
all the x, y coordinates are random , so they don't depend on some other deciding procedure, So that need not to be passed to panel1_paint rather you can run a lpop and generate random number to use them as your x, y coordinates.