MythTV master
pgm.cpp
Go to the documentation of this file.
1#include <climits>
2
3// MythTV
4#include "libmythbase/mythconfig.h"
7
8// Commercial Flagging headers
9#include "pgm.h"
10
11extern "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 */
24static 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
35int 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 // Automatically close file at function exit
45 auto close_fp = [](FILE *fp2) { fclose(fp2); };
46 std::unique_ptr<FILE,decltype(close_fp)> cleanup { fp, close_fp };
47
48 int fwidth = 0;
49 int fheight = 0;
50 int maxgray = 0;
51 int nn = fscanf(fp, "P5\n%20d %20d\n%20d\n", &fwidth, &fheight, &maxgray);
52 if (nn != 3)
53 {
54 LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_read fscanf %1 failed: %2")
55 .arg(filename, strerror(errno)));
56 return -1;
57 }
58
59 if (fwidth != width || fheight != height || maxgray != UCHAR_MAX)
60 {
61 LOG(VB_COMMFLAG, LOG_ERR,
62 QString("pgm_read header (%1x%2,%3) != (%4x%5,%6)")
63 .arg(fwidth).arg(fheight).arg(maxgray)
64 .arg(width).arg(height).arg(UCHAR_MAX));
65 return -1;
66 }
67
68 for (ptrdiff_t rr = 0; rr < height; rr++)
69 {
70 if (fread(buf + (rr * width), 1, width, fp) != (size_t)width)
71 {
72 LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_read fread %1 failed: %2")
73 .arg(filename, strerror(errno)));
74 return -1;
75 }
76 }
77 return 0;
78}
79
80int 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, strerror(errno)));
90 return -1;
91 }
92 // Automatically close file at function exit
93 auto close_fp = [](FILE *fp2) { fclose(fp2); };
94 std::unique_ptr<FILE,decltype(close_fp)> cleanup { fp, close_fp };
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 return -1;
104 }
105 }
106 return 0;
107}
108
109static int pgm_expand(AVFrame *dst, const AVFrame *src, int srcheight,
110 int extratop, int extraright, int extrabottom,
111 int extraleft)
112{
113 /* Pad all edges with the edge color. */
114 const int srcwidth = src->linesize[0];
115 const int newwidth = srcwidth + extraleft + extraright;
116 const int newheight = srcheight + extratop + extrabottom;
117
118 /* Copy the image. */
119 for (ptrdiff_t rr = 0; rr < srcheight; rr++)
120 {
121 memcpy(dst->data[0] + ((rr + extratop) * newwidth) + extraleft,
122 src->data[0] + (rr * srcwidth),
123 srcwidth);
124 }
125
126 /* Pad the top. */
127 const uchar *srcdata = src->data[0];
128 for (ptrdiff_t rr = 0; rr < extratop; rr++)
129 memcpy(dst->data[0] + (rr * newwidth) + extraleft, srcdata, srcwidth);
130
131 /* Pad the bottom. */
132 srcdata = src->data[0] + (static_cast<ptrdiff_t>(srcheight - 1) * srcwidth);
133 for (ptrdiff_t rr = extratop + srcheight; rr < newheight; rr++)
134 memcpy(dst->data[0] + (rr * newwidth) + extraleft, srcdata, srcwidth);
135
136 /* Pad the left. */
137 for (ptrdiff_t rr = 0; rr < newheight; rr++)
138 {
139 memset(dst->data[0] + (rr * newwidth),
140 dst->data[0][(rr * newwidth) + extraleft],
141 extraleft);
142 }
143
144 /* Pad the right. */
145 for (ptrdiff_t rr = 0; rr < newheight; rr++)
146 {
147 memset(dst->data[0] + (rr * newwidth) + extraleft + srcwidth,
148 dst->data[0][(rr * newwidth) + extraleft + srcwidth - 1],
149 extraright);
150 }
151
152 return 0;
153}
154
155static int pgm_expand_uniform(AVFrame *dst, const AVFrame *src,
156 int srcheight, int extramargin)
157{
158 return pgm_expand(dst, src, srcheight,
159 extramargin, extramargin, extramargin, extramargin);
160}
161
162int pgm_crop(AVFrame *dst, const AVFrame *src,
163 [[maybe_unused]] int srcheight,
164 int srcrow, int srccol, int cropwidth, int cropheight)
165{
166 const int srcwidth = src->linesize[0];
167
168 if (dst->linesize[0] != cropwidth)
169 {
170 LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_crop want width %1, have %2")
171 .arg(cropwidth).arg(dst->linesize[0]));
172 return -1;
173 }
174
175 for (ptrdiff_t rr = 0; rr < cropheight; rr++)
176 {
177 memcpy(dst->data[0] + (rr * cropwidth),
178 src->data[0] + ((srcrow + rr) * srcwidth) + srccol,
179 cropwidth);
180 }
181
182 return 0;
183}
184
185int pgm_overlay(AVFrame *dst, const AVFrame *s1, int s1height,
186 int s1row, int s1col, const AVFrame *s2, int s2height)
187{
188 const int dstwidth = dst->linesize[0];
189 const int s1width = s1->linesize[0];
190 const int s2width = s2->linesize[0];
191
192 if (dstwidth != s1width)
193 {
194 LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_overlay want width %1, have %2")
195 .arg(s1width).arg(dst->linesize[0]));
196 return -1;
197 }
198
199 // av_image_copy is badly designed to require writeable
200 // pointers to the read-only data, so copy the pointers here
201 std::array<const uint8_t*,4> src_data
202 {s1->data[0], s1->data[1], s1->data[2], s1->data[3]};
203
204 av_image_copy(dst->data, dst->linesize, src_data.data(), s1->linesize,
205 AV_PIX_FMT_GRAY8, s1width, s1height);
206
207 /* Overwrite overlay area of "dst" with "s2". */
208 for (ptrdiff_t rr = 0; rr < s2height; rr++)
209 {
210 memcpy(dst->data[0] + ((s1row + rr) * s1width) + s1col,
211 s2->data[0] + (rr * s2width),
212 s2width);
213 }
214
215 return 0;
216}
217
219 const AVFrame *src, int srcheight,
220 const double *mask, int mask_radius)
221{
222 /*
223 * Pad and convolve an image.
224 *
225 * "s1" and "s2" are caller-pre-allocated "scratch space" (avoid repeated
226 * per-frame allocation/deallocation).
227 *
228 * Remove noise from image; smooth by convolving with a Gaussian mask. See
229 * http://www.cogs.susx.ac.uk/users/davidy/teachvision/vision0.html
230 *
231 * Optimization for radially-symmetric masks: implement a single
232 * two-dimensional convolution with two commutative single-dimensional
233 * convolutions.
234 */
235 const int srcwidth = src->linesize[0];
236 const int newwidth = srcwidth + (2 * mask_radius);
237 const int newheight = srcheight + (2 * mask_radius);
238
239 /* Get a padded copy of the src image for use by the convolutions. */
240 if (pgm_expand_uniform(s1, src, srcheight, mask_radius))
241 return -1;
242
243 /* copy s1 to s2 and dst */
244
245 // av_image_copy is badly designed to require writeable
246 // pointers to the read-only data, so copy the pointers here
247 std::array<const uint8_t*,4> src_data
248 {s1->data[0], s1->data[1], s1->data[2], s1->data[3]};
249
250 av_image_copy(s2->data, s2->linesize, src_data.data(), s1->linesize,
251 AV_PIX_FMT_GRAY8, newwidth, newheight);
252 av_image_copy(dst->data, dst->linesize, src_data.data(), s1->linesize,
253 AV_PIX_FMT_GRAY8, newwidth, newheight);
254
255 /* "s1" convolve with column vector => "s2" */
256 int rr2 = mask_radius + srcheight;
257 int cc2 = mask_radius + srcwidth;
258 for (int rr = mask_radius; rr < rr2; rr++)
259 {
260 for (int cc = mask_radius; cc < cc2; cc++)
261 {
262 double sum = 0;
263 for (int ii = -mask_radius; ii <= mask_radius; ii++)
264 {
265 sum += mask[ii + mask_radius] *
266 s1->data[0][((rr + ii) * newwidth) + cc];
267 }
268 s2->data[0][(rr * newwidth) + cc] = lround(sum);
269 }
270 }
271
272 /* "s2" convolve with row vector => "dst" */
273 for (int rr = mask_radius; rr < rr2; rr++)
274 {
275 for (int cc = mask_radius; cc < cc2; cc++)
276 {
277 double sum = 0;
278 for (int ii = -mask_radius; ii <= mask_radius; ii++)
279 {
280 sum += mask[ii + mask_radius] *
281 s2->data[0][(rr * newwidth) + cc + ii];
282 }
283 dst->data[0][(rr * newwidth) + cc] = lround(sum);
284 }
285 }
286
287 return 0;
288}
289
290/* 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:138
int pgm_convolve_radial(AVFrame *dst, AVFrame *s1, AVFrame *s2, const AVFrame *src, int srcheight, const double *mask, int mask_radius)
Definition: pgm.cpp:218
int pgm_write(const unsigned char *buf, int width, int height, const char *filename)
Definition: pgm.cpp:80
static int pgm_expand(AVFrame *dst, const AVFrame *src, int srcheight, int extratop, int extraright, int extrabottom, int extraleft)
Definition: pgm.cpp:109
int pgm_crop(AVFrame *dst, const AVFrame *src, int srcheight, int srcrow, int srccol, int cropwidth, int cropheight)
Definition: pgm.cpp:162
int pgm_read(unsigned char *buf, int width, int height, const char *filename)
Definition: pgm.cpp:35
static int pgm_expand_uniform(AVFrame *dst, const AVFrame *src, int srcheight, int extramargin)
Definition: pgm.cpp:155
int pgm_overlay(AVFrame *dst, const AVFrame *s1, int s1height, int s1row, int s1col, const AVFrame *s2, int s2height)
Definition: pgm.cpp:185
static QString cleanup(const QString &str)