Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Im trying to automate the processing of my shooting targets to measure my performance in the range.

I was able to process the shots, but I still have to select the center of the target by the use of locator function.

Tried using the following questions as a base Detecting Grid Lines

src = ColorSeparate[Import["http://i.imgur.com/BW61u.jpg"], "RGB"][[1]];
p = ImageDimensions[src]/2;
shots = DeleteCases[DeleteCases[Last /@ ComponentMeasurements[MaxDetect@DistanceTransform
@ColorNegate@DeleteSmallComponents@
      Binarize[
       Closing[ImageAdjust[Blur@Blur@Blur@src, {1, 1}], 2]], 
  "Centroid"], {0.5, ___}], {___, 0.5}];
pts = Graphics[{Red, PointSize[Large], Point[#]}] & /@ shots;
Show[src, pts, Graphics[Locator[Dynamic[p]]]]

Identified Shots

With the locator in place I can now extract the actual shots with respect to the center of the target.

values = (# - p)/100 & /@ shots;
sigImage = 
 DensityHistogram[values, {0.2}, ColorFunction -> "DarkRainbow", 
  Method -> {"DistributionAxes" -> True}, 
  ChartBaseStyle -> EdgeForm[Thin], 
  GridLines -> {{{0, {Red, Dashed, Thick}}}, {{0, {Red, Thick, 
   Dashed}}}}, PlotRange -> {{-2, 2}, {-2, 2}}]

Mathematica graphics

I would like for Mathematica to be able to find the center of the target automatically, so then I can create a program to process several targets which I can extract the shots automatically and save in a database, which I can then use to review the Density Histograms of different dates, weapons, distances, stances, etc.

share|improve this question
add comment

2 Answers

up vote 10 down vote accepted

Assuming that your target-paper looks always the same, this is a task for ImageCorrelate. You select only one time the center of the paper with the diamond and store this image as your kernel (you can use it with all other targets too). Then you correlate your current target with this kernel and select the position, where the minimum pixel-value is.

Lets first select the center diamond

img = Import["http://i.imgur.com/BW61u.jpg"];
kern = ImageTake[img, {445, 508}, {315, 378}];

Mathematica graphics

The rest is done in a few lines

center = Block[{data},
 data = ImageData[ImageCorrelate[img, kern, NormalizedSquaredEuclideanDistance], "Byte"];
 Rest@Reverse@First@Position[Reverse@data, Min[Flatten[data]]]
]
(* {347, 487} *)

Checking the result.

Show[img, 
 Graphics[{Green, Thick, Line[{{-100, 0} + center, {100, 0} + center}],
   Line[{{0, -100} + center, {0, 100} + center}]}]]

Mathematica graphics

share|improve this answer
add comment

Assuming that the concentric diamonds in the center enclose the target center, you can get the following best estimate of the target center. Detailed explanations and code is below the image.

Approach:

  1. I use ChanVeseBinarize targeted at Red colored objects in the image. The goal is to eventually localize the guiding markers. Making the area penalty negative results in bigger blobs (which helps fill in the gray areas, leaving the lines mostly intact unless you decrease it further) and increasing the length penalty results in shorter line segments:

    src = Import["http://i.stack.imgur.com/Lnljh.png"]
    ch = ChanVeseBinarize[src, "TargetColor" -> Red, "AreaPenalty" -> -3,
        "LengthPenalty" -> 1.5, "LevelPenalty" -> {15, 30}]
    

  2. Next, I use Erosion to erode the thin lines, while leaving the bigger blobs mostly intact, followed by deleting small components and filling in the holes:

    mark = Erosion[ch, DiamondMatrix@3] // DeleteSmallComponents // FillingTransform
    

  3. Finally, use ComponentMeasurements to get the centroid, semiaxes and perimeters of each of the above components. It's easy to pick the diamond out of the list, because its enclosing ellipse should be pretty close to a circle or in other words, almost equal semi major and minor axes.

    {centroid, semiaxes, perimeter} = First@SortBy[
        Last /@ ComponentMeasurements[mark, {"Centroid", "SemiAxes", "PerimeterLength"}], 
        Abs@Differences@#[[2]] &
    ]
    (* {{346.495, 486.644}, {87.0033, 86.6958}, 908} *)
    
  4. With these measurements in hand, you can draw a diamond and overlay with your original image to see if it is close enough:

    Show[src, Graphics[{ 
        {Opacity@0.5, Green, Rotate[Rectangle[centroid - perimeter/(8 Sqrt[2]), 
            centroid + perimeter/(8 Sqrt[2])], 45 Degree]},
        {Blue, PointSize@0.03, Point@centroid}}]]
    

    The above should produce the first image in the answer.

You can see that it is a pretty good estimate and it is slightly skewed because of the shots in the lower right quadrant, pretty close to the edge of the diamond which made it look bulkier. This won't be an issue if your shots fall either fully inside or fully outside, but one obvious way of improving this would be to use information from the other 4 guiding marks (which are free from shot marks) to get a better estimate. This can be done simply by drawing a line between the two horizontal markers' centroids and another between the two vertical markers and finding their point of intersection. I'll leave this for you to implement.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.