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