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 = fopen(filename, "r");
35  if (fp == nullptr)
36  {
37  LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_read fopen %1 failed: %2")
38  .arg(filename).arg(strerror(errno)));
39  return -1;
40  }
41 
42  int fwidth = 0;
43  int fheight = 0;
44  int maxgray = 0;
45  int nn = fscanf(fp, "P5\n%20d %20d\n%20d\n", &fwidth, &fheight, &maxgray);
46  if (nn != 3)
47  {
48  LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_read fscanf %1 failed: %2")
49  .arg(filename).arg(strerror(errno)));
50  goto error;
51  }
52 
53  if (fwidth != width || fheight != height || maxgray != UCHAR_MAX)
54  {
55  LOG(VB_COMMFLAG, LOG_ERR,
56  QString("pgm_read header (%1x%2,%3) != (%4x%5,%6)")
57  .arg(fwidth).arg(fheight).arg(maxgray)
58  .arg(width).arg(height).arg(UCHAR_MAX));
59  goto error;
60  }
61 
62  for (int rr = 0; rr < height; rr++)
63  {
64  if (fread(buf + rr * width, 1, width, fp) != (size_t)width)
65  {
66  LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_read fread %1 failed: %2")
67  .arg(filename).arg(strerror(errno)));
68  goto error;
69  }
70  }
71 
72  (void)fclose(fp);
73  return 0;
74 
75 error:
76  (void)fclose(fp);
77  return -1;
78 }
79 
80 int pgm_write(const unsigned char *buf, int width, int height,
81  const char *filename)
82 {
83  /* Copied from libavcodec/apiexample.c */
84 
85  FILE *fp = fopen(filename, "w");
86  if (fp == nullptr)
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 (int 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 
121  /* Copy the image. */
122  for (int rr = 0; rr < srcheight; rr++)
123  {
124  memcpy(dst->data[0] + (rr + extratop) * newwidth + extraleft,
125  src->data[0] + rr * srcwidth,
126  srcwidth);
127  }
128 
129  /* Pad the top. */
130  const uchar *srcdata = src->data[0];
131  for (int 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 (int rr = extratop + srcheight; rr < newheight; rr++)
137  memcpy(dst->data[0] + rr * newwidth + extraleft, srcdata, srcwidth);
138 
139  /* Pad the left. */
140  for (int rr = 0; rr < newheight; rr++)
141  {
142  memset(dst->data[0] + rr * newwidth,
143  dst->data[0][rr * newwidth + extraleft],
144  extraleft);
145  }
146 
147  /* Pad the right. */
148  for (int rr = 0; rr < newheight; rr++)
149  {
150  memset(dst->data[0] + rr * newwidth + extraleft + srcwidth,
151  dst->data[0][rr * newwidth + extraleft + srcwidth - 1],
152  extraright);
153  }
154 
155  return 0;
156 }
157 
158 static int pgm_expand_uniform(AVFrame *dst, const AVFrame *src,
159  int srcheight, int extramargin)
160 {
161  return pgm_expand(dst, src, srcheight,
162  extramargin, extramargin, extramargin, extramargin);
163 }
164 
165 int pgm_crop(AVFrame *dst, const AVFrame *src, int srcheight,
166  int srcrow, int srccol, int cropwidth, int cropheight)
167 {
168  const int srcwidth = src->linesize[0];
169 
170  if (dst->linesize[0] != cropwidth)
171  {
172  LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_crop want width %1, have %2")
173  .arg(cropwidth).arg(dst->linesize[0]));
174  return -1;
175  }
176 
177  for (int rr = 0; rr < cropheight; rr++)
178  {
179  memcpy(dst->data[0] + rr * cropwidth,
180  src->data[0] + (srcrow + rr) * srcwidth + srccol,
181  cropwidth);
182  }
183 
184  (void)srcheight; /* gcc */
185  return 0;
186 }
187 
188 int pgm_overlay(AVFrame *dst, const AVFrame *s1, int s1height,
189  int s1row, int s1col, const AVFrame *s2, int s2height)
190 {
191  const int dstwidth = dst->linesize[0];
192  const int s1width = s1->linesize[0];
193  const int s2width = s2->linesize[0];
194 
195  if (dstwidth != s1width)
196  {
197  LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_overlay want width %1, have %2")
198  .arg(s1width).arg(dst->linesize[0]));
199  return -1;
200  }
201 
202  // av_image_copy is badly designed to require writeable
203  // pointers to the read-only data, so copy the pointers here
204  const uint8_t *src_data[4]
205  = {s1->data[0], s1->data[1], s1->data[2], s1->data[3]};
206 
207  av_image_copy(dst->data, dst->linesize, src_data, s1->linesize,
208  AV_PIX_FMT_GRAY8, s1width, s1height);
209 
210  /* Overwrite overlay area of "dst" with "s2". */
211  for (int rr = 0; rr < s2height; rr++)
212  {
213  memcpy(dst->data[0] + (s1row + rr) * s1width + s1col,
214  s2->data[0] + rr * s2width,
215  s2width);
216  }
217 
218  return 0;
219 }
220 
222  const AVFrame *src, int srcheight,
223  const double *mask, int mask_radius)
224 {
225  /*
226  * Pad and convolve an image.
227  *
228  * "s1" and "s2" are caller-pre-allocated "scratch space" (avoid repeated
229  * per-frame allocation/deallocation).
230  *
231  * Remove noise from image; smooth by convolving with a Gaussian mask. See
232  * http://www.cogs.susx.ac.uk/users/davidy/teachvision/vision0.html
233  *
234  * Optimization for radially-symmetric masks: implement a single
235  * two-dimensional convolution with two commutative single-dimensional
236  * convolutions.
237  */
238  const int srcwidth = src->linesize[0];
239  const int newwidth = srcwidth + 2 * mask_radius;
240  const int newheight = srcheight + 2 * mask_radius;
241 
242  /* Get a padded copy of the src image for use by the convolutions. */
243  if (pgm_expand_uniform(s1, src, srcheight, mask_radius))
244  return -1;
245 
246  /* copy s1 to s2 and dst */
247 
248  // av_image_copy is badly designed to require writeable
249  // pointers to the read-only data, so copy the pointers here
250  const uint8_t *src_data[4]
251  = {s1->data[0], s1->data[1], s1->data[2], s1->data[3]};
252 
253  av_image_copy(s2->data, s2->linesize, src_data, s1->linesize,
254  AV_PIX_FMT_GRAY8, newwidth, newheight);
255  av_image_copy(dst->data, dst->linesize, src_data, s1->linesize,
256  AV_PIX_FMT_GRAY8, newwidth, newheight);
257 
258  /* "s1" convolve with column vector => "s2" */
259  int rr2 = mask_radius + srcheight;
260  int cc2 = mask_radius + srcwidth;
261  for (int rr = mask_radius; rr < rr2; rr++)
262  {
263  for (int cc = mask_radius; cc < cc2; cc++)
264  {
265  double sum = 0;
266  for (int ii = -mask_radius; ii <= mask_radius; ii++)
267  {
268  sum += mask[ii + mask_radius] *
269  s1->data[0][(rr + ii) * newwidth + cc];
270  }
271  s2->data[0][rr * newwidth + cc] = lround(sum);
272  }
273  }
274 
275  /* "s2" convolve with row vector => "dst" */
276  for (int rr = mask_radius; rr < rr2; rr++)
277  {
278  for (int cc = mask_radius; cc < cc2; cc++)
279  {
280  double sum = 0;
281  for (int ii = -mask_radius; ii <= mask_radius; ii++)
282  {
283  sum += mask[ii + mask_radius] *
284  s2->data[0][rr * newwidth + cc + ii];
285  }
286  dst->data[0][rr * newwidth + cc] = lround(sum);
287  }
288  }
289 
290  return 0;
291 }
292 
293 /* vim: set expandtab tabstop=4 shiftwidth=4: */
Definition: cc.h:13
static void error(const char *str,...)
Definition: vbi.c:42
struct AVFrame AVFrame
VideoFrameType
Definition: mythframe.h:23
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:139
static int pgm_expand_uniform(AVFrame *dst, const AVFrame *src, int srcheight, int extramargin)
Definition: pgm.cpp:158
int pgm_overlay(AVFrame *dst, const AVFrame *s1, int s1height, int s1row, int s1col, const AVFrame *s2, int s2height)
Definition: pgm.cpp:188
#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:165
int pgm_write(const unsigned char *buf, int width, int height, const char *filename)
Definition: pgm.cpp:80
int pgm_convolve_radial(AVFrame *dst, AVFrame *s1, AVFrame *s2, const AVFrame *src, int srcheight, const double *mask, int mask_radius)
Definition: pgm.cpp:221