calculating the scores using matlab - matlab

I am working on calculating the scores for air rifle paper target. I'm able to calculate the distance from center of the image to the center of the bullet hole in Pixels.
Here's my code:
I = imread('Sample.jpg');
RGB = imresize(I,0.9);
imshow(RGB);
bw = im2bw(RGB,graythresh(getimage));
figure, imshow(bw);
bw2 = imfill(bw,'holes');
s = regionprops(bw2,'centroid');
centroids = cat(1,s.Centroid);
dist_from_center = norm(size(bw(:,:,1))/2 - centroids,2);
hold(imgca,'on');
plot(imgca,centroids(:,1),centroids(:,2),'r*');
hold(imgca,'off');
numberOfPixels = numel(I);
Number_Of_Pixel = numel(RGB);
This is the raw image with one bullet hole.
This is the result I am having.
This is the paper target I'm using to get the score.
Can any one suggest me how to calculate the score using this.

See my walk through your problem in Python
It's a very fun problem you have.
I assumed you have already a way of getting the binary holes mask (since you gave us the image)
Some scores are wrong because of target centering issues in given image
Given hole-mask, find 2D shot center
I assume that the actual images would include several holes instead of one.
Shot locations extracted by computing the local maxima of the distance transform of the binary hole image. Since the distance transform gives as intensity output the distance from the examined point to a border, this allows us to compute the centermost pixels as local maximum.
Local maximum technique I used is computing a maximum filter of your image with a given size (10 for me) and find the pixels that have filtered == original.
You have to remove the 0-valued "maxima" but apart from that it's a nice trick to remember, since it works in N-dimension by using a N-dimensional maximum filter.
Given a 2D position of shot center, compute the score
You need to transform your coordinate system from cartesian (X,Y) to polar (distance,angle).
Image from MathWorks to illustrate the math.
To use the center of image as reference point, offset each position by the image center vector.
Discarding the angle, your score is directly linked to the distance from center.
Your score is an integer that you need to compute based on distance :
As I understand you score 10 if you are at distance 0 and decrease till 0 points.
This means the scoring function is
border_space = 10 px # distance between each circle, up to you to find it :)
score = 10 - (distance / border_space) # integer division though
with the added constraint that score can not be negative :
score = max(10 - (distance / border_space),0)
Really do look through my ipython notebook, it's very visual
Edit: Regarding the distance conversion.
Your target practice image is in pixels, but these pixel distances can be mapped to millimeters : You probably know what your target's size is in centimeters (it's regulation size, right ?), so you can set up a conversion rate:
target_size_mm = 1000 # 1 meter = 1000 millimeters
target_size_px = 600 # to be measured for each image
px_to_mm_ratio = target_size_mm / target_size_px
object_size_px = 102 # any value you want to measure really
object_size_mm = object_size_px * px_to_mm_ratio
Everytime you're thinking about a facet of your problem, think "Is what I'm looking at in pixels or in millimeters ?". Try to conceptually separate the code that uses pixels from the one in millimeters.
It is coding best practice to avoid these assumptions where you can, so that if you get a bunch of images from different cameras, with different properties, you can "convert" everything to a common format (millimeters) and have a uniform treatment of data afterwards

Related

How to turn a pair of X, Y points in decimal into an image more accurately?

