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