MythTV  0.26-pre
pgm.cpp
Go to the documentation of this file.
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: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends