Bounding box using MATLAB for the image - image-processing

I am trying to draw a bounding box around the white blob in the image below:
I did like this:
bw = imread('box.jpg');
bw=im2bw(bw);
imshow(bw)
L = bwlabel(bw);
s = regionprops(L, 'Area', 'BoundingBox');
s(1);
area_values = [s.Area];
idx = find((100 <= area_values) & (area_values <= 1000)); % list of all the objects
%whose area is between 100 and 1000
bw2 = ismember(L, idx); %construct a binary image containing all the objects whose
%area is between 100 and 1000 by passing L and idx to ismember.
imshow(bw2)
The output bw2, so far is:
Can someone one tell me how to draw a bounding box around this blob(white)?
Update
Wajih's answer actually accurately solved the issue.

Pseduo -
Pick largest y, largest x, smallest x, smallest y with in the blob. That is, points on the blob. These are your coordinates that you can use to build the bounding box.
assuming top left of image as (0,0)
(smallestX,smallestY)-----------------(largestX,smallestY)
| |
| |
| |
| |
(smallestX,largestY)------------------(largestX,largestY)
And for finding minimum/maximum values and indices.
[r,c]=find(img==min(min(img)))
[r,c]=find(img==max(max(img)))
r,c represent row and column in the img matrix.
I have marked the points on your image that you can use to create the bounding box.
Zoomed Image to get a better view.

As the 20,000th viewer of this question, I'll answer what I think the asker is actually asking.
To render a rectangle on the page, you just need to properly interpret the bounding box for the shape. You've already computed this in s(idx).BoundingBox
Thus, add these two lines to your script:
bb = s(idx).BoundingBox;
rectangle('Position',[bb(1) bb(2) bb(3) bb(4)],'EdgeColor','green');
and you'll get:

Have you tried regionprops from Image Toolbox?

I think you can try to use bwboundries
boundaries = bwboundaries(blob);
numberOfBoundaries = size(boundaries);
for k = 1 : numberOfBoundaries
thisBoundary = boundaries{k};
plot(thisBoundary(:,2), thisBoundary(:,1), 'g', 'LineWidth', 2);
end

Related

Placing a shape inside another shape using opencv

I have two images and I need to place the second image inside the first image. The second image can be resized, rotated or skewed such that it covers a larger area of the other images as possible. As an example, in the figure shown below, the green circle need to be placed inside the blue shape:
Here the green circle is transformed such that it covers a larger area. Another example is shown below:
Note that there may be some multiple results. However, any similar result is acceptable as shown in the above example.
How do I solve this problem?
Thanks in advance!
I tested the idea I mentioned earlier in the comments and the output is almost good. It may be better but it takes time. The final code was too much and it depends on one of my old personal projects, so I will not share. But I will explain step by step how I wrote such an algorithm. Note that I have tested the algorithm many times. Not yet 100% accurate.
for N times do this:
1. Copy from shape
2. Transform it randomly
3. Put the shape on the background
4-1. It is not acceptable if the shape exceeds the background. Go to
the first step.
4.2. Otherwise we will continue to step 5.
5. We calculate the length, width and number of shape pixels.
6. We keep a list of the best candidates and compare these three
parameters (W, H, Pixels) with the members of the list. If we
find a better item, we will save it.
I set the value of N to 5,000. The larger the number, the slower the algorithm runs, but the better the result.
You can use anything for Transform. Mirror, Rotate, Shear, Scale, Resize, etc. But I used warpPerspective for this one.
im1 = cv2.imread(sys.path[0]+'/Back.png')
im2 = cv2.imread(sys.path[0]+'/Shape.png')
bH, bW = im1.shape[:2]
sH, sW = im2.shape[:2]
# TopLeft, TopRight, BottomRight, BottomLeft of the shape
_inp = np.float32([[0, 0], [sW, 0], [sW, sH], [0, sH]])
cx = random.randint(5, sW-5)
ch = random.randint(5, sH-5)
o = 0
# Random transformed output
_out = np.float32([
[random.randint(-o, cx-1), random.randint(1-o, ch-1)],
[random.randint(cx+1, sW+o), random.randint(1-o, ch-1)],
[random.randint(cx+1, sW+o), random.randint(ch+1, sH+o)],
[random.randint(-o, cx-1), random.randint(ch+1, sH+o)]
])
# Transformed output
M = cv2.getPerspectiveTransform(_inp, _out)
t = cv2.warpPerspective(shape, M, (bH, bW))
You can use countNonZero to find the number of pixels and findContours and boundingRect to find the shape size.
def getSize(msk):
cnts, _ = cv2.findContours(msk, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnts.sort(key=lambda p: max(cv2.boundingRect(p)[2],cv2.boundingRect(p)[3]), reverse=True)
w,h=0,0
if(len(cnts)>0):
_, _, w, h = cv2.boundingRect(cnts[0])
pix = cv2.countNonZero(msk)
return pix, w, h
To find overlaping of back and shape you can do something like this:
make a mask from back and shape and use bitwise methods; Change this section according to the software you wrote. This is just an example :)
mskMix = cv2.bitwise_and(mskBack, mskShape)
mskMix = cv2.bitwise_xor(mskMix, mskShape)
isCandidate = not np.any(mskMix == 255)
For example this is not a candidate answer; This is because if you look closely at the image on the right, you will notice that the shape has exceeded the background.
I just tested the circle with 4 different backgrounds; And the results:
After 4879 Iterations:
After 1587 Iterations:
After 4621 Iterations:
After 4574 Iterations:
A few additional points. If you use a method like medianBlur to cover the noise in the Background mask and Shape mask, you may find a better solution.
I suggest you read about Evolutionary Computation, Metaheuristic and Soft Computing algorithms for better understanding of this algorithm :)

How to add an image over another image using x,y coordinates?

I want to add image 'abc.jpg' on xyz.jpg using openCV and python. I have got the coordinates x,y on which I have to add the image and also resized my 'abc.jpg' so that it will fit on the image. Now how can I add it?
To computers, images are just a grid of numbers. There are a few ways to 'add' a grid of numbers. In this answer, I will explain three ways to add image 'abc' on image 'xyz'. This is a very simple task a + b = c. But, that only works if the images are the same shape. To work with images of different shapes, only certain parts of the images should be modified using the code image[y: y+height, x: x+width].
To start, let's take a look at the sample images I created. Image xyz has vertical bars and a shape of 600,600. The bars are the color 123 (where 0 is black and 255 is white).
Next, I created another image to add on top of image xyz. This image is called image abc. It has a shape of 300,300. The horizontal bars are also the color 123:
You can 'add' the images by replacing the pixels in the xyz image with the pixels in the abc image:
x,y = 123,123
replace = xyz.copy()
replace[y: y + abc_size, x: x + abc_size] = abc
cv2.imshow('replace', replace)
You can 'add' the images by summing the arrays. This will result in an image that is brighter in places than either of the source images. Summing will produce odd results if the values go out of the range (0, 255).
x,y = 123,123
added = xyz.copy()
added[y: y + abc_size, x: x + abc_size] += abc
cv2.imshow('added', added)
If you want to average the pixels in the images you can use the cv2.addWeighted() function.
background = np.zeros_like(xyz)
x,y = 123,123
background[y: y + abc_size, x: x + abc_size] = abc
add_weighted = cv2.addWeighted(background, .5, xyz, .5, 1)
cv2.imshow('add_weighted', add_weighted)

How can I count the number of blobs in this image?

