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