Calculating the fraction of each cell in a grid overlapped by a 2D object - python

I have an arbitrary rectangular Cartesian grid divided into potentially 10^6 or so rectangular cells. (Arbitrary means that the $x$ grid is along points $x_1,...x_n$ and the same goes for the $y$ grid.) I would like to draw an arbitrary object on top of it (say a rotated rectangle, or a circle), and efficiently calculate what fraction of each cell is overlapped by the object: if the cell is entirely inside the bounds of the object, 1.0; if the cell is entirely outside, 0.0; if half of the cell is covered by the object, 0.5. If you displayed this as an image and scaled it where 1 is black and 0 is white, the result would look like an antialiased drawing of the black object.
My application for this question is in Python, and it seems like this capability might be provided by some existing graphics library. Is there a Python module that will test for the fractional intersection of a rectangle and an arbitrary object? Is there a Python library that can at least efficiently test if a point is inside an arbitrary object like a rotated rectangle?

You could use PyCairo, which has fast native routines to do its drawing. It's antialiased by default.
Implementing the drawing algorithms in Python would be very slow.

To find the area of a trapezoid resulting from a polygon-square intersection, you can follow the process described by Sean Barrett at https://nothings.org/gamedev/rasterize/
The shapely Python library can find the area of a trapezoid and perform point-in-object tests. However, for best performance this sounds like something that you'd want to write in C/C++ and provide numpy bindings.

Related

Efficient way to draw line with specific falloff (eg blurry line)

Not completely sure what to call this problem but I will try my best to explain it here.
I have the coordinates of a line I want to draw onto a numpy array. However, I don't just want a simple line, but a thick line where I can specify the falloff (brightness with distance from the line) with a curve or mathematic function. For example, I might want to have a gaussian falloff, which would look something similar to the example below where a gaussian blur was applied to the image.
However, using blur filters does not allow the flexibility in functions I would like and does not enable precise control of the falloff (for example, when I want points on the line to have exactly value 1.0 and points further than say 10 pixels away to be 0.0).
I have attempted to solve this problem by creating the falloff pattern for a point, and then drawing that pattern into a new numpy channel for every point of the line, before merging them via the max function. This works but is too slow.
Is there a more efficient way to draw such a line from my input coordinates?
The solution I came up with is to make use of dilations. This method is more general and can be applied to any polygonal shape or binary mask.
Rasterize geometry the simple way first. For points set the corresponding pixel; for lines draw 1 pixel thick lines with library function from opencv or similar; for polygons draw the boundary or fill the polygon with opencv functions. This results in the initial mask with value 1 on the lines.
Iteratively apply dilations to this mask. This grows the mask pixel by pixel. Set the strength of the new mask according to an arbitrary falloff function.
The dilation operation is available in opencv. Alternatively, it can efficiently be implemented as a simple convolution with boolean matrices, which can then run on GPU devices.
An example of the results can be seen with the polygonal input:
Exponential falloff:
Sinusoidal falloff:

how to draw paths with bezier curves in opencv (python) created with paper.js

I have many paths associated with images, drawn with paper.js application, which produce a json object of format:
[[[x,y], [handle1_x, handle1_y], [handle2_x, handle2_y]], ...].
Thus, for each point on a polygon ("path"), I have got x, y and a notion of the handles that control the curvature ("bezier").
How can I turn these paths into contours in python for drawing these paths over an image (to get a mask)?
A contour in OpenCV is an array of points, std::vector<cv::Point>.
So I guess the easiest way would be to output the list of all points of the curve in your json file, (using Paper.js path.getPointAt() - from 0 to offset to "get all points/pixels" of the curve) and then to create a std::vector<cv::Point> in OpenCV.
This would create bigger json files though (the list of curve pixels will be bigger than the list of curve points & handles), unless you really have huge amount of data it should not be a problem. If it is a problem, you could also use something like OpenCV-Beziers (I have never used it) to compute the list of point in the OpenCV side.

Converting an AutoCAD model to a matrix of points/volumes with the mass density specified at each location

