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