Normally this table is around 600 points but I didn't want to type it all, let's say I have points like this:
240.021000000000 291.414100000000
250.985100000000 297.566300000000
260.143500000000 310.125800000000
270.605100000000 315.355400000000
279.775500000000 327.352000000000
288.302300000000 335.765900000000
301.487400000000 348.374900000000
313.892100000000 340.501400000000
323.391400000000 328.044800000000
334.615100000000 322.182400000000
Where number on the left is X and number on the right is Y of a coordinate where there is a "thing" or let's say where the color is white and rest is black.
And I want to turn this into an image, what I did so far is this:
% Added 50 more pixels to not stick to edge of image
image = uint8(zeros(max(table(:, 1))+50, max(table(:, 2)+50))
for i = size(table(:))
image(round(table(i, 1)), round(table(i, 2))) = 256;
end
imshow(image);
I am wondering how accurate this is and how I can improve it or if I can improve it?
Reason here is I will do this for two tables and need to compare the similarity of two images that belong to these tables, but I don't even have an image and rounding didn't feel like the best way since 270.49999999 and 270.5000001 are similar, yet 270 and 271 are different. There can also be points that overlap each other if all is just rounded up or just rounded down.
I see two approaches, you can increase the resolution by binning your image to be N*600 by N*600 point instead of 600x600, for example 6000x6000, then each 0.1 value will be in a different pixel. Or, you can convolve your 1 pixel with a distribution like a 5x5 Gaussian of signa=1 that will capture the spread around that point position. For example using exp(- ((x-xn).^2+(x-yn).^2)/2) where xn and yn are the n-th point coordinate in your question, and x and y are obtained via [x y]=meshgrid(1:600) or whatever your image size is....

Detecting strongest points on text

I need to find text areas on a natural image.
I = rgb2gray(imread('image-name.jpg'));
points = detectHarrisFeatures(I);
imshow(I); hold on;
plot(points);
Above version of code retrieves all detected strongest points.
When I change the line that starts with "plot" like this:
[m,n] = size(points.Location);
plot(points.selectStrongest(int64((m*2)/3)));
I get the points with less noise from above but in various situations I need to reduce noisy points and the output figure was:
Input image is on the left side and Output image is on the right side
As you can see, there are still noisy points out of the rectangle(red lines) area. (rectangle lines are added byme on photoshop, output is the same without red lines)
The main question is I need a perspectively noised text regions rectangle like this (red rectangle on the image):
Desired output with rectangle
By finding this rectangle, I can afford affine process to image to correct the perspective issue and make it ready for OCR process.
The interest point density in noisy regions looks low compared to the point-density in other regions. By density, I mean the number of interest-points per unit area. Assuming this observation holds in general, it is possible to filter out the noisy regions.
I don't have matlab, so the code is in opencv.
As I mentioned in a comment, I initially thought a median filter would work, but when I tried it, it didn't. So I tried adaptive thresholding, because it is doing kind-of density calculation in my implementation and rejecting less-dense regions. Please see the comments in the code for further clarification.
/* load image as gray scale */
Mat im = imread("yEEy9.jpg", 0);
/* find interest points: using FAST here */
vector<KeyPoint> keypoints;
FAST(im, keypoints, 15);
/* mark interest points pixels with value 255 in a blank image */
Mat goodfeatures = Mat::zeros(im.rows, im.cols, CV_8U);
for (KeyPoint p: keypoints)
{
goodfeatures.at<unsigned char>(p.pt) = 255;
}
/* density filtering using adaptive thresholding:
compute a threshold for each pixel as the mean value of blocksize*blocksize neighborhood
of (x,y) minus c */
int blocksize = 15, c = 7;
Mat bw;
adaptiveThreshold(goodfeatures, bw, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, blocksize, c);
We can't detect bounding rectangle from printed text lines as lines may not cover entire page area or line detection itself may be improper as we've not yet done perspective corrections.
So i suggest eased out approach for problem:
Detect all four page edge lines which will give good estimate of page's rotation on table's plane (or camera roll). Correct image for rotation first.
I guess much correction may not be required to image for camera yaw and tilt, as one will not be shooting a page from high angles say 45 degrees and for 5 to 10 degree yaw / tilt characters will still be recognizable. Moreover difference in width of top to bottom edge and left to right edge can be used to estimate correction factor against tilt and yaw for easing out detection algorithm threashold.

Subpixel edge detection for almost vertical edges

I want to detect edges (with sub-pixel accuracy) in images like the one displayed:
The resolution would be around 600 X 1000.
I came across a comment by Mark Ransom here, which mentions about edge detection algorithms for vertical edges. I haven't come across any yet. Will it be useful in my case (since the edge isn't strictly a straight line)? It will always be a vertical edge though. I want it to be accurate till 1/100th of a pixel at least. I also want to have access to these sub-pixel co-ordinate values.
I have tried "Accurate subpixel edge location" by Agustin Trujillo-Pino. But this does not give me a continuous edge.
Are there any other algorithms available? I will be using MATLAB for this.
I have attached another similar image which the algorithm has to work on:
Any inputs will be appreciated.
Thank you.
Edit:
I was wondering if I could do this:
Apply Canny / Sobel in MATLAB and get the edges of this image (note that it won't be a continuous line). Then, somehow interpolate this Sobel edges and get the co-ordinates in subpixel. Is it possible?
A simple approach would be to project your image vertically and fit the projected profile with an appropriate function.
Here is a try, with an atan shape:
% Load image
Img = double(imread('bQsu5.png'));
% Project
x = 1:size(Img,2);
y = mean(Img,1);
% Fit
f = fit(x', y', 'a+b*atan((x0-x)/w)', 'Startpoint', [150 50 10 150])
% Display
figure
hold on
plot(x, y);
plot(f);
legend('Projected profile', 'atan fit');
And the result:
I get x_0 = 149.6 pix for your first image.
However, I doubt you will be able to achieve a subpixel accuracy of 1/100th of pixel with those images, for several reasons:
As you can see on the profile, your whites are saturated (grey levels at 255). As you cut the real atan profile, the fit is biased. If you have control over the experiments, I suggest you do it again again with a smaller exposure time for instance.
There are not so many points on the transition, so there is not so many information on where the transition is. Typically, your resolution will be the square root of the width of the atan (or whatever shape you prefer). In you case this limits the subpixel resolution at 1/5th of a pixel, at best.
Finally, your edges are not stricly vertical, they are slightly titled. If you choose to use this projection method, to increase the accuracy you should look for a way to correct this tilt before projecting. This won't increase your accuracy by several orders of magnitude, though.
Best,
There is a problem with your image. At pixel level, it seems like there are four interlaced subimages (odd and even rows and columns). Look at this zoomed area close to the edge.
In order to avoid this artifact, I just have taken the even rows and columns of your image, and compute subpixel edges. And finally, I look for the best fitting straight line, using the function clsq whose code is in this page:
%load image
url='http://i.stack.imgur.com/bQsu5.png';
image = imread(url);
imageEvenEven = image(1:2:end,1:2:end);
imshow(imageEvenEven, 'InitialMagnification', 'fit');
% subpixel detection
threshold = 25;
edges = subpixelEdges(imageEvenEven, threshold);
visEdges(edges);
% compute fit line
A = [ones(size(edges.x)) edges.x edges.y];
[c n] = clsq(A,2);
y = [1,200];
x = -(n(2)*y+c) / n(1);
hold on;
plot(x,y,'g');
When executing this code, you can see the green line that best aproximate all the edge points. The line is given by the equation c + n(1)*x + n(2)*y = 0
Take into account that this image has been scaled by 1/2 when taking only even rows and columns, so the right coordinates must be scaled.
Besides, you can try with the other tree subimages (imageEvenOdd, imageOddEven and imageOddOdd) and combine the four straigh lines to obtain the best solution.

Garment Cropping from mannequin

I have two images – mannequin with and without garment.
Please refer sample images below. Ignore the jewels, footwear on the mannequin, imagine the second mannequin has only dress.
I want to extract only the garment from the two images for further processing.
The complexity is that there is slight displacement in the position of camera when taking the two pictures. Due to this simple subtraction to generate the garment mask will not work.
Can anyone tell me how to handle it?
I think I need to do registration between the two images so that I can extract only the garment from the image?
Any references to blogs, articles and codes is highly appreciated.
--
Thanks
Idea
This is an idea of how you could do it, I haven't tested it but my gut tells me it might work. I'm assuming that there will be slight differences in the pose of the manequin as well as the camera attitude.
Let the original image be A, and the clothed image be B.
Take the difference D = |A - B|, apply a median filter that is proportional to the largest deviation you expect from pose and camera attitude error: Dmedian = Median(D, kernelsize).
Quantize Dmedian into a binary mask Dmask = Q(Dmedian, threshold) using appropriate threshold values to obtain an approximate mask for the garment (this will be smaller than the garment itself due to the median filter). Reject any shapes in Dmedian that have too small area by setting their pixels to 0.
Expand the shape(s) in Dmask proportionally to the size of the median kernel into Emask=expand(Dmask, k*kernelsize). Then construct the difference in the masks Fmask=|Dmask - Emask| which now contains areas of pixels where the garment edge is expected to be. For every pixel in Fmask which is in this area, find the correlation Cxy between A and B using a small neighbourhood, store the correlations into an image C=1.0 - Corr(A,B, Fmask, n).
Your final garment mask will be M=C+Dmask.
Explanation
Since your image has nice and continuous swatches of colour, the difference between the two similar images will be thin lines and small gradients where the pose and camera attitude is different. When taking a median filter of the difference image over a sufficiently large kernel, these lines will be removed because they are in a minority of the pixels.
The garment on the other hand will (hopefully) have a significant difference from the colors in the unclothed version. And will generate a bigger difference. Thresholding the difference after the median filter should give you a rough mask of the garment that is undersized dues to some of the pixels on the edge being rejected due to their median values being too low. You could stop here if the approximation is good enough for you.
By expanding the mask we obtained above we get a probable region for the "true" edge. The above process has served to narrow our search region for the true edge considerably and we can apply a more costly correlation search between the images along this edge to find where the garment is. High correlation means no carment and low correlation means garment.
We use the inverted correlation as an alpha value together with the initially smaller mask to obtain a alpha valued mask of the garment that can be used for extracting it.
Clarification
Expand: What I mean by "expanding the mask" is to find the contour of the mask region and outsetting/growing/enlarging it to make it larger.
Corr(A,B,Fmask,n): Is just an arbitrarily chosen correlation function that gives correlation between pixels in A and B that are selected by the mask Fmask using a region of size n. The function returns 1.0 for perfect match and 0.0 for anti-match for each pixel tested. A good function is this pseudocode:
foreach px_pos in Fmask where Fmask[px_pos] == 1
Ap = subregion(A, px_pos, size) - mean(mean(A));
Bp = subregion(B, px_pos, size) - mean(mean(B))
Cxy = sum(sum(Ap .* Bp))*sum(sum(Ap .* Bp)) / (sum(sum(Ap.*Ap))*sum(sum(Bp.*Bp)))
C[px_pos] = 1.0 - Cxy;
end
where subregion selects a region of size size around the pixel with position px_pos.
You can see that if Ap == Bp then Cxy=1

How to find the distance between the only two points in an image produced by a grating like substance?

i need to find the distance between the two points.I can find the distance between them manually by the pixel to cm converter in the image processing tool box. But i want a code which detects the point positions in the image and calculate the distance.
More accurately speaking the image contains only three points one mid and the other two approximately distanced equally from it...
There might be a better way then this, but I hacked something similar together last night.
Use bwboundaries to find the objects in the image (the contiguous regions in a black/white image).
The second returned matrix, L, is the same image but with the regions numbered. So for the first point, you want to isolate all the pixels related to it,
L2 = (L==1)
Now find the center of that region (for object 1).
x1 = (1:size(L2,2))*sum(L2,1)'/size(L2,2);
y1 = (1:size(L2,1))*sum(L2,2)/size(L2,1);
Repeat that for all the regions in your image. You should have the center of mass of each point. I think that should do it for you, but I haven't tested it.