I am an experimental physicist (grad student) that is trying to take an AutoCAD model of the experiment I've built and find the gravitational potential from the whole instrument over a specified volume. Before I find the potential, I'm trying to make a map of the mass density at each point in the model.
What's important is that I already have a model and in the end I'll have a something that says "At (x,y,z) the value is d". If that's an crazy csv file, a numpy array, an excel sheet, or... whatever, I'll be happy.
Here's what I've come up with so far:
Step 1: I color code the AutoCAD file so that color associates with material.
Step 2: I send the new drawing/model to a slicer (made for 3D printing). This takes my 3D object and turns it into equally spaced (in z-direction) 2d objects... but then that's all output as g-code. But hey! G-code is a way of telling a motor how to move.
Step 3: This is the 'hard part' and the meat of this question. I'm thinking that I take that g-code, which is in essence just a set of instructions on how to move a nozzle and use it to populate a numpy array. Basically I have 3D array, each level corresponds to one position in z, and the grid left is my x-y plane. It reads what color is being put where, and follows the nozzle and puts that mass into those spots. It knows the mass because of the color. It follows the path by parsing the g-code.
When it is done with that level, it moves to the next grid and repeats.
Does this sound insane? Better yet, does it sound plausible? Or maybe someone has a smarter way of thinking about this.
Even if you just read all that, thank you. Seriously.
Does this sound insane? Better yet, does it sound plausible?
It's very reasonable and plausible. Using the g-code could do that, but it would require a g-code interpreter that could map the instructions to a 2D path. (Not 3D, since you mentioned that you're taking fixed z-slices.) That could be problematic, but, if you found one, it could work, but may require some parser manipulation. There are several of these in a variety of languages, that could be useful.
SUGGESTION
From what you describe, it's akin to doing a MRI scan of the object, and trying to determine its constituent mass profile along a given axis. In this case, and unlike MRI, you have multiple colors, so that can be used to your advantage in region selection / identification.
Even if you used a g-code interpreter, it would reproduce an image whose area you'll still have to calculate, so noting that and given that you seek to determine and classify material composition by path (in that the path defines the boundary of a particular material, which has a unique color), there may be a couple ways to approach this without resorting to g-code:
1) If the colors of your material are easily (or reasonably) distinguishable, you can create a color mask which will quantify the occupied area, from which you can then determine the mass.
That is, if you take a photograph of the slice, load the image into a numpy array, and then search for a specific value (say red), you can identify the area of the region. Then, you apply a mask on your array. Once done, you count the occupied elements within your array, and then you divide it by the array size (i.e. rows by columns), which would give you the relative area occupied. Since you know the mass of the material, and there is a constant z-thickness, this will give you the relative mass. An example of color masking using numpy alone is shown here: http://scikit-image.org/docs/dev/user_guide/numpy_images.html
As such, let's define an example that's analogous to your problem - let's say we have a picture of a red cabbage, and we want to know which how much of the picture contains red / purple-like pixels.
To simplify our life, we'll set any pixel above a certain threshold to white (RGB: 255,255,255), and then count how many non-white pixels there are:
from copy import deepcopy
import numpy as np
import matplotlib.pyplot as plt
def plot_image(fname, color=128, replacement=(255, 255, 255), plot=False):
# 128 is a reasonable guess since most of the pixels in the image that have the
# purplish hue, have RGB's above this value.
data = imread(fname)
image_data = deepcopy(data) # copy the original data (for later use if need be)
mask = image_data[:, :, 0] < color # apply the color mask over the image data
image_data[mask] = np.array(replacement) # replace the match
if plot:
plt.imshow(image_data)
plt.show()
return data, image_data
data, image_data = plot_image('cabbage.jpg') # load the image, and apply the mask
# Find the locations of all the pixels that are non-white (i.e. 255)
# This returns 3 arrays of the same size)
indices = np.where(image_data != 255)
# Now, calculate the area: in this case, ~ 62.04 %
effective_area = indices[0].size / float(data.size)
The selected region in question is shown here below:
Note that image_data contains the pixel information that has been masked, and would provide the coordinates (albeit in pixel space) of where each occupied (i.e. non-white) pixel occurs. The issue with this of course is that these are pixel coordinates and not a physical one. But, since you know the physical dimensions, extrapolating those quantities are easily done.
Furthermore, with the effective area known, and knowledge of the physical dimension, you have a good estimate of the real area occupied. To obtain better results, tweak the value of the color threshold (i.e. color). In your real-life example, since you know the color, search within a pixel range around that value (to offset noise and lighting issues).
The above method is a bit crude - but effective - and, it may be worth exploring using it in tandem with edge-detection, as that could help improve the region identification, and area selection. (Note that isn't always strictly true!) Also, color deconvolution may be useful: http://scikit-image.org/docs/dev/auto_examples/color_exposure/plot_ihc_color_separation.html#sphx-glr-auto-examples-color-exposure-plot-ihc-color-separation-py
The downside to this is that the analysis requires a high quality image, good lighting; and, most importantly, it's likely that you'll lose some of the more finer details of the edges, which would impact your masses.
2) Instead of resorting to camera work, and given that you have the AutoCAD model, you can use that and the software itself in addition to the above prescribed method.
Since you've colored each material in the model differently, you can use AutoCAD's slicing tool, and can do something similar to what the first method suggests doing physically: slicing the model, and taking pictures of the slice to expose the surface. Then, using a similar method described above of color masking / edge detection / region determination through color selection, you should obtain a much better and (arguably) very accurate result.
The downside to this, is that you're also limited by the image quality used. But, as it's software, that shouldn't be much of an issue, and you can get extremely high accuracy - close to its actual result.
The last suggestion to improve these results would be to script numerous random thin slicing of the AutoCAD model along a particular directional vector shared by every subsequent slice, exporting each exposed surface, analyzing each image in the manner described above, and then collecting those results to given you a Monte Carlo-like and statistically quantifiable determination of the mass (to correct for geometry effects due to slicing along one given axis).

