Finding antenna gain curves/functions from an image

18 views (last 30 days)
Good morning
I have an image of the antenna gain (LB-159-10-C-NF) as a function of its frequency and I wonder if is there a way to find (almost) precisely the functions of the curve using MatLAB?
Thanks

Answers (2)

DGM
DGM on 11 Nov 2023
I strongly recommend manual transcription. Trying to automagically isolate the curve from the gridlines is going to be tedious, unreliable, and inaccurate.
There are some tools on the FEX for this, but I don't like any of them. They all inherit fundamental ergonomics problems from the MATLAB environment and none that I recall offer what I consider to be very basic features.
Following from the examples given in those posts:
% using the following FEX tools:
% https://www.mathworks.com/matlabcentral/fileexchange/72225-load-svg-into-your-matlab-code
% filename of manually-fit svg file
fname = 'gain.svg';
% data range from original image axis labels
xrange = [4.9 7.1];
yrange = [8 15];
% spline discretization parameter [0 1]
coarseness = 0.001;
% get plot box geometry
str = fileread(fname);
str = regexp(str,'((?<=<rect)(.*?)(?=\/>))','match');
pbx = regexp(str,'((?<=x=")(.*?)(?="))','match');
pby = regexp(str,'((?<=y=")(.*?)(?="))','match');
pbw = regexp(str,'((?<=width=")(.*?)(?="))','match');
pbh = regexp(str,'((?<=height=")(.*?)(?="))','match');
pbrect = [str2double(pbx{1}{1}) str2double(pby{1}{1}) ...
str2double(pbw{1}{1}) str2double(pbh{1}{1})];
% get coordinates representing the curve
S = loadsvg(fname,coarseness,false);
x = S{1}(:,1); % assuming the first path is the correct one
y = S{1}(:,2);
% if there are multiple paths you want to extract
% you'll need to do do the rescaling, etc for each element of S
% rescale to fit data range
x = xrange(1) + diff(xrange)*(x-pbrect(1))/pbrect(3);
y = yrange(1) + diff(yrange)*(pbrect(4) - (y-pbrect(2)))/pbrect(4);
% get rid of nonunique points
[x,idx,~] = unique(x);
y = y(idx);
% plot
plot(x,y); grid on; hold on
xlim(xrange)
ylim(yrange)
  8 Comments
DGM
DGM on 12 Nov 2023
Edited: DGM on 12 Nov 2023
Before anything, bear in mind that this isn't some widely-accepted canonical practice. This is just the way I chose to approach the problem with the tools I had. If using Illustrator or something else to create the SVG, there may be unforeseen issues. I only have Inkscape, so that's what I designed around.
Step 0: Prepare the image
If the image is rotated, skewed or subject to perspective distortion, correct it so that the plot box is rectangular and level. This answer includes an example using imwarp().
In your case, this is not necessary.
Step 1: Import the image
In Inkscape, go to File-Import and select the image file. You may position it whereever you want in the workspace by dragging it with the mouse. Since we're not printing the image, it doesn't matter if it's actually on the page area.
Step 2: Mark the plot box extents
Select the rectangle tool from the toolbox. Draw a rectangle which covers the range of known X and Y values. The rectangle's edges are being used as markers for spatial calibration. They need to correspond to known values on the X and Y axes. With that in mind, the rectangle should extend from the minimum to maximum known values on the given axes. That's not always the same as the plot box, but in this case, it is. In this other image, the last x-tick doesn't actually lie on the right-most edge of the plot box, so we'd use that instead..
  1. Select rectangle tool
  2. Drag to position rectangle with respect to plot box or axes extents
  3. Set fill/stroke properties as desired to make adjustment easier
Once you have a rectangle you can adjust its fill/stroke properties to make manipulation more visually convenient. The Fill and Stroke editor should be available in a side panel or it can be accessed via the Object menu. There are three tabs in the Fill/Stroke editor for controlling the rectangle's Fill (the face color) and Stroke (the edge color/width). I tend to prefer a semitransparent fill (see the slider positions). I also remove the stroke like so:
At this point, you can zoom in and fine-tune the alignment of the rectangle by dragging its corner handles as necessary.
Step 3: Create a path
Select the Path (Bezier) tool from the toolbox and start placing individual nodes along the curve by clicking with the mouse. Aside from the endpoints, where should the nodes be placed? That depends on what the shape looks like, but for a smooth curve like this, it should suffice to place nodes at each inflection point. In the given example, I used only 17 nodes. You may elect to add more than that, but bear in mind that more nodes means more things that need to be adjusted. When done placing nodes, right-click to finish the object.
  1. Select the Path/Bezier tool
  2. Place nodes along the curve (e.g. at inflection points)
Now you have a path object (a spline). If you have multiple traces in the plot, you will need to draw one path for each. If you want, you can edit the stroke color of the path to make it easier to see. Here, I'm just using black.
Step 4: Adjust the path
Select the path editor tool from the toolbox and use the mouse to select the path you want to edit. Since this is a smooth curve, I'm going to set all the nodes to have the same type. Use Ctrl-A to select all the object's nodes. In the path editor toolbar, select the button for Make Selected Nodes Smooth. The path should now have some curvature to it.
  1. Select path editor
  2. Select all nodes
  3. Make selected nodes smooth
If your curve has cusps, or if you want to tailor the individual node types, you can do so as needed.
You can now begin adjusting the path. Bear in mind that right now you still probably have all nodes selected, so you might need to manually click off the path to deselect them before you can select an individual node. You can use the mouse to drag individual nodes or to bend the path by grabbing the line between nodes. The handles on the tangent lines control the slope and curvature local to each node. For a simple smooth curve like this, you may find it easiest to take an incremental approach to adjustment:
  1. Adjust the node positions so that they're centered on the trace and otherwise located as desired (e.g. at the inflection points and endpoints)
  2. Grab the segments between nodes to roughly bend them into shape.
  3. Use the tangent handles to adjust the shape of the spline. The tangent handles should remain tangent to the plot trace. Don't forget to adjust the tangent handles at the endpoint nodes.
  4. Go back and refine your adjustments as necessary.
  1. Adjust node position
  2. Adjust tangent line handles
Continue making adjustments until you feel that the spline adequately matches the plot trace. If you feel that it's necessary to add or remove nodes, or if you want to change a given node's type (e.g. to add a pointy cusp), then you can use the buttons in the path editor toolbar.
When you feel that you're done, save the file as an SVG.
Step 5: Import considerations
When the given example script imports the SVG file, the only objects that it looks for are:
  • A single rectangle object
  • One or more path objects
The raster image and other objects will be ignored. If there are multiple path objects representing multiple traces, they will be read in the order that they were initially created.
The xrange and yrange parameters should correspond to the edge positions of whereever we chose to draw the rectangle object.
The coarseness parameter specifies how the spline is decimated. In this case, it's 0.001, so the spline will be discretized such that there are 1000 samples between each node. You may choose however many points you think is appropriate. When set to 1, the output x,y data contains only the node positions (i.e. a simple polyline).
Depending on your scenario, you may need to do some further postprocessing of the x,y data. For example, you may choose to truncate the data to make sure that slight inaccuracy in manual node placement didn't result in the path extending outside the expected x or y range. Also, if the graph is plotted in log-scale, you'll need to rescale things accordingly.
Conclusion
I'm probably missing something else, but that's a rough walkthrough.
DGM
DGM on 12 Nov 2023
Edited: DGM on 12 Nov 2023
For sake of comparison, this is the extraction as done via raster image processing. This will be fragile, and would likely require adjustment for every single image that needs to be processed. The end result will tend to be fairly jagged and inaccurate in unpredictable ways.
% the raster image
inpict = imread('https://www.mathworks.com/matlabcentral/answers/uploaded_files/1535920/image.png');
% box extents in data coordinates
xrange = [4.9 7.1];
yrange = [8 15];
% binarize the image
mask = im2gray(inpict) < 160; % simple thresholding
mask = bwareaopen(mask,200); % get rid of small speckles
% get plot box and grid lines by selecting only long straight lines
stlen = 100; % length of strel
pbmask = imopen(mask,ones(stlen,1)) | imopen(mask,ones(1,stlen));
% find trace
% since major grid lines are slightly colored, we can use LCHab
% chroma in an additional effort to improve trace/grid isolation
% HSV S isn't really useful here, due to S ambiguity near black
[~,A,B] = imsplit(rgb2lab(inpict));
C = hypot(A,B); % chroma
cmask = C > 25;
trmask = mask & ~(pbmask & ~cmask) & ~cmask;
% try to connect all the chunks of the trace
trmask = imclose(trmask,strel('disk',11));
% get rid of any remaining speckles by selecting the largest blob
trmask = bwareafilt(trmask,1);
% reduce the trace to a single line
% reduce the plot box as well
trmask = bwskel(trmask);
pbmask = bwskel(pbmask);
% box extents in image coordinates
[x1 x2] = bounds(find(any(pbmask,1)));
[y1 y2] = bounds(find(any(pbmask,2)));
% convert the trace to xy data
[y0 x0] = find(trmask,1); % find initial point
TB = bwtraceboundary(trmask,[y0 x0],'E'); % [y x]
x = TB(:,2);
y = TB(:,1);
% rescale the trace to data coordinates
x = xrange(1) + diff(xrange)*(x-x1)/(x2-x1);
y = yrange(1) + diff(yrange)*((y2-y1) - (y-y1))/(y2-y1);
% get rid of nonunique points
[x,idx,~] = unique(x);
y = y(idx);
% plot
plot(x,y); grid on; hold on
xlim(xrange)
ylim(yrange)
If we were to compare this result to that obtained from the SVG transcription method, we could see the amount of error.

Sign in to comment.


Image Analyst
Image Analyst on 14 Nov 2023
Here's how I'd do it starting from a PNG image.
Basically
  1. Find the chart/gridded region and erase black stuff outside there.
  2. Within the gridded region, find the vertical and horizontal grid lines and erase them.
  3. Scan the remaining white pixels column by column getting the average location of the curve (it will be several pixels thick so we need to get the average to get the centerline).
  4. Rescale everything to your known limits of x and y.
See attached m file. If you want to fill in missing x values (where the vertical gridlines were), then you could certainly interpolate them. If you want to smooth it, you could do that too with sgolayfilt. Of course, a higher resolution image to start with, and fewer gridlines, will give better results.
  1 Comment
DGM
DGM on 14 Nov 2023
Edited: DGM on 14 Nov 2023
% ...
% inaccurate guesses cause stretching and offset
% we are not trying to estimate the apparent curve extents in the original image
% we have to guess where the extrema of x and y will be in data coordinates
% _after_ grid removal, outlier removal, and row averaging
xRange = [4.9 7.1];
yRange = [10.2 13.5];
% ...
That's why I use the plot box or labeled ticks for calibration. It removes a few compounded layers of guesswork.

Sign in to comment.

Categories

Find more on Line Plots in Help Center and File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!