|
MythTV
0.26-pre
|
00001 #include <climits> 00002 00003 extern "C" { 00004 #include "libavcodec/avcodec.h" 00005 } 00006 #include "frame.h" 00007 #include "mythlogging.h" 00008 #include "myth_imgconvert.h" 00009 #include "pgm.h" 00010 00011 // TODO: verify this 00012 /* 00013 * N.B.: this is really C code, but LOG, #define'd in mythlogging.h, is in 00014 * a C++ header file, so this has to be compiled with a C++ compiler, which 00015 * means this has to be a C++ source file. 00016 */ 00017 00018 static enum PixelFormat pixelTypeOfVideoFrameType(VideoFrameType codec) 00019 { 00020 /* XXX: how to map VideoFrameType values to PixelFormat values??? */ 00021 switch (codec) { 00022 case FMT_YV12: return PIX_FMT_YUV420P; 00023 default: break; 00024 } 00025 return PIX_FMT_NONE; 00026 } 00027 00028 int pgm_fill(AVPicture *dst, const VideoFrame *frame) 00029 { 00030 enum PixelFormat srcfmt; 00031 AVPicture src; 00032 00033 if ((srcfmt = pixelTypeOfVideoFrameType(frame->codec)) == PIX_FMT_NONE) 00034 { 00035 LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_fill unknown codec: %1") 00036 .arg(frame->codec)); 00037 return -1; 00038 } 00039 00040 if (avpicture_fill(&src, frame->buf, srcfmt, frame->width, 00041 frame->height) < 0) 00042 { 00043 LOG(VB_COMMFLAG, LOG_ERR, "pgm_fill avpicture_fill failed"); 00044 return -1; 00045 } 00046 00047 if (myth_sws_img_convert(dst, PIX_FMT_GRAY8, &src, srcfmt, frame->width, 00048 frame->height)) 00049 { 00050 LOG(VB_COMMFLAG, LOG_ERR, "pgm_fill img_convert failed"); 00051 return -1; 00052 } 00053 00054 return 0; 00055 } 00056 00057 int pgm_read(unsigned char *buf, int width, int height, const char *filename) 00058 { 00059 FILE *fp; 00060 int nn, fwidth, fheight, maxgray, rr; 00061 00062 if (!(fp = fopen(filename, "r"))) 00063 { 00064 LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_read fopen %1 failed: %2") 00065 .arg(filename).arg(strerror(errno))); 00066 return -1; 00067 } 00068 00069 if ((nn = fscanf(fp, "P5\n%20d %20d\n%20d\n", 00070 &fwidth, &fheight, &maxgray)) != 3) 00071 { 00072 LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_read fscanf %1 failed: %2") 00073 .arg(filename).arg(strerror(errno))); 00074 goto error; 00075 } 00076 00077 if (fwidth != width || fheight != height || maxgray != UCHAR_MAX) 00078 { 00079 LOG(VB_COMMFLAG, LOG_ERR, 00080 QString("pgm_read header (%1x%2,%3) != (%4x%5,%6)") 00081 .arg(fwidth).arg(fheight).arg(maxgray) 00082 .arg(width).arg(height).arg(UCHAR_MAX)); 00083 goto error; 00084 } 00085 00086 for (rr = 0; rr < height; rr++) 00087 { 00088 if (fread(buf + rr * width, 1, width, fp) != (size_t)width) 00089 { 00090 LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_read fread %1 failed: %2") 00091 .arg(filename).arg(strerror(errno))); 00092 goto error; 00093 } 00094 } 00095 00096 (void)fclose(fp); 00097 return 0; 00098 00099 error: 00100 (void)fclose(fp); 00101 return -1; 00102 } 00103 00104 int pgm_write(const unsigned char *buf, int width, int height, 00105 const char *filename) 00106 { 00107 /* Copied from libavcodec/apiexample.c */ 00108 FILE *fp; 00109 int rr; 00110 00111 if (!(fp = fopen(filename, "w"))) 00112 { 00113 LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_write fopen %1 failed: %2") 00114 .arg(filename).arg(strerror(errno))); 00115 return -1; 00116 } 00117 00118 (void)fprintf(fp, "P5\n%d %d\n%d\n", width, height, UCHAR_MAX); 00119 for (rr = 0; rr < height; rr++) 00120 { 00121 if (fwrite(buf + rr * width, 1, width, fp) != (size_t)width) 00122 { 00123 LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_write fwrite %1 failed: %2") 00124 .arg(filename).arg(strerror(errno))); 00125 goto error; 00126 } 00127 } 00128 00129 (void)fclose(fp); 00130 return 0; 00131 00132 error: 00133 (void)fclose(fp); 00134 return -1; 00135 } 00136 00137 static int pgm_expand(AVPicture *dst, const AVPicture *src, int srcheight, 00138 int extratop, int extraright, int extrabottom, 00139 int extraleft) 00140 { 00141 /* Pad all edges with the edge color. */ 00142 const int srcwidth = src->linesize[0]; 00143 const int newwidth = srcwidth + extraleft + extraright; 00144 const int newheight = srcheight + extratop + extrabottom; 00145 const unsigned char *srcdata; 00146 int rr; 00147 00148 /* Copy the image. */ 00149 for (rr = 0; rr < srcheight; rr++) 00150 memcpy(dst->data[0] + (rr + extratop) * newwidth + extraleft, 00151 src->data[0] + rr * srcwidth, 00152 srcwidth); 00153 00154 /* Pad the top. */ 00155 srcdata = src->data[0]; 00156 for (rr = 0; rr < extratop; rr++) 00157 memcpy(dst->data[0] + rr * newwidth + extraleft, srcdata, srcwidth); 00158 00159 /* Pad the bottom. */ 00160 srcdata = src->data[0] + (srcheight - 1) * srcwidth; 00161 for (rr = extratop + srcheight; rr < newheight; rr++) 00162 memcpy(dst->data[0] + rr * newwidth + extraleft, srcdata, srcwidth); 00163 00164 /* Pad the left. */ 00165 for (rr = 0; rr < newheight; rr++) 00166 memset(dst->data[0] + rr * newwidth, 00167 dst->data[0][rr * newwidth + extraleft], 00168 extraleft); 00169 00170 /* Pad the right. */ 00171 for (rr = 0; rr < newheight; rr++) 00172 memset(dst->data[0] + rr * newwidth + extraleft + srcwidth, 00173 dst->data[0][rr * newwidth + extraleft + srcwidth - 1], 00174 extraright); 00175 00176 return 0; 00177 } 00178 00179 static int pgm_expand_uniform(AVPicture *dst, const AVPicture *src, 00180 int srcheight, int extramargin) 00181 { 00182 return pgm_expand(dst, src, srcheight, 00183 extramargin, extramargin, extramargin, extramargin); 00184 } 00185 00186 int pgm_crop(AVPicture *dst, const AVPicture *src, int srcheight, 00187 int srcrow, int srccol, int cropwidth, int cropheight) 00188 { 00189 const int srcwidth = src->linesize[0]; 00190 int rr; 00191 00192 if (dst->linesize[0] != cropwidth) 00193 { 00194 LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_crop want width %1, have %2") 00195 .arg(cropwidth).arg(dst->linesize[0])); 00196 return -1; 00197 } 00198 00199 for (rr = 0; rr < cropheight; rr++) 00200 memcpy(dst->data[0] + rr * cropwidth, 00201 src->data[0] + (srcrow + rr) * srcwidth + srccol, 00202 cropwidth); 00203 00204 (void)srcheight; /* gcc */ 00205 return 0; 00206 } 00207 00208 int pgm_overlay(AVPicture *dst, const AVPicture *s1, int s1height, 00209 int s1row, int s1col, const AVPicture *s2, int s2height) 00210 { 00211 const int dstwidth = dst->linesize[0]; 00212 const int s1width = s1->linesize[0]; 00213 const int s2width = s2->linesize[0]; 00214 int rr; 00215 00216 if (dstwidth != s1width) 00217 { 00218 LOG(VB_COMMFLAG, LOG_ERR, QString("pgm_overlay want width %1, have %2") 00219 .arg(s1width).arg(dst->linesize[0])); 00220 return -1; 00221 } 00222 00223 av_picture_copy(dst, s1, PIX_FMT_GRAY8, s1width, s1height); 00224 00225 /* Overwrite overlay area of "dst" with "s2". */ 00226 for (rr = 0; rr < s2height; rr++) 00227 memcpy(dst->data[0] + (s1row + rr) * s1width + s1col, 00228 s2->data[0] + rr * s2width, 00229 s2width); 00230 00231 return 0; 00232 } 00233 00234 int pgm_convolve_radial(AVPicture *dst, AVPicture *s1, AVPicture *s2, 00235 const AVPicture *src, int srcheight, 00236 const double *mask, int mask_radius) 00237 { 00238 /* 00239 * Pad and convolve an image. 00240 * 00241 * "s1" and "s2" are caller-pre-allocated "scratch space" (avoid repeated 00242 * per-frame allocation/deallocation). 00243 * 00244 * Remove noise from image; smooth by convolving with a Gaussian mask. See 00245 * http://www.cogs.susx.ac.uk/users/davidy/teachvision/vision0.html 00246 * 00247 * Optimization for radially-symmetric masks: implement a single 00248 * two-dimensional convolution with two commutative single-dimensional 00249 * convolutions. 00250 */ 00251 const int srcwidth = src->linesize[0]; 00252 const int newwidth = srcwidth + 2 * mask_radius; 00253 const int newheight = srcheight + 2 * mask_radius; 00254 int ii, rr, cc, rr2, cc2; 00255 double sum; 00256 00257 /* Get a padded copy of the src image for use by the convolutions. */ 00258 if (pgm_expand_uniform(s1, src, srcheight, mask_radius)) 00259 return -1; 00260 00261 /* copy s1 to s2 and dst */ 00262 av_picture_copy(s2, s1, PIX_FMT_GRAY8, newwidth, newheight); 00263 av_picture_copy(dst, s1, PIX_FMT_GRAY8, newwidth, newheight); 00264 00265 /* "s1" convolve with column vector => "s2" */ 00266 rr2 = mask_radius + srcheight; 00267 cc2 = mask_radius + srcwidth; 00268 for (rr = mask_radius; rr < rr2; rr++) 00269 { 00270 for (cc = mask_radius; cc < cc2; cc++) 00271 { 00272 sum = 0; 00273 for (ii = -mask_radius; ii <= mask_radius; ii++) 00274 { 00275 sum += mask[ii + mask_radius] * 00276 s1->data[0][(rr + ii) * newwidth + cc]; 00277 } 00278 s2->data[0][rr * newwidth + cc] = (unsigned char)(sum + 0.5); 00279 } 00280 } 00281 00282 /* "s2" convolve with row vector => "dst" */ 00283 for (rr = mask_radius; rr < rr2; rr++) 00284 { 00285 for (cc = mask_radius; cc < cc2; cc++) 00286 { 00287 sum = 0; 00288 for (ii = -mask_radius; ii <= mask_radius; ii++) 00289 { 00290 sum += mask[ii + mask_radius] * 00291 s2->data[0][rr * newwidth + cc + ii]; 00292 } 00293 dst->data[0][rr * newwidth + cc] = (unsigned char)(sum + 0.5); 00294 } 00295 } 00296 00297 return 0; 00298 } 00299 00300 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1.7.6.1