-
-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Render/CannyFilterRenderProcess] Added a Canny filter/edge detector …
…proc.
- Loading branch information
Showing
8 changed files
with
209 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#pragma once | ||
|
||
#ifndef RAZ_CANNYFILTERRENDERPROCESS_HPP | ||
#define RAZ_CANNYFILTERRENDERPROCESS_HPP | ||
|
||
#include "RaZ/Render/MonoPassRenderProcess.hpp" | ||
|
||
namespace Raz { | ||
|
||
/// [Canny filter/edge detector](https://en.wikipedia.org/wiki/Canny_edge_detector) render process. | ||
/// Detects the edges within an image given its pixels' gradient information. | ||
class CannyFilterRenderProcess final : public MonoPassRenderProcess { | ||
public: | ||
explicit CannyFilterRenderProcess(RenderGraph& renderGraph); | ||
|
||
void resizeBuffers(unsigned int width, unsigned int height) override; | ||
/// Sets the given gradient buffer as input. | ||
/// \param gradientBuffer Buffer containing the gradient values. Obtained from another filter such as Sobel. | ||
/// \see SobelFilterRenderProcess | ||
void setInputGradientBuffer(Texture2DPtr gradientBuffer); | ||
/// Sets the given gradient direction buffer as input. | ||
/// \param gradDirBuffer Buffer containing the gradient direction values. Obtained from another filter such as Sobel. | ||
/// \see SobelFilterRenderProcess | ||
void setInputGradientDirectionBuffer(Texture2DPtr gradDirBuffer); | ||
void setOutputBuffer(Texture2DPtr binaryBuffer); | ||
void setLowerBound(float lowerBound) const; | ||
void setUpperBound(float upperBound) const; | ||
}; | ||
|
||
} // namespace Raz | ||
|
||
#endif // RAZ_CANNYFILTERRENDERPROCESS_HPP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
in vec2 fragTexcoords; | ||
|
||
uniform sampler2D uniGradients; | ||
uniform sampler2D uniGradDirs; | ||
uniform vec2 uniInvBufferSize; | ||
uniform float uniLowerBound; | ||
uniform float uniUpperBound; | ||
|
||
layout(location = 0) out vec4 fragColor; | ||
|
||
void main() { | ||
vec3 midGrad = texture(uniGradients, fragTexcoords).rgb; | ||
vec3 gradDir = texture(uniGradDirs, fragTexcoords).rgb; | ||
|
||
vec3 rightGrad = texture(uniGradients, fragTexcoords + vec2( 1.0, 0.0) * uniInvBufferSize).rgb; | ||
vec3 leftGrad = texture(uniGradients, fragTexcoords + vec2(-1.0, 0.0) * uniInvBufferSize).rgb; | ||
vec3 downGrad = texture(uniGradients, fragTexcoords + vec2( 0.0, -1.0) * uniInvBufferSize).rgb; | ||
vec3 upGrad = texture(uniGradients, fragTexcoords + vec2( 0.0, 1.0) * uniInvBufferSize).rgb; | ||
vec3 lowerRightGrad = texture(uniGradients, fragTexcoords + vec2( 1.0, -1.0) * uniInvBufferSize).rgb; | ||
vec3 upperLeftGrad = texture(uniGradients, fragTexcoords + vec2(-1.0, 1.0) * uniInvBufferSize).rgb; | ||
vec3 lowerLeftGrad = texture(uniGradients, fragTexcoords + vec2(-1.0, -1.0) * uniInvBufferSize).rgb; | ||
vec3 upperRightGrad = texture(uniGradients, fragTexcoords + vec2( 1.0, 1.0) * uniInvBufferSize).rgb; | ||
|
||
for (int i = 0; i < 3; ++i) { | ||
// Merging the two directions' halves together; we want to check opposite directions each time, and both will be combined that way | ||
// | ||
// /--0.75--\ | ||
// / \ | ||
// / \ | ||
// 0.5 0/1 | ||
// \ / | ||
// \ / | ||
// \--0.25--/ | ||
// | ||
// 0.6 (upper-left) will become 0.1 (lower-right), 0.75 (up) will become 0.25 (down), etc | ||
if (gradDir[i] > 0.5) | ||
gradDir[i] -= 0.5; | ||
|
||
// Gradient magnitude thresholding (edge thinning) | ||
|
||
if (gradDir[i] <= 0.0625 || gradDir[i] > 0.4375) { // Right or left | ||
if (midGrad[i] < rightGrad[i] || midGrad[i] < leftGrad[i]) | ||
midGrad[i] = 0.0; | ||
} else if (gradDir[i] > 0.1875 && gradDir[i] <= 0.3125) { // Down or up | ||
if (midGrad[i] < downGrad[i] || midGrad[i] < upGrad[i]) | ||
midGrad[i] = 0.0; | ||
} else if (gradDir[i] > 0.0625 && gradDir[i] <= 0.1875) { // Lower-right or upper-left | ||
if (midGrad[i] < lowerRightGrad[i] || midGrad[i] < upperLeftGrad[i]) | ||
midGrad[i] = 0.0; | ||
} else if (gradDir[i] > 0.3125 && gradDir[i] <= 0.4375) { // Lower-left or upper-right | ||
if (midGrad[i] < lowerLeftGrad[i] || midGrad[i] < upperRightGrad[i]) | ||
midGrad[i] = 0.0; | ||
} | ||
|
||
// Double thresholding + hysteresis | ||
|
||
if (midGrad[i] <= uniLowerBound) { | ||
midGrad[i] = 0.0; | ||
} else if (midGrad[i] >= uniUpperBound) { | ||
midGrad[i] = 1.0; | ||
} else { | ||
// If the pixel is on a weak edge (between bounds), refining it with hysteresis: if any pixel around the current one is on a strong edge | ||
// (has a gradient value above the upper bound), consider it as part of the edge | ||
if (rightGrad[i] >= uniUpperBound || leftGrad[i] >= uniUpperBound | ||
|| downGrad[i] >= uniUpperBound || upGrad[i] >= uniUpperBound | ||
|| lowerRightGrad[i] >= uniUpperBound || upperLeftGrad[i] >= uniUpperBound | ||
|| lowerLeftGrad[i] >= uniUpperBound || upperRightGrad[i] >= uniUpperBound) { | ||
midGrad[i] = 1.0; | ||
} else { | ||
midGrad[i] = 0.0; | ||
} | ||
} | ||
} | ||
|
||
float colorVal = max(midGrad.x, max(midGrad.y, midGrad.z)); | ||
fragColor = vec4(vec3(colorVal), 1.0); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#include "RaZ/Render/CannyFilterRenderProcess.hpp" | ||
#include "RaZ/Render/RenderPass.hpp" | ||
|
||
#include <string_view> | ||
|
||
namespace Raz { | ||
|
||
namespace { | ||
|
||
constexpr std::string_view cannySource = { | ||
#include "canny_filter.frag.embed" | ||
}; | ||
|
||
} // namespace | ||
|
||
CannyFilterRenderProcess::CannyFilterRenderProcess(RenderGraph& renderGraph) | ||
: MonoPassRenderProcess(renderGraph, FragmentShader::loadFromSource(cannySource), "Canny filter") { | ||
setLowerBound(0.1f); | ||
setUpperBound(0.3f); | ||
} | ||
|
||
void CannyFilterRenderProcess::resizeBuffers(unsigned int width, unsigned int height) { | ||
const Vec2f invBufferSize(1.f / static_cast<float>(width), 1.f / static_cast<float>(height)); | ||
m_pass.getProgram().setAttribute(invBufferSize, "uniInvBufferSize"); | ||
m_pass.getProgram().sendAttributes(); | ||
} | ||
|
||
void CannyFilterRenderProcess::setInputGradientBuffer(Texture2DPtr gradientBuffer) { | ||
assert("Error: The input gradient buffer is invalid." && gradientBuffer != nullptr); | ||
|
||
resizeBuffers(gradientBuffer->getWidth(), gradientBuffer->getHeight()); | ||
MonoPassRenderProcess::setInputBuffer(std::move(gradientBuffer), "uniGradients"); | ||
} | ||
|
||
void CannyFilterRenderProcess::setInputGradientDirectionBuffer(Texture2DPtr gradDirBuffer) { | ||
MonoPassRenderProcess::setInputBuffer(std::move(gradDirBuffer), "uniGradDirs"); | ||
} | ||
|
||
void CannyFilterRenderProcess::setOutputBuffer(Texture2DPtr binaryBuffer) { | ||
MonoPassRenderProcess::setOutputBuffer(std::move(binaryBuffer), 0); | ||
} | ||
|
||
void CannyFilterRenderProcess::setLowerBound(float lowerBound) const { | ||
m_pass.getProgram().setAttribute(lowerBound, "uniLowerBound"); | ||
m_pass.getProgram().sendAttributes(); | ||
} | ||
|
||
void CannyFilterRenderProcess::setUpperBound(float upperBound) const { | ||
m_pass.getProgram().setAttribute(upperBound, "uniUpperBound"); | ||
m_pass.getProgram().sendAttributes(); | ||
} | ||
|
||
} // namespace Raz |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters