MythTV  master
pgm.cpp
Go to the documentation of this file.
1 #include <climits>
2 
3 // MythTV
4 #include "libmythbase/mythconfig.h"
6 #include "libmythtv/mythframe.h"
7 
8 // Commercial Flagging headers
9 #include "pgm.h"
10 
11 extern "C" {
12 #include "libavcodec/avcodec.h"
13 #include "libavutil/imgutils.h"
14 }
15 
16 // TODO: verify this
17 /*
18  * N.B.: this is really C code, but LOG, #define'd in mythlogging.h, is in
19  * a C++ header file, so this has to be compiled with a C++ compiler, which
20  * means this has to be a C++ source file.
21  */
22 
23 #if 0 /* compiler says its unused */
24 static enum PixelFormat pixelTypeOfVideoFrameType(VideoFrameType codec)
25 {
26  /* XXX: how to map VideoFrameType values to PixelFormat values??? */
27  switch (codec) {
28  case FMT_YV12: return AV_PIX_FMT_YUV420P;
29  default: break;
30  }
31  return AV_PIX_FMT_NONE;
32 }
33 #endif
34 
35 int pgm_read(unsigned char *buf, int width, int height, const char *filename)
36 {
37  FILE *fp = fopen(filename, "r");
38  if (fp == nullptr)
39  {
40  LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_read fopen %1 failed: %2")
41  .arg(filename, strerror(errno)));
42  return -1;
43  }
44 
45  int fwidth = 0;
46  int fheight = 0;
47  int maxgray = 0;
48  int nn = fscanf(fp, "P5\n%20d %20d\n%20d\n", &fwidth, &fheight, &maxgray);
49  if (nn != 3)
50  {
51  LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_read fscanf %1 failed: %2")
52  .arg(filename, strerror(errno)));
53  goto error;
54  }
55 
56  if (fwidth != width || fheight != height || maxgray != UCHAR_MAX)
57  {
58  LOG(VB_COMMFLAG, LOG_ERR,
59  QString("pgm_read header (%1x%2,%3) != (%4x%5,%6)")
60  .arg(fwidth).arg(fheight).arg(maxgray)
61  .arg(width).arg(height).arg(UCHAR_MAX));
62  goto error;
63  }
64 
65  for (ptrdiff_t rr = 0; rr < height; rr++)
66  {
67  if (fread(buf + rr * width, 1, width, fp) != (size_t)width)
68  {
69  LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_read fread %1 failed: %2")
70  .arg(filename, strerror(errno)));
71  goto error;
72  }
73  }
74 
75  (void)fclose(fp);
76  return 0;
77 
78 error:
79  (void)fclose(fp);
80  return -1;
81 }
82 
83 int pgm_write(const unsigned char *buf, int width, int height,
84  const char *filename)
85 {
86  /* Copied from libavcodec/apiexample.c */
87 
88  FILE *fp = fopen(filename, "w");
89  if (fp == nullptr)
90  {
91  LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_write fopen %1 failed: %2")
92  .arg(filename, strerror(errno)));
93  return -1;
94  }
95 
96  (void)fprintf(fp, "P5\n%d %d\n%d\n", width, height, UCHAR_MAX);
97  for (ptrdiff_t rr = 0; rr < height; rr++)
98  {
99  if (fwrite(buf + rr * width, 1, width, fp) != (size_t)width)
100  {
101  LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_write fwrite %1 failed: %2")
102  .arg(filename, strerror(errno)));
103  goto error;
104  }
105  }
106 
107  (void)fclose(fp);
108  return 0;
109 
110 error:
111  (void)fclose(fp);
112  return -1;
113 }
114 
115 static int pgm_expand(AVFrame *dst, const AVFrame *src, int srcheight,
116  int extratop, int extraright, int extrabottom,
117  int extraleft)
118 {
119  /* Pad all edges with the edge color. */
120  const int srcwidth = src->linesize[0];
121  const int newwidth = srcwidth + extraleft + extraright;
122  const int newheight = srcheight + extratop + extrabottom;
123 
124  /* Copy the image. */
125  for (ptrdiff_t rr = 0; rr < srcheight; rr++)
126  {
127  memcpy(dst->data[0] + (rr + extratop) * newwidth + extraleft,
128  src->data[0] + rr * srcwidth,
129  srcwidth);
130  }
131 
132  /* Pad the top. */
133  const uchar *srcdata = src->data[0];
134  for (ptrdiff_t rr = 0; rr < extratop; rr++)
135  memcpy(dst->data[0] + rr * newwidth + extraleft, srcdata, srcwidth);
136 
137  /* Pad the bottom. */
138  srcdata = src->data[0] + static_cast<ptrdiff_t>(srcheight - 1) * srcwidth;
139  for (ptrdiff_t rr = extratop + srcheight; rr < newheight; rr++)
140  memcpy(dst->data[0] + rr * newwidth + extraleft, srcdata, srcwidth);
141 
142  /* Pad the left. */
143  for (ptrdiff_t rr = 0; rr < newheight; rr++)
144  {
145  memset(dst->data[0] + rr * newwidth,
146  dst->data[0][rr * newwidth + extraleft],
147  extraleft);
148  }
149 
150  /* Pad the right. */
151  for (ptrdiff_t rr = 0; rr < newheight; rr++)
152  {
153  memset(dst->data[0] + rr * newwidth + extraleft + srcwidth,
154  dst->data[0][rr * newwidth + extraleft + srcwidth - 1],
155  extraright);
156  }
157 
158  return 0;
159 }
160 
161 static int pgm_expand_uniform(AVFrame *dst, const AVFrame *src,
162  int srcheight, int extramargin)
163 {
164  return pgm_expand(dst, src, srcheight,
165  extramargin, extramargin, extramargin, extramargin);
166 }
167 
168 int pgm_crop(AVFrame *dst, const AVFrame *src,
169  [[maybe_unused]] int srcheight,
170  int srcrow, int srccol, int cropwidth, int cropheight)
171 {
172  const int srcwidth = src->linesize[0];
173 
174  if (dst->linesize[0] != cropwidth)
175  {
176  LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_crop want width %1, have %2")
177  .arg(cropwidth).arg(dst->linesize[0]));
178  return -1;
179  }
180 
181  for (ptrdiff_t rr = 0; rr < cropheight; rr++)
182  {
183  memcpy(dst->data[0] + rr * cropwidth,
184  src->data[0] + (srcrow + rr) * srcwidth + srccol,
185  cropwidth);
186  }
187 
188  return 0;
189 }
190 
191 int pgm_overlay(AVFrame *dst, const AVFrame *s1, int s1height,
192  int s1row, int s1col, const AVFrame *s2, int s2height)
193 {
194  const int dstwidth = dst->linesize[0];
195  const int s1width = s1->linesize[0];
196  const int s2width = s2->linesize[0];
197 
198  if (dstwidth != s1width)
199  {
200  LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_overlay want width %1, have %2")
201  .arg(s1width).arg(dst->linesize[0]));
202  return -1;
203  }
204 
205  // av_image_copy is badly designed to require writeable
206  // pointers to the read-only data, so copy the pointers here
207  std::array<const uint8_t*,4> src_data
208  {s1->data[0], s1->data[1], s1->data[2], s1->data[3]};
209 
210  av_image_copy(dst->data, dst->linesize, src_data.data(), s1->linesize,
211  AV_PIX_FMT_GRAY8, s1width, s1height);
212 
213  /* Overwrite overlay area of "dst" with "s2". */
214  for (ptrdiff_t rr = 0; rr < s2height; rr++)
215  {
216  memcpy(dst->data[0] + (s1row + rr) * s1width + s1col,
217  s2->data[0] + rr * s2width,
218  s2width);
219  }
220 
221  return 0;
222 }
223 
225  const AVFrame *src, int srcheight,
226  const double *mask, int mask_radius)
227 {
228  /*
229  * Pad and convolve an image.
230  *
231  * "s1" and "s2" are caller-pre-allocated "scratch space" (avoid repeated
232  * per-frame allocation/deallocation).
233  *
234  * Remove noise from image; smooth by convolving with a Gaussian mask. See
235  * http://www.cogs.susx.ac.uk/users/davidy/teachvision/vision0.html
236  *
237  * Optimization for radially-symmetric masks: implement a single
238  * two-dimensional convolution with two commutative single-dimensional
239  * convolutions.
240  */
241  const int srcwidth = src->linesize[0];
242  const int newwidth = srcwidth + 2 * mask_radius;
243  const int newheight = srcheight + 2 * mask_radius;
244 
245  /* Get a padded copy of the src image for use by the convolutions. */
246  if (pgm_expand_uniform(s1, src, srcheight, mask_radius))
247  return -1;
248 
249  /* copy s1 to s2 and dst */
250 
251  // av_image_copy is badly designed to require writeable
252  // pointers to the read-only data, so copy the pointers here
253  std::array<const uint8_t*,4> src_data
254  {s1->data[0], s1->data[1], s1->data[2], s1->data[3]};
255 
256  av_image_copy(s2->data, s2->linesize, src_data.data(), s1->linesize,
257  AV_PIX_FMT_GRAY8, newwidth, newheight);
258  av_image_copy(dst->data, dst->linesize, src_data.data(), s1->linesize,
259  AV_PIX_FMT_GRAY8, newwidth, newheight);
260 
261  /* "s1" convolve with column vector => "s2" */
262  int rr2 = mask_radius + srcheight;
263  int cc2 = mask_radius + srcwidth;
264  for (int rr = mask_radius; rr < rr2; rr++)
265  {
266  for (int cc = mask_radius; cc < cc2; cc++)
267  {
268  double sum = 0;
269  for (int ii = -mask_radius; ii <= mask_radius; ii++)
270  {
271  sum += mask[ii + mask_radius] *
272  s1->data[0][(rr + ii) * newwidth + cc];
273  }
274  s2->data[0][rr * newwidth + cc] = lround(sum);
275  }
276  }
277 
278  /* "s2" convolve with row vector => "dst" */
279  for (int rr = mask_radius; rr < rr2; rr++)
280  {
281  for (int cc = mask_radius; cc < cc2; cc++)
282  {
283  double sum = 0;
284  for (int ii = -mask_radius; ii <= mask_radius; ii++)
285  {
286  sum += mask[ii + mask_radius] *
287  s2->data[0][rr * newwidth + cc + ii];
288  }
289  dst->data[0][rr * newwidth + cc] = lround(sum);
290  }
291  }
292 
293  return 0;
294 }
295 
296 /* vim: set expandtab tabstop=4 shiftwidth=4: */
error
static void error(const char *str,...)
Definition: vbi.cpp:36
pgm_read
int pgm_read(unsigned char *buf, int width, int height, const char *filename)
Definition: pgm.cpp:35
cc
Definition: cc.h:9
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
mythframe.h
mythburn.FILE
int FILE
Definition: mythburn.py:139
AVFrame
struct AVFrame AVFrame
Definition: BorderDetector.h:15
pgm_write
int pgm_write(const unsigned char *buf, int width, int height, const char *filename)
Definition: pgm.cpp:83
pgm_convolve_radial
int pgm_convolve_radial(AVFrame *dst, AVFrame *s1, AVFrame *s2, const AVFrame *src, int srcheight, const double *mask, int mask_radius)
Definition: pgm.cpp:224
mythlogging.h
pgm.h
FMT_YV12
@ FMT_YV12
Definition: mythframe.h:24
pgm_crop
int pgm_crop(AVFrame *dst, const AVFrame *src, [[maybe_unused]] int srcheight, int srcrow, int srccol, int cropwidth, int cropheight)
Definition: pgm.cpp:168
pgm_overlay
int pgm_overlay(AVFrame *dst, const AVFrame *s1, int s1height, int s1row, int s1col, const AVFrame *s2, int s2height)
Definition: pgm.cpp:191
pgm_expand_uniform
static int pgm_expand_uniform(AVFrame *dst, const AVFrame *src, int srcheight, int extramargin)
Definition: pgm.cpp:161
pgm_expand
static int pgm_expand(AVFrame *dst, const AVFrame *src, int srcheight, int extratop, int extraright, int extrabottom, int extraleft)
Definition: pgm.cpp:115
VideoFrameType
VideoFrameType
Definition: mythframe.h:20
build_compdb.filename
filename
Definition: build_compdb.py:21