detecting lane lines on a binary mask - python-3.x

I have a binary mask of a road, the mask is a little irregular(sometimes even more than depicted in the image).
I have tried houghLine in OpenCV to detect boundary lines, but the boundary lines are not as expected. I tried erosion and dilation to smooth out things, but no luck. Also since the path is curved it becomes even difficult to detect boundary lines using houghLines. How can I modify the code to detect lines better?
img2=cv2.erode(img2,None,iterations=2)
img2=cv2.dilate(img2,None,iterations=2)
can=cv2.Canny(img2,150,50)
lines=cv2.HoughLinesP(can,1,np.pi/180,50,maxLineGap=50,minLineLength=10)
if(lines is not None):
for x in lines:
#print(lines[0])
#mask=np.zeros(frame2.shape,dtype=np.uint8)
#roi=lines
#cv2.fillPoly(mask,roi,(255,255,255))
#cv2.imshow(mask)
for x1,y1,x2,y2 in x:
cv2.line(frame2,(x1,y1),(x2,y2),(255,0,0),2)

You say that Hough is failing but you don't say why. Why is your output "not as expected"? In my experience, Hough Line Detection’s critical points are two: 1) The edges mask you pass to it and 2) how you filter the resulting lines. You should be fine-tuning those two steps and Hough should be enough for your problem.
I don't know what kind of problems the line detector is giving you, but suppose you are interested (as your question suggests) in other methods for lane detection. There are at least two things you could try: 1) Bird's eye transform of the road – which makes line detection much easier since all your lines are now parallel lines. And 2) Contour detection (instead of lines).
Let's examine 2 and what kind of results you can obtain. Listen, man, I offer my answer in C++, but I make notes along with it. I try to highlight the important ideas, so you can implement them in your language of choice. However, if all you want is a CTRL+C and CTRL+V solution, that's ok, but this answer won't help you.
Ok, let's start by reading the image and converting it to binary. Our goal here is to first obtain the edges. Pretty standard stuff:
//Read input image:
std::string imagePath = "C://opencvImages//lanesMask.png";
cv::Mat testImage = cv::imread( imagePath );
//Convert BGR to Gray:
cv::Mat grayImage;
cv::cvtColor( testImage, grayImage, cv::COLOR_RGB2GRAY );
//Get binary image via Otsu:
cv::Mat binaryImage;
cv::threshold( grayImage, binaryImage, 0, 255, cv::THRESH_OTSU );
Now, simply pass this image to Canny's Edge detector. The parameters are also pretty standard. As per Canny's documentation, the ratios between lower and upper thresholds are related by a factor of 3:
//Get Edges via Canny:
cv::Mat testEdges;
//Setup lower and upper thresholds for edge detection:
float lowerThreshold = 30;
float upperThreshold = 3 * lowerThreshold;
cv::Canny( binaryImage, testEdges, lowerThreshold, upperThreshold );
Your mask is pretty good; these are the edges Canny finds:
Now, here's where we are trying something different. We won't use Hough's line detection, instead, let's find the contours of the mask. Each contour is made of points. What we are looking for is actually lines, straight lines that can be fitted to these points. There's more than a method for achieving that. I propose K-means, a clustering algorithm.
The idea is that the points, as you can see, can be clustered in 4 groups: The vanishing point of the lanes (those should be 2 endpoints there) and the 2 starting points of the road. If we give K-means the points of the contour and tell it to cluster the data in 4 separate groups, we should get the means (location) of those 4 points.
Let's try it out. The first step is to find the contours in the edges mask:
//Get contours:
std::vector< std::vector<cv::Point> > contours;
std::vector< cv::Vec4i > hierarchy;
cv::findContours( testEdges, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );
K-means needs a specific data type on its input. I'll use a cv::Point2f vector to store all the contour points. Let's set up the variables used by K-means:
//Set up the data containers used by K-means:
cv::Mat centers; cv::Mat labels;
std::vector<cv::Point2f> points; //the data for clustering is stored here
Next, let's loop through the contours and store each point inside the Point2f vector, so we can further pass it to K-means. Let’s use the loop to also draw the contours and make sure we are not messing things up:
//Loop thru the found contours:
for( int i = 0; i < (int)contours.size(); i++ ){
//Set a color & draw contours:
cv::Scalar color = cv::Scalar( 0, 256, 0 );
cv::drawContours( testImage, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
//This is the current vector of points that is being processed:
std::vector<cv::Point> currentVecPoint = contours[i];
//Loop thru it and store each point as a float point inside a plain vector:
for(int k = 0; k < (int)currentVecPoint.size(); k++){
cv::Point currentPoint = currentVecPoint[k];
//Push (store) the point into the vector:
points.push_back( currentPoint );
}
}
These are the contours found:
There, now, I have the contour points in my vector. Let's pass the info on to K-means:
//Setup K-means:
int clusterCount = 4; //Number of clusters to split the set by
int attempts = 5; //Number of times the algorithm is executed using different initial labels
int flags = cv::KMEANS_PP_CENTERS;
cv::TermCriteria criteria = cv::TermCriteria( CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 10, 0.01 );
//The call to kmeans:
cv::kmeans( points, clusterCount, labels, criteria, attempts, flags, centers );
And that's all. The result of K-means is in the centers matrix. Each row of the matrix should have 2 columns, denoting a point center. In this case, the matrix is of size 4 x 2. Let's draw that info:
As expected, 4 center points, each is the mean of a cluster. Very cool, now, is this approximation enough for your application? Only you know that! You could work with those points and extend both lines, but that's a possible improvement of this result.

Related

How to draw a border outline on a group of Goldberg polyhedron faces?

I have a Goldberg polyhedron that I have procedurally generated. I would like to draw an outline effect around a group of “faces” (let's call them tiles) similar to the image below, preferably without generating two meshes, doing the scaling in the vertex shader. Can anyone help?
My assumption is to use a scaled version of the tiles to write into a stencil buffer, then redraw those tiles comparing the stencil to draw the outline (as usual for this kind of effect), but I can't come up with an elegant solution to scale the tiles.
My best idea so far is to get the center point of the neighbouring tiles (green below) for each edge vertex (blue) and move the vertex towards them weighted by how many there are, which would leave the interior ones unmodified and the exterior ones moved inward. I think this works in principle, but I would need to generate two meshes as I couldn't do scaling this way in the vertex shader (as far as I know).
If it’s relevant this is how the polyhedron is constructed. Each tile is a separate object, the surface is triangulated with a central point and there is another point at the polyhedron’s origin (also the tile object’s origin). This is just so the tiles can be scaled uniformly and protrude from the polyhedron without creating gaps or overlaps.
Thanks in advance for any help!
EDIT:
jsb's answer was a simple and elegant solution to this problem. I just wanted to add some extra information in case someone else has the same problem.
First, here is the C# code I used to calculate these UVs:
// Use duplicate vertex count (over 4)
var vertices = mesh.vertices;
var uvs = new Vector2[vertices.Length];
for(int i = 0; i < vertices.Length; i++)
{
var duplicateCount = vertices.Count(s => s == vertices[i]);
var isInterior = duplicateCount > 4;
uvs[i] = isInterior ? Vector2.zero : Vector2.one;
}
Note that this works because I have not welded any vertices in my original mesh so I can count the adjoining triangles by just looking for duplicate vertices.
You can also do it by counting triangles like this (this would work with merged vertices, at least with how Unity's mesh data is laid out):
// Use triangle count using this vertex (over 4)
var triangles = mesh.triangles;
var uvs = new Vector2[mesh.vertices.Length];
for(int i = 0; i < triangles.Length; i++)
{
var triCount = triangles.Count(s => mesh.vertices[s] == mesh.vertices[triangles[i]]);
var isInterior = triCount > 4;
uvs[i] = isInterior ? Vector2.zero : Vector2.one;
}
Now on to the following problem. In my use case I also need to generate outlines for irregular tile patterns like this:
I neglected to mention this in the original post. Jsb's answer is still valid but the above code will not work as is for this. As you can see, when we have a tile that is only connected by one edge, the connecting vertices only "share" 2 interior triangles so we get an "exterior" edge. As a solution to this I created extra vertices along the the exterior edges of the tiles like so:
I did this by calculating the half way point along the vector between the original exterior tile vertices (a + (b - a) * 0.5) and inserting a point there. But, as you can see, the simple "duplicate vertices > 4" no longer works for determining which tiles are on the exterior.
My solution was to wind the vertices in a specific order so I know that every 3rd vertex is one I inserted along the edge like this:
Vector3 a = vertex;
Vector3 b = nextVertex;
Vector3 c = (vertex + (nextVertex - vertex) * 0.5f);
Vector3 d = tileCenter;
CreateTriangle(c, d, a);
CreateTriangle(c, b, d);
Then modify the UV code to test duplicates > 2 for these vertices (every third vertex starting at 0):
// Use duplicate vertex count
var vertices = mesh.vertices;
var uvs = new Vector2[vertices.Length];
for(int i = 0; i < vertices.Length; i++)
{
var duplicateCount = vertices.Count(s => s == vertices[i]);
var isMidPoint = i % 3 == 0;
var isInterior = duplicateCount > (isMidPoint ? 2 : 4);
uvs[i] = isInterior ? Vector2.zero : Vector2.one;
}
And here is the final result:
Thanks jsb!
One option that avoids a second mesh would be texturing:
Let's say you define 1D texture coordinates on the triangle vertices like this:
When rendering the mesh, use these coordinates to look up in a 1D texture which defines the interior and border color:
Of course, instead of using a texture, you can just as well implement this behavior in a fragment shader by thresholding the texture coordinate, conceptually:
if (u > 0.9)
fragColor = white;
else
fragColor = gray;
To update the outline, you would only need upload a new set of tex coords, which are just 1 for vertices on the outline and 0 everywhere else.
Depending on whether you want the outlines to extend only into the interior of the selected region or symmetrically to both sides of the boundary, you would need to specify the tex coords either per-corner or per-vertex, respectively.

threejs - creating "cel-shading" for objects that are close by

So I'm trying to "outline" 3D objects. Standard problem, for which the answer is meant to be that you copy the mesh, color it the outline color, scale it up, and then set it to only render faces that are "pointed in the wrong direction" - for us that means setting side:THREE.BackSide in the material. Eg here https://stemkoski.github.io/Three.js/Outline.html
But see what happens for me
Here's what I'd like to make
I have a bunch of objects that are close together - they get "inside" one another's outline.
Any advice on what I should do? What I want to be seeing is everywhere on the rendered frame that these shapes touch the background or each other, there you have outline.
What do you want to happen? Is that one mesh in your example or is it a bunch of intersecting meshes. If it's a bunch of intersecting meshes do you want them to have one outline? What about other meshes? My point is you need some way to define which "groups" of meshes get a single outline if you're using multiple meshes.
For multiple meshes and one outline a common solution is to draw all the meshes in a single group to a render target to generate a silhouette, then post process the silhouette to expand it. Finally apply the silhouette to the scene. I don't know of a three.js example but the concept is explained here and there's also many references here
Another solution that might work, should be possible to move the outline shell back in Z so doesn't intersect. Either all the way back (Z = 1 in clip space) or back some settable amount. Drawing with groups so that a collection of objects in front has an outline that blocks a group behind would be harder.
For example if I take this sample that prisoner849 linked to
And change the vertexShaderChunk in OutlineEffect.js to this
var vertexShaderChunk = `
#include <fog_pars_vertex>
uniform float outlineThickness;
vec4 calculateOutline( vec4 pos, vec3 objectNormal, vec4 skinned ) {
float thickness = outlineThickness;
const float ratio = 1.0; // TODO: support outline thickness ratio for each vertex
vec4 pos2 = projectionMatrix * modelViewMatrix * vec4( skinned.xyz + objectNormal, 1.0 );
// NOTE: subtract pos2 from pos because BackSide objectNormal is negative
vec4 norm = normalize( pos - pos2 );
// ----[ added ] ----
// compute a clipspace value
vec4 pos3 = pos + norm * thickness * pos.w * ratio;
// do the perspective divide in the shader
pos3.xyz /= pos3.w;
// just return screen 2d values at the back of the clips space
return vec4(pos3.xy, 1, 1);
}
`;
It's easier to see if you remove all references to reflectionCube and set the clear color to white renderer.setClearColor( 0xFFFFFF );
Original:
After:

Merging overlapping transparent shapes in directx

This is the problem I am facing simplified:
Using directx I need to draw two(or more) exactly (in the same 2d plane) overlapping triangles. The triangles are semi transparent but the effect I want to release is that they clip to transparency of a single triangle. The picture below might depict the problem better.
Is there a way to do this?
I use this to get overlapping transparent triangles to not "accumulate". You need to create a blendstate and set it on output merge.
blendStateDescription.AlphaToCoverageEnable = false;
blendStateDescription.RenderTarget[0].IsBlendEnabled = true;
blendStateDescription.RenderTarget[0].SourceBlend = D3D11.BlendOption.SourceAlpha;
blendStateDescription.RenderTarget[0].DestinationBlend = D3D11.BlendOption.One; //
blendStateDescription.RenderTarget[0].BlendOperation = D3D11.BlendOperation.Maximum;
blendStateDescription.RenderTarget[0].SourceAlphaBlend = D3D11.BlendOption.SourceAlpha; //Zero
blendStateDescription.RenderTarget[0].DestinationAlphaBlend = D3D11.BlendOption.DestinationAlpha;
blendStateDescription.RenderTarget[0].AlphaBlendOperation = D3D11.BlendOperation.Maximum;
blendStateDescription.RenderTarget[0].RenderTargetWriteMask = D3D11.ColorWriteMaskFlags.All;
Hope this helps. Code is in C# but it works the same in C++ etc. Basically, takes the alpha of both source and destination, compares and takes the max. Which will always be the same (as long as you use the same alpha on both triangles) otherwise it will render the one with the most alpha.
edit: I've added a sample of what the blending does in my project. The roads here overlap. Overlap Sample
My pixel shader is as:
I pass the UV co-ords in a float4.
xy = uv coords.
w is the alpha value.
Pixel shader code
float4 pixelColourBlend;
pixelColourBlend = primaryTexture.Sample(textureSamplerStandard, input.uv.xy, 0);
pixelColourBlend.w = input.uv.w;
clip(pixelColourBlend.w - 0.05f);
return pixelColourBlend;
Ignore my responses, couldn't edit them...grrrr.
Enabling the depth stencil prevents this problem

How can I translate an image with subpixel accuracy?

I have a system that requires moving an image on the screen. I am currently using a png and just placing it at the desired screen coordinates.
Because of a combination of the screen resolution and the required frame rate, some frames are identical because the image has not yet moved a full pixel. Unfortunately, the resolution of the screen is not negotiable.
I have a general understanding of how sub-pixel rendering works to smooth out edges but I have been unable to find a resource (if it exists) as to how I can use shading to translate an image by less than a single pixel.
Ideally, this would be usable with any image but if it was only possible with a simple shape like a circle or a ring, that would also be acceptable.
Sub-pixel interpolation is relatively simple. Typically you apply what amounts to an all-pass filter with a constant phase shift, where the phase shift corresponds to the required sub-pixel image shift. Depending on the required image quality you might use e.g. a 5 point Lanczos or other windowed sinc function and then apply this in one or both axes depending on whether you want an X shift or a Y shift or both.
E.g. for a 0.5 pixel shift the coefficients might be [ 0.06645, 0.18965, 0.27713, 0.27713, 0.18965 ]. (Note that the coefficients are normalised, i.e. their sum is equal to 1.0.)
To generate a horizontal shift you would convolve these coefficients with the pixels from x - 2 to x + 2, e.g.
const float kCoeffs[5] = { 0.06645f, 0.18965f, 0.27713f, 0.27713f, 0.18965f };
for (y = 0; y < height; ++y) // for each row
for (x = 2; x < width - 2; ++x) // for each col (apart from 2 pixel border)
{
float p = 0.0f; // convolve pixel with Lanczos coeffs
for (dx = -2; dx <= 2; ++dx)
p += in[y][x + dx] * kCoeffs[dx + 2];
out[y][x] = p; // store interpolated pixel
}
Conceptually, the operation is very simple. First you scale up the image (using any method of interpolation, as you like), then you translate the result, and finally you subsample down to the original image size.
The scale factor depends on the precision of sub-pixel translation you want to do. If you want to translate by 0.5 degrees, you need scale up the original image by a factor of 2 then you translate the resulting image by 1 pixel; if you want to translate by 0.25 degrees, you need to scale up by a factor of 4, and so on.
Note that this implementation is not efficient because when you scale up you end up calculating pixel values that you won't actually use because they're just dropped when you subsample back to the original image size. The implementation in Paul's answer is more efficient.

Plotting RGB spectrum as 2-d color matrix?

Any suggestions on how I might go about plotting the RGB color space as a 2-D matrix? I need a theoretical description of what's going on; a code sample or pseudocode would be helpful but is not required. Thanks!
If you want to represent every color in RGB space in a 2D grid, it may be impossible to avoid discontinuities / sharp borders in the result. But some mapping techniques will look better than others.
Examples from Possiblywrong.wordpress.com post allRGB: Hilbert curves and random spanning trees:
Traverse the pixels of the image via a 2-dimensional (order 12) Hilbert curve, while at the same time traversing the RGB color cube via a 3-dimensional (order 8) Hilbert curve, assigning each pixel in turn the corresponding color
"Breadth-first traversal of random spanning tree of pixels, assigning colors in Hilbert curve order."
Also check out allrgb.com, "The objective of allRGB is simple: To create images with one pixel for every RGB color (16777216); not one color missing, and not one color twice."
If you don't want to lose any information, you will need to use three dimension. If you can lose some dimensional information, then it's easy. Just do this:
// or HSV
int [256*256][256] colorMatrix;
for (int r = 0; r < 256; r++) {
for (int r = 0; r < 256; r++) {
for (int r = 0; r < 256; r++) {
colorMatrix[256*r+g][b] = color(r, g, b);
}
}
}
There isn't really a good answer for 2D, because you really need 3 dimensions. Of course, you can project a 3D space onto 2D, but to retain a meaningful amount of information you nearly need to provide the normal 3D manipulation, so you can see the projection viewed from various different angles and such.
Here are some experiments I tried based on matching hue to angles and lightness and saturation to distances from the centre of the image. I could not find a way to avoid discontinuities, dithering and ripples at the same time (original image link):

Resources