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