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
8 extern "C" {
9 #include "libavcodec/avcodec.h" // AVFrame
10 }
11 
12 // MythTV headers
13 #include "libmythtv/mythframe.h" // VideoFrame
14 #include "libmythtv/mythplayer.h"
15 
16 // Commercial Flagging headers
17 #include "EdgeDetector.h"
18 #include "FrameAnalyzer.h"
19 
20 namespace edgeDetector {
21 
22 using namespace frameAnalyzer;
23 
24 unsigned int *
25 sgm_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
59 unsigned int *
60 sgm_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 
66 static int sort_ascending(const void *aa, const void *bb)
67 {
68  return *(unsigned int*)aa - *(unsigned int*)bb;
69 }
70 
71 static int
72 edge_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
176 int 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 
186 int 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 
198 int
199 EdgeDetector::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: */
edgeDetector::edge_mark_uniform_exclude
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)
Definition: EdgeDetector.cpp:186
frameAnalyzer::rrccinrect
bool rrccinrect(int rr, int cc, int rrow, int rcol, int rwidth, int rheight)
Definition: FrameAnalyzer.cpp:10
cc
Definition: cc.h:9
edgeDetector::sgm_init_exclude
unsigned int * sgm_init_exclude(unsigned int *sgm, const AVFrame *src, int srcheight, int excluderow, int excludecol, int excludewidth, int excludeheight)
Definition: EdgeDetector.cpp:25
mythplayer.h
mythframe.h
EdgeDetector::setExcludeArea
virtual int setExcludeArea(int row, int col, int width, int height)
Definition: EdgeDetector.cpp:199
AVFrame
struct AVFrame AVFrame
Definition: BorderDetector.h:15
edgeDetector::sort_ascending
static int sort_ascending(const void *aa, const void *bb)
Definition: EdgeDetector.cpp:66
edgeDetector::edge_mark
static int edge_mark(AVFrame *dst, int dstheight, int extratop, int extraright, [[maybe_unused]] int extrabottom, int extraleft, const unsigned int *sgm, unsigned int *sgmsorted, int percentile, int excluderow, int excludecol, int excludewidth, int excludeheight)
Definition: EdgeDetector.cpp:72
FrameAnalyzer.h
edgeDetector
Definition: EdgeDetector.cpp:20
uint
unsigned int uint
Definition: compat.h:81
EdgeDetector.h
assert
#define assert(x)
Definition: audiooutputalsa.cpp:16
frameAnalyzer
Definition: FrameAnalyzer.cpp:7