MythTV master
CannyEdgeDetector.cpp
Go to the documentation of this file.
1// C++ headers
2#include <cmath>
3
4// MythTV headers
7#include "libmythtv/mythframe.h" // VideoFrame
9
10// Commercial Flagging headers
11#include "CannyEdgeDetector.h"
12#include "pgm.h"
13
14extern "C" {
15#include "libavutil/imgutils.h"
16}
17
18using namespace edgeDetector;
19
21{
22 /*
23 * In general, the Gaussian mask is truncated at a point where values cease
24 * to make any meaningful contribution. The sigma=>truncation computation
25 * is best done by table lookup (which I don't have here). For sigma=0.5,
26 * the magic truncation value is 4.
27 */
28 const int TRUNCATION = 4;
29 const double sigma = 0.5;
30 const double TWO_SIGMA2 = 2 * sigma * sigma;
31
32 /* The SGM computations require that mask_radius >= 2. */
33 m_maskRadius = std::max(2, (int)std::round(TRUNCATION * sigma));
34 int mask_width = (2 * m_maskRadius) + 1;
35
36 /* Compute Gaussian mask. */
37 m_mask = new double[mask_width];
38 double val = 1.0; /* Initialize center of Gaussian mask (rr=0 => exp(0)). */
39 m_mask[m_maskRadius] = val;
40 double sum = val;
41 for (int rr = 1; rr <= m_maskRadius; rr++)
42 {
43 val = exp(-(rr * rr) / TWO_SIGMA2); // Gaussian weight(rr,sigma)
44 m_mask[m_maskRadius + rr] = val;
45 m_mask[m_maskRadius - rr] = val;
46 sum += 2 * val;
47 }
48 for (int ii = 0; ii < mask_width; ii++)
49 m_mask[ii] /= sum; /* normalize to [0,1] */
50}
51
53{
54 av_freep(reinterpret_cast<void*>(&m_edges.data[0]));
55 av_freep(reinterpret_cast<void*>(&m_convolved.data[0]));
56 av_freep(reinterpret_cast<void*>(&m_s2.data[0]));
57 av_freep(reinterpret_cast<void*>(&m_s1.data[0]));
58 delete []m_sgmSorted;
59 delete []m_sgm;
60 delete []m_mask;
61}
62
63int
64CannyEdgeDetector::resetBuffers(int newwidth, int newheight)
65{
66 if (m_sgm) {
67 /*
68 * Sentinel value to determine whether or not stuff has already been
69 * allocated.
70 */
71 if (m_ewidth == newwidth && m_eheight == newheight)
72 return 0;
73 av_freep(reinterpret_cast<void*>(&m_s1.data[0]));
74 av_freep(reinterpret_cast<void*>(&m_s2.data[0]));
75 av_freep(reinterpret_cast<void*>(&m_convolved.data[0]));
76 av_freep(reinterpret_cast<void*>(&m_edges.data[0]));
77 delete []m_sgm;
78 delete []m_sgmSorted;
79 m_sgm = nullptr;
80 }
81
82 const int padded_width = newwidth + (2 * m_maskRadius);
83 const int padded_height = newheight + (2 * m_maskRadius);
84
85 // Automatically clean up allocations at function exit
86 auto cleanup_fn = [&](CannyEdgeDetector * /*x*/) {
87 if (m_convolved.data[0])
88 av_freep(reinterpret_cast<void*>(&m_convolved.data[0]));
89 if (m_s2.data[0])
90 av_freep(reinterpret_cast<void*>(&m_s2.data[0]));
91 if (m_s1.data[0])
92 av_freep(reinterpret_cast<void*>(&m_s1.data[0]));
93 };
94 std::unique_ptr<CannyEdgeDetector, decltype(cleanup_fn)> cleanup { this, cleanup_fn };
95
96 if (av_image_alloc(m_s1.data, m_s1.linesize,
97 padded_width, padded_height, AV_PIX_FMT_GRAY8, IMAGE_ALIGN) < 0)
98 {
99 LOG(VB_COMMFLAG, LOG_ERR, "CannyEdgeDetector::resetBuffers "
100 "av_image_alloc s1 failed");
101 return -1;
102 }
103
104 if (av_image_alloc(m_s2.data, m_s2.linesize,
105 padded_width, padded_height, AV_PIX_FMT_GRAY8, IMAGE_ALIGN) < 0)
106 {
107 LOG(VB_COMMFLAG, LOG_ERR, "CannyEdgeDetector::resetBuffers "
108 "av_image_alloc s2 failed");
109 return -1;
110 }
111
112 if (av_image_alloc(m_convolved.data, m_convolved.linesize,
113 padded_width, padded_height, AV_PIX_FMT_GRAY8, IMAGE_ALIGN) < 0)
114 {
115 LOG(VB_COMMFLAG, LOG_ERR, "CannyEdgeDetector::resetBuffers "
116 "av_image_alloc convolved failed");
117 return -1;
118 }
119
120 if (av_image_alloc(m_edges.data, m_edges.linesize,
121 newwidth, newheight, AV_PIX_FMT_GRAY8, IMAGE_ALIGN) < 0)
122 {
123 LOG(VB_COMMFLAG, LOG_ERR, "CannyEdgeDetector::resetBuffers "
124 "av_image_alloc edges failed");
125 return -1;
126 }
127
128 m_sgm = new unsigned int[1_UZ * padded_width * padded_height];
129 m_sgmSorted = new unsigned int[1_UZ * newwidth * newheight];
130
131 m_ewidth = newwidth;
132 m_eheight = newheight;
133
134 (void)cleanup.release(); // Don't release allocated memory.
135 return 0;
136}
137
138int
139CannyEdgeDetector::setExcludeArea(int row, int col, int width, int height)
140{
141 m_exclude.row = row;
142 m_exclude.col = col;
143 m_exclude.width = width;
144 m_exclude.height = height;
145 return 0;
146}
147
148const AVFrame *
149CannyEdgeDetector::detectEdges(const AVFrame *pgm, int pgmheight,
150 int percentile)
151{
152 /*
153 * Canny edge detection
154 *
155 * See
156 * http://www.cs.cornell.edu/courses/CS664/2003fa/handouts/664-l6-edges-03.pdf
157 */
158
159 const int pgmwidth = pgm->linesize[0];
160 const int padded_height = pgmheight + (2 * m_maskRadius);
161
162 if (resetBuffers(pgmwidth, pgmheight))
163 return nullptr;
164
165 if (pgm_convolve_radial(&m_convolved, &m_s1, &m_s2, pgm, pgmheight,
167 return nullptr;
168
170 sgm_init_exclude(m_sgm, &m_convolved, padded_height,
172 m_exclude.width, m_exclude.height),
173 m_sgmSorted, percentile,
174 m_exclude.row, m_exclude.col, m_exclude.width, m_exclude.height))
175 return nullptr;
176
177 return &m_edges;
178}
179
180/* vim: set expandtab tabstop=4 shiftwidth=4: */
AVFrame AVFrame
unsigned int * m_sgm
int setExcludeArea(int row, int col, int width, int height) override
const AVFrame * detectEdges(const AVFrame *pgm, int pgmheight, int percentile) override
unsigned int * m_sgmSorted
int resetBuffers(int newwidth, int newheight)
~CannyEdgeDetector(void) override
struct CannyEdgeDetector::@76 m_exclude
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
int edge_mark_uniform_exclude(AVFrame *dst, int dstheight, int extramargin, const unsigned int *sgm, unsigned int *sgmsorted, int percentile, int excluderow, int excludecol, int excludewidth, int excludeheight)
unsigned int * sgm_init_exclude(unsigned int *sgm, const AVFrame *src, int srcheight, int excluderow, int excludecol, int excludewidth, int excludeheight)
int pgm_convolve_radial(AVFrame *dst, AVFrame *s1, AVFrame *s2, const AVFrame *src, int srcheight, const double *mask, int mask_radius)
Definition: pgm.cpp:218
static QString cleanup(const QString &str)