MythTV master
EdgeDetector.cpp
Go to the documentation of this file.
1// C++ headers
2#include <algorithm>
3#include <cstdlib>
4
5#include "libmythbase/mythconfig.h"
6
7// avlib/ffmpeg headers
8extern "C" {
9#include "libavcodec/avcodec.h" // AVFrame
10}
11
12// MythTV headers
13#include "libmythtv/mythframe.h" // VideoFrame
15
16// Commercial Flagging headers
17#include "EdgeDetector.h"
18#include "FrameAnalyzer.h"
19
20namespace edgeDetector {
21
22using namespace frameAnalyzer;
23
24unsigned int *
25sgm_init_exclude(unsigned int *sgm, const AVFrame *src, int srcheight,
26 int excluderow, int excludecol, int excludewidth, int excludeheight)
27{
28 /*
29 * Squared Gradient Magnitude (SGM) calculations: use a 45-degree rotated
30 * set of axes.
31 *
32 * Intuitively, the SGM of a pixel is a measure of the "edge intensity" of
33 * that pixel: how much it differs from its neighbors.
34 */
35 const size_t srcwidth = src->linesize[0];
36
37 memset(sgm, 0, srcwidth * srcheight * sizeof(*sgm));
38 int rr2 = srcheight - 1;
39 int cc2 = srcwidth - 1;
40 for (int rr = 0; rr < rr2; rr++)
41 {
42 for (int cc = 0; cc < cc2; cc++)
43 {
44 if (!rrccinrect(rr, cc, excluderow, excludecol,
45 excludewidth, excludeheight))
46 {
47 uchar *rr0 = &src->data[0][(rr * srcwidth) + cc];
48 uchar *rr1 = &src->data[0][((rr + 1) * srcwidth) + cc];
49 int dx = rr1[1] - rr0[0]; /* southeast - northwest */
50 int dy = rr1[0] - rr0[1]; /* southwest - northeast */
51 sgm[(rr * srcwidth) + cc] = (dx * dx) + (dy * dy);
52 }
53 }
54 }
55 return sgm;
56}
57
58#ifdef LATER
59unsigned int *
60sgm_init(unsigned int *sgm, const AVFrame *src, int srcheight)
61{
62 return sgm_init_exclude(sgm, src, srcheight, 0, 0, 0, 0);
63}
64#endif /* LATER */
65
66static int sort_ascending(const void *aa, const void *bb)
67{
68 return *(unsigned int*)aa - *(unsigned int*)bb;
69}
70
71static int
72edge_mark(AVFrame *dst, int dstheight,
73 int extratop, int extraright,
74 [[maybe_unused]] int extrabottom,
75 int extraleft,
76 const unsigned int *sgm, unsigned int *sgmsorted, int percentile,
77 int excluderow, int excludecol, int excludewidth, int excludeheight)
78{
79 /*
80 * TUNABLE:
81 *
82 * Conventionally, the Canny edge detector should select for intensities at
83 * the 95th percentile or higher. In case the requested percentile actually
84 * yields something lower (degenerate cases), pick the next unique
85 * intensity, to try to salvage useful data.
86 */
87 static constexpr int kMinThresholdPct = 95;
88
89 const int dstwidth = dst->linesize[0];
90 const int padded_width = extraleft + dstwidth + extraright;
91
92 /*
93 * sgm: SGM values of padded (convolved) image
94 *
95 * sgmsorted: sorted SGM values of unexcluded areas of unpadded image (same
96 * dimensions as "dst").
97 */
98 int nn = 0;
99 for (int rr = 0; rr < dstheight; rr++)
100 {
101 for (int cc = 0; cc < dstwidth; cc++)
102 {
103 if (!rrccinrect(rr, cc, excluderow, excludecol,
104 excludewidth, excludeheight))
105 {
106 sgmsorted[nn++] = sgm[((extratop + rr) * padded_width) +
107 extraleft + cc];
108 }
109 }
110 }
111
112 int dstnn = dstwidth * dstheight;
113#if 0
114 assert(nn == dstnn -
115 (min(max(0, excluderow + excludeheight), dstheight) -
116 min(max(0, excluderow), dstheight)) *
117 (min(max(0, excludecol + excludewidth), dstwidth) -
118 min(max(0, excludecol), dstwidth)));
119#endif
120 memset(dst->data[0], 0, dstnn * sizeof(*dst->data[0]));
121
122 if (!nn)
123 {
124 /* Degenerate case (entire area excluded from analysis). */
125 return 0;
126 }
127
128 qsort(sgmsorted, nn, sizeof(*sgmsorted), sort_ascending);
129
130 int ii = percentile * nn / 100;
131 uint thresholdval = sgmsorted[ii];
132
133 /*
134 * Try not to pick up too many edges, and eliminate degenerate edge-less
135 * cases.
136 */
137 int first = ii;
138 for ( ; first > 0 && sgmsorted[first] == thresholdval; first--) ;
139 if (sgmsorted[first] != thresholdval)
140 first++;
141 if (first * 100 / nn < kMinThresholdPct)
142 {
143 int last = ii;
144 int last2 = nn - 1;
145 for ( ; last < last2 && sgmsorted[last] == thresholdval;
146 last++) ;
147 if (sgmsorted[last] != thresholdval)
148 last--;
149
150 uint newthresholdval = sgmsorted[std::min(last + 1, nn - 1)];
151 if (thresholdval == newthresholdval)
152 {
153 /* Degenerate case; no edges (e.g., blank frame). */
154 return 0;
155 }
156
157 thresholdval = newthresholdval;
158 }
159
160 /* sgm is a padded matrix; dst is the unpadded matrix. */
161 for (int rr = 0; rr < dstheight; rr++)
162 {
163 for (int cc = 0; cc < dstwidth; cc++)
164 {
165 if (!rrccinrect(rr, cc, excluderow, excludecol,
166 excludewidth, excludeheight) &&
167 sgm[((extratop + rr) * padded_width) + extraleft + cc] >=
168 thresholdval)
169 dst->data[0][(rr * dstwidth) + cc] = UCHAR_MAX;
170 }
171 }
172 return 0;
173}
174
175#ifdef LATER
176int edge_mark_uniform(AVFrame *dst, int dstheight, int extramargin,
177 const unsigned int *sgm, unsigned int *sgmsorted,
178 int percentile)
179{
180 return edge_mark(dst, dstheight,
181 extramargin, extramargin, extramargin, extramargin,
182 sgm, sgmsorted, percentile, 0, 0, 0, 0);
183}
184#endif /* LATER */
185
186int edge_mark_uniform_exclude(AVFrame *dst, int dstheight, int extramargin,
187 const unsigned int *sgm, unsigned int *sgmsorted, int percentile,
188 int excluderow, int excludecol, int excludewidth, int excludeheight)
189{
190 return edge_mark(dst, dstheight,
191 extramargin, extramargin, extramargin, extramargin,
192 sgm, sgmsorted, percentile,
193 excluderow, excludecol, excludewidth, excludeheight);
194}
195
196}; /* namespace */
197
198int
199EdgeDetector::setExcludeArea([[maybe_unused]] int row,
200 [[maybe_unused]] int col,
201 [[maybe_unused]] int width,
202 [[maybe_unused]] int height)
203{
204 return 0;
205}
206
207/* vim: set expandtab tabstop=4 shiftwidth=4: */
AVFrame AVFrame
#define assert(x)
virtual int setExcludeArea(int row, int col, int width, int height)
unsigned int uint
Definition: freesurround.h:24
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)
static int edge_mark(AVFrame *dst, int dstheight, int extratop, int extraright, int extrabottom, int extraleft, 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)
static int sort_ascending(const void *aa, const void *bb)
bool rrccinrect(int rr, int cc, int rrow, int rcol, int rwidth, int rheight)