I have an image which is a result of k-means segmentation. The code to obtain it it's here:
% Read the image and convert to L*a*b* color space
I = imread('Crop.jpg');
% h = ginput(2);
% Diameter = sqrt((h(2)-h(1))^2+(h(4)-h(3))^2);
% MeanArea = 3.14*(diameter^2)/4;
Ilab = rgb2lab(I);
% Extract a* and b* channels and reshape
ab = double(Ilab(:,:,2:3));
nrows = size(ab,1);
ncols = size(ab,2);
ab = reshape(ab,nrows*ncols,2);
% Segmentation usign k-means
nColors = 4;
[cluster_idx, cluster_center] = kmeans(ab,nColors,...
'distance', 'sqEuclidean', ...
'Replicates', 3);
% Show the result
pixel_labels = reshape(cluster_idx,nrows,ncols);
figure(1);
imshow(pixel_labels,[]), title('image labeled by cluster index');
Resulting in this picture:
Now as you can see, most of the elements are connected, so I want to count all of the blobs (besides the background one), then filter them using MeanArea, area of the elements incircle. If the blob has dimensions < MeanArea I do not count it, while if the blob has dimensions > MeanArea I want to divide its area by MeanArea to obtain the number of elements. All of this is to have a measure such that #blobs = #elements. I know that it has something to do with 'bwlabel' and 'regionprops' but I don't know how to code this since I'm a beginner, any coding help is appreciated. Thanks.
EDIT: using the 'trees' approach linked in the comments I got very bad results, so I don't think it's the right method. I don't have objects with same color as the tree example, I just have same shape.
I'm following this other approach. Color segmentation by k-means
I obtained the labeled image above, but how can I save it into a variable so that I can erode it and count the number of blobs? That's my question.
EDIT2: The original picture is this one. I'm trying to detect the number of red green and blue objects.

How to classify images based on the amount of colors in the image?

I have a problem where all images have the same object; however, these objects can either have number_of_colors<=3 OR number_of_colors>3 and images are not labeled.
My attempt starts by converting RGB to LAB and Consider only the A & B to find the color coverage of that image. I was thinking of it as an area on the AB space. So for every image, I found the range of A and B (i.e max(A)-min(A), max(B)-min(B)) and multiplied them together to get the area, assuming it's a rectangle. Then I threshold using this feature.
Please let me know if intuition is correct and why it isn't working. Here is the confusion matrix:
TP: 0.41935483871, FN: 0.0645161290323
FP: 0.0967741935484, TN: 0.41935483871
Here is the basic routine the should work per image
LAB = rgb_to_lab(data_rgb[...,0],data_rgb[...,1],data_rgb[...,2])
A = LAB[1]
B = LAB[2]
minA,maxA = np.min(A),np.max(A)
minB,maxB = np.min(B),np.max(B)
diff_A = maxA - minA
diff_B = maxB - minB
area = diff_A * diff_B
# p is the normalized area
p = area/(200*200.)
# Found 0.53 to be the best possible threshold
if p >0.53:
print 'class 1 - colors > 3'
else:
print 'class 2 - colors <= 3'
Edit 1:
Here is an image to show how the threshold separates positive and negative images
Edit 2:
This shows the same plot but only considering A and B values with luminance between 16 and 70 which seems to increase the area of separation reduces the FP by 1.

Extracting image of words from a scanned paper

I want get a small image of every word in a lot of scanned books (that is in Persian (Arabic-script)).
I have no experiment in image prossessing.
How can I do that in most efficient way?
I suggest you write a script in MATLAB something like this.
a : half of the maximum distance between the letters.(in pixels)
b : half of the minimum distance between the words.(in pixels)
(lets hope a < b )
Threshold the scanned image of the page.
I(I < Th) = 0;I(I > Th) = 1;
Choose 'Th' by experimenting. You should get a binary image 'I' having 1's where letters are.
Dilate the image.
imdilate(I,a);
This will connect the letters together.
Remove noise.
I = bwareaopen(I,n);
this will remove all connected components with less that n pixels.
Do connected component analysis.
CC = bwconncomp(I);
Rect = regionprops(I,'BoundingBox');
This will return a list of co-ordinates of a rectangle containing a single word.
Extract the sub-matrix from original copy and write the image using imwrite().

Resources