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