Getting approximate vertices of curved-edge closed shapes (for calculating centroid and other properties)

I'm looking to draw outlines of 2D-closed irregular shapes with curved edges, and then compute both (a) the center of gravity (centroid) of the shape's area, and (b) the center of gravity of the shape's perimeter (i.e. the centroid of, e.g., a wire wrapped tightly around the outside of the shape). I have a late-beginner's level proficiency with Python and Matlab.
(a) and (b) are easy enough given a polygon's vertices: the centroid of a polygon's area is given by the equation here, and the centroid of a polygon's perimeter is just the average of all the line segments' midpoints weighted by the line segments' lengths. I've already written some functions to do this.
The trouble I'm having is getting/approximating those vertices from any of the ways I know how to draw a closed shape with curved edges. The best solution I've come up with so far is to use something like this matplotlib-based script to draw the curvy shape, and then call path.to_polygons(), which converts Path objects to polygons — but does so with surprisingly low resolution, such that the resulting approximation is quite poor (and too poor for my purposes — I'd like to compute those centroids fairly precisely).
So, I'm looking to either (i) find some way to increase the resolution of .to_polygons (about 10-fold), which would be satisfactory for my purposes, or (ii) try some new strategy entirely. One option would be to draw the shapes using something like Adobe Illustrator and then get an approximation of their vertices via some plugin or maybe an image processing toolbox (but I have no clue how to do either of those things). Another would be to draw the shape using some toolbox/library that already has built-in functions for finding the centroids of areas and perimeters (I've seen some having the former, but none with the latter). But I'm sure there are many other options out there that I haven't considered.

Check if pixels lie within ellipse area in Python

I'm trying to create true/false mask based on test whether pixels are inside ellipse area or not.
Straightforward ways were proposed here - Counting points inside an ellipse
However, I'm wondering if there's more elegant solution to this, like matplotlib.nxutils. If I want to use nxutils then I have to convert my ellipse to polygon somehow, which creates more complications and introduces additional error to calculations.
Is there any module capable of running this kind of check with ellipses?

Categories