diff mbox

[FFmpeg-devel] Add FITS Encoder

Message ID 1500579987-14511-1-git-send-email-paraschadha18@gmail.com
State Superseded
Headers show

Commit Message

Paras July 20, 2017, 7:46 p.m. UTC
Signed-off-by: Paras Chadha <paraschadha18@gmail.com>
---

Made the changes suggested.

 doc/general.texi       |   2 +-
 libavcodec/Makefile    |   1 +
 libavcodec/allcodecs.c |   2 +-
 libavcodec/fitsenc.c   | 237 +++++++++++++++++++++++++++++++++++++++++++++++++
 libavformat/img2enc.c  |   1 +
 5 files changed, 241 insertions(+), 2 deletions(-)
 create mode 100644 libavcodec/fitsenc.c

--
2.4.11

Comments

James Almer July 20, 2017, 8:15 p.m. UTC | #1
On 7/20/2017 4:46 PM, Paras Chadha wrote:
> Signed-off-by: Paras Chadha <paraschadha18@gmail.com>
> ---
> 
> Made the changes suggested.
> 
>  doc/general.texi       |   2 +-
>  libavcodec/Makefile    |   1 +
>  libavcodec/allcodecs.c |   2 +-
>  libavcodec/fitsenc.c   | 237 +++++++++++++++++++++++++++++++++++++++++++++++++
>  libavformat/img2enc.c  |   1 +
>  5 files changed, 241 insertions(+), 2 deletions(-)
>  create mode 100644 libavcodec/fitsenc.c
> 
> diff --git a/doc/general.texi b/doc/general.texi
> index 01402cb..1ea7984 100644
> --- a/doc/general.texi
> +++ b/doc/general.texi
> @@ -592,7 +592,7 @@ following image formats are supported:
>      @tab Digital Picture Exchange
>  @item EXR          @tab   @tab X
>      @tab OpenEXR
> -@item FITS         @tab   @tab X
> +@item FITS         @tab X @tab X
>      @tab Flexible Image Transport System
>  @item JPEG         @tab X @tab X
>      @tab Progressive JPEG is not supported.
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 5348ed9..9b1429f 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -292,6 +292,7 @@ OBJS-$(CONFIG_FFV1_ENCODER)            += ffv1enc.o ffv1.o
>  OBJS-$(CONFIG_FFWAVESYNTH_DECODER)     += ffwavesynth.o
>  OBJS-$(CONFIG_FIC_DECODER)             += fic.o
>  OBJS-$(CONFIG_FITS_DECODER)            += fitsdec.o
> +OBJS-$(CONFIG_FITS_ENCODER)            += fitsenc.o
>  OBJS-$(CONFIG_FLAC_DECODER)            += flacdec.o flacdata.o flac.o
>  OBJS-$(CONFIG_FLAC_ENCODER)            += flacenc.o flacdata.o flac.o vorbis_data.o
>  OBJS-$(CONFIG_FLASHSV_DECODER)         += flashsv.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 8678ac2..7fe66f4 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -192,7 +192,7 @@ static void register_all(void)
>      REGISTER_ENCDEC (FFV1,              ffv1);
>      REGISTER_ENCDEC (FFVHUFF,           ffvhuff);
>      REGISTER_DECODER(FIC,               fic);
> -    REGISTER_DECODER(FITS,              fits);
> +    REGISTER_ENCDEC (FITS,              fits);
>      REGISTER_ENCDEC (FLASHSV,           flashsv);
>      REGISTER_ENCDEC (FLASHSV2,          flashsv2);
>      REGISTER_DECODER(FLIC,              flic);
> diff --git a/libavcodec/fitsenc.c b/libavcodec/fitsenc.c
> new file mode 100644
> index 0000000..cdb662b
> --- /dev/null
> +++ b/libavcodec/fitsenc.c
> @@ -0,0 +1,237 @@
> +/*
> + * FITS image encoder
> + * Copyright (c) 2017 Paras Chadha
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +/**
> + * @file
> + * FITS image encoder
> + *
> + * Specification: https://fits.gsfc.nasa.gov/fits_standard.html Version 3.0
> + *
> + * RGBA images are encoded as planes in RGBA order. So, NAXIS3 is 3 or 4 for them.
> + * Also CTYPE3 = 'RGB ' is added to the header to distinguish them from 3d images.
> + */
> +
> +#include "libavutil/intreadwrite.h"
> +#include "avcodec.h"
> +#include "bytestream.h"
> +#include "internal.h"
> +
> +typedef struct FITSContext {
> +    int first_image;
> +} FITSContext;
> +
> +static av_cold int fits_encode_init(AVCodecContext *avctx)
> +{
> +    FITSContext * fitsctx = avctx->priv_data;
> +    fitsctx->first_image = 1;
> +    return 0;
> +}
> +
> +static int write_keyword_value(uint8_t **bytestream, const char *keyword, int value)
> +{
> +    int len, ret;
> +    uint8_t *header = *bytestream;
> +    len = strlen(keyword);
> +
> +    memset(header, ' ', 80);
> +    memcpy(header, keyword, len);
> +    header[8] = '=';
> +    header[9] = ' ';
> +    header += 10;
> +    ret = snprintf(header, 70, "%d", value);
> +    header[ret] = ' ';
> +
> +    *bytestream += 80;
> +    return 0;
> +}
> +
> +static int fits_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
> +                            const AVFrame *pict, int *got_packet)
> +{
> +    AVFrame * const p = (AVFrame *)pict;
> +    FITSContext *fitsctx = avctx->priv_data;
> +    uint8_t *bytestream, *bytestream_start, *ptr;
> +    uint64_t header_size = 2880, data_size = 0, padded_data_size = 0;
> +    int ret, bitpix, naxis, naxis3 = 1, bzero = 0, i, j, k, t, rgb = 0;
> +
> +    switch (avctx->pix_fmt) {
> +        case AV_PIX_FMT_GRAY8:
> +            bitpix = 8;
> +            naxis = 2;
> +            break;
> +        case AV_PIX_FMT_GRAY16BE:
> +            bitpix = 16;
> +            naxis = 2;
> +            bzero = 32768;
> +            break;
> +        case AV_PIX_FMT_RGB24:
> +        case AV_PIX_FMT_RGBA:
> +            bitpix = 8;
> +            naxis = 3;
> +            rgb = 1;
> +            if (avctx->pix_fmt == AV_PIX_FMT_RGB24) {
> +                naxis3 = 3;
> +            } else {
> +                naxis3 = 4;
> +            }
> +            break;
> +        case AV_PIX_FMT_RGB48BE:
> +        case AV_PIX_FMT_RGBA64BE:
> +            bitpix = 16;
> +            naxis = 3;
> +            bzero = 32768;
> +            rgb = 1;
> +            if (avctx->pix_fmt == AV_PIX_FMT_RGB48BE) {
> +                naxis3 = 3;
> +            } else {
> +                naxis3 = 4;
> +            }
> +            break;
> +        default:
> +            av_log(avctx, AV_LOG_ERROR, "unsupported pixel format\n");
> +            return AVERROR(EINVAL);
> +    }
> +
> +    data_size = (bitpix >> 3) * avctx->height * avctx->width * naxis3;
> +    padded_data_size = ((data_size + 2879) / 2880 ) * 2880;
> +
> +    if ((ret = ff_alloc_packet2(avctx, pkt, header_size + padded_data_size, 0)) < 0)
> +        return ret;
> +
> +    bytestream_start =
> +    bytestream       = pkt->data;
> +
> +    if (fitsctx->first_image) {
> +        memcpy(bytestream, "SIMPLE  = ", 10);
> +        memset(bytestream + 10, ' ', 70);
> +        bytestream[29] = 'T';
> +    } else {
> +        memcpy(bytestream, "XTENSION= 'IMAGE   '", 20);
> +        memset(bytestream + 20, ' ', 60);
> +    }
> +    bytestream += 80;
> +
> +    write_keyword_value(&bytestream, "BITPIX", bitpix);         // no of bits per pixel
> +    write_keyword_value(&bytestream, "NAXIS", naxis);           // no of dimensions of image
> +    write_keyword_value(&bytestream, "NAXIS1", avctx->width);   // first dimension i.e. width
> +    write_keyword_value(&bytestream, "NAXIS2", avctx->height);  // second dimension i.e. height
> +
> +    if (rgb)
> +        write_keyword_value(&bytestream, "NAXIS3", naxis3);     // third dimension to store RGBA planes
> +
> +    if (!fitsctx->first_image) {
> +        write_keyword_value(&bytestream, "PCOUNT", 0);
> +        write_keyword_value(&bytestream, "GCOUNT", 1);
> +    } else {
> +        fitsctx->first_image = 0;
> +    }
> +
> +    /*
> +     * Since FITS does not support unsigned 16 bit integers,
> +     * BZERO = 32768 is used to store unsigned 16 bit integers as
> +     * signed integers so that it can be read properly.
> +     */
> +    if (bitpix == 16)
> +        write_keyword_value(&bytestream, "BZERO", bzero);
> +
> +    if (rgb) {
> +        memcpy(bytestream, "CTYPE3  = 'RGB     '", 20);
> +        memset(bytestream + 20, ' ', 60);
> +        bytestream += 80;
> +    }
> +
> +    memcpy(bytestream, "END", 3);
> +    memset(bytestream + 3, ' ', 77);
> +    bytestream += 80;
> +
> +    t = header_size - (bytestream - bytestream_start);
> +    memset(bytestream, ' ', t);
> +    bytestream += t;
> +
> +    if (rgb) {
> +        switch (avctx->pix_fmt) {
> +            case AV_PIX_FMT_RGB24:
> +            case AV_PIX_FMT_RGBA:
> +                for (k = 0; k < naxis3; k++) {
> +                    for (i = 0; i < avctx->height; i++) {
> +                        ptr = p->data[0] + (avctx->height - i - 1) * p->linesize[0] + k;
> +                        for (j = 0; j < avctx->width; j++) {
> +                            bytestream_put_byte(&bytestream, ptr[0]);
> +                            ptr += naxis3;
> +                        }
> +                    }
> +                }
> +                break;
> +            case AV_PIX_FMT_RGB48BE:
> +            case AV_PIX_FMT_RGBA64BE:
> +                for (k = 0; k < naxis3; k++) {
> +                    for (i = 0; i < avctx->height; i++) {
> +                        ptr = p->data[0] + (avctx->height - i - 1) * p->linesize[0] + k * 2;
> +                        for (j = 0; j < avctx->width; j++) {
> +                            bytestream_put_be16(&bytestream, AV_RB16(ptr) - bzero);
> +                            ptr += naxis3 * 2;
> +                        }
> +                    }
> +                }
> +                break;
> +        }
> +    } else {
> +        for (i = 0; i < avctx->height; i++) {
> +            ptr = p->data[0] + (avctx->height - i - 1) * p->linesize[0];
> +            if (bitpix == 16) {
> +                for (j = 0; j < avctx->width; j++) {
> +                    bytestream_put_be16(&bytestream, AV_RB16(ptr) - bzero);
> +                    ptr += 2;
> +                }
> +            } else {
> +                memcpy(bytestream, ptr, avctx->width);
> +                bytestream += avctx->width;
> +            }
> +        }
> +    }
> +
> +    t = padded_data_size - data_size;
> +    memset(bytestream, 0, t);
> +    bytestream += t;
> +
> +    pkt->size   = bytestream - bytestream_start;
> +    pkt->flags |= AV_PKT_FLAG_KEY;
> +    *got_packet = 1;
> +
> +    return 0;
> +}
> +
> +AVCodec ff_fits_encoder = {
> +    .name           = "fits",
> +    .long_name      = NULL_IF_CONFIG_SMALL("Flexible Image Transport System"),
> +    .type           = AVMEDIA_TYPE_VIDEO,
> +    .id             = AV_CODEC_ID_FITS,
> +    .priv_data_size = sizeof(FITSContext),
> +    .init           = fits_encode_init,
> +    .encode2        = fits_encode_frame,
> +    .pix_fmts       = (const enum AVPixelFormat[]) { AV_PIX_FMT_RGBA64BE,
> +                                                 AV_PIX_FMT_RGB48BE,
> +                                                 AV_PIX_FMT_RGBA,
> +                                                 AV_PIX_FMT_RGB24,
> +                                                 AV_PIX_FMT_GRAY16BE,
> +                                                 AV_PIX_FMT_GRAY8,
> +                                                 AV_PIX_FMT_NONE },
> +};
> diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c
> index 1297b1a..25283cc 100644
> --- a/libavformat/img2enc.c
> +++ b/libavformat/img2enc.c
> @@ -237,6 +237,7 @@ AVOutputFormat ff_image2_muxer = {
>  AVOutputFormat ff_image2pipe_muxer = {
>      .name           = "image2pipe",
>      .long_name      = NULL_IF_CONFIG_SMALL("piped image2 sequence"),
> +    .extensions     = "fits",

This is probably wrong. Did you intend to add this extension to
ff_image2_muxer instead?

Also, libavformat changes should be in their own separate commits.

>      .priv_data_size = sizeof(VideoMuxData),
>      .video_codec    = AV_CODEC_ID_MJPEG,
>      .write_header   = write_header,
> --
> 2.4.11
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
Carl Eugen Hoyos July 20, 2017, 9:57 p.m. UTC | #2
2017-07-20 21:46 GMT+02:00 Paras Chadha <paraschadha18@gmail.com>:

> +            case AV_PIX_FMT_RGB24:
> +            case AV_PIX_FMT_RGBA:
> +                for (k = 0; k < naxis3; k++) {
> +                    for (i = 0; i < avctx->height; i++) {
> +                        ptr = p->data[0] + (avctx->height - i - 1) * p->linesize[0] + k;
> +                        for (j = 0; j < avctx->width; j++) {
> +                            bytestream_put_byte(&bytestream, ptr[0]);
> +                            ptr += naxis3;

(Sorry if this is nonsense:)
Shouldn't you be using PIX_FMT_GBRP and PIX_FMT_GBRAP?

Carl Eugen
Paras July 21, 2017, 2:25 p.m. UTC | #3
On Fri, Jul 21, 2017 at 1:45 AM, James Almer <jamrial@gmail.com> wrote:

> On 7/20/2017 4:46 PM, Paras Chadha wrote:
> > diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c
> > index 1297b1a..25283cc 100644
> > --- a/libavformat/img2enc.c
> > +++ b/libavformat/img2enc.c
> > @@ -237,6 +237,7 @@ AVOutputFormat ff_image2_muxer = {
> >  AVOutputFormat ff_image2pipe_muxer = {
> >      .name           = "image2pipe",
> >      .long_name      = NULL_IF_CONFIG_SMALL("piped image2 sequence"),
> > +    .extensions     = "fits",
>
> This is probably wrong. Did you intend to add this extension to
> ff_image2_muxer instead?
>

No, if there are multiple images, i want them to be appended one after
other. I think image2pipe is the correct muxer to use.


>
> Also, libavformat changes should be in their own separate commits.
>

Okay, i will send separated patches in a few days (waiting for any other
suggestion in the encoder).


>
> >      .priv_data_size = sizeof(VideoMuxData),
> >      .video_codec    = AV_CODEC_ID_MJPEG,
> >      .write_header   = write_header,
> > --
> > 2.4.11
> >
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
Paras July 21, 2017, 2:26 p.m. UTC | #4
On Fri, Jul 21, 2017 at 3:27 AM, Carl Eugen Hoyos <ceffmpeg@gmail.com>
wrote:

> 2017-07-20 21:46 GMT+02:00 Paras Chadha <paraschadha18@gmail.com>:
>
> > +            case AV_PIX_FMT_RGB24:
> > +            case AV_PIX_FMT_RGBA:
> > +                for (k = 0; k < naxis3; k++) {
> > +                    for (i = 0; i < avctx->height; i++) {
> > +                        ptr = p->data[0] + (avctx->height - i - 1) *
> p->linesize[0] + k;
> > +                        for (j = 0; j < avctx->width; j++) {
> > +                            bytestream_put_byte(&bytestream, ptr[0]);
> > +                            ptr += naxis3;
>
> (Sorry if this is nonsense:)
> Shouldn't you be using PIX_FMT_GBRP and PIX_FMT_GBRAP?
>

No, the current pixel formats are working fine. I have tested using GIMP
too. It is showing all the RGB images (and others too) correctly.


>
> Carl Eugen
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
Nicolas George July 21, 2017, 2:29 p.m. UTC | #5
Le tridi 3 thermidor, an CCXXV, Paras Chadha a écrit :
> > >  AVOutputFormat ff_image2pipe_muxer = {
> > >      .name           = "image2pipe",
> > >      .long_name      = NULL_IF_CONFIG_SMALL("piped image2 sequence"),
> > > +    .extensions     = "fits",
> > This is probably wrong. Did you intend to add this extension to
> > ff_image2_muxer instead?
> No, if there are multiple images, i want them to be appended one after
> other. I think image2pipe is the correct muxer to use.

But it is not the way image2pipe handles extensions, otherwise you would
see tga, tiff, sun, etc., already there.

Regards,
Reimar Döffinger July 21, 2017, 6:16 p.m. UTC | #6
On 21.07.2017, at 16:26, Paras Chadha <paraschadha18@gmail.com> wrote:

> On Fri, Jul 21, 2017 at 3:27 AM, Carl Eugen Hoyos <ceffmpeg@gmail.com>
> wrote:
> 
>> 2017-07-20 21:46 GMT+02:00 Paras Chadha <paraschadha18@gmail.com>:
>> 
>>> +            case AV_PIX_FMT_RGB24:
>>> +            case AV_PIX_FMT_RGBA:
>>> +                for (k = 0; k < naxis3; k++) {
>>> +                    for (i = 0; i < avctx->height; i++) {
>>> +                        ptr = p->data[0] + (avctx->height - i - 1) *
>> p->linesize[0] + k;
>>> +                        for (j = 0; j < avctx->width; j++) {
>>> +                            bytestream_put_byte(&bytestream, ptr[0]);
>>> +                            ptr += naxis3;
>> 
>> (Sorry if this is nonsense:)
>> Shouldn't you be using PIX_FMT_GBRP and PIX_FMT_GBRAP?
>> 
> 
> No, the current pixel formats are working fine. I have tested using GIMP
> too. It is showing all the RGB images (and others too) correctly.

I think that's not what he meant.
You are explicitly requesting a packed format that has the data in RGBRGBRGB... order, and you then need these loops to split it into RRRR... GGGG... BBBB...
He's suggesting you should simply request the format storing the data like that, then all you need to do is the unsigned -> signed conversion (which is also unnecessarily complicated since the subtraction is equivalent with a single bit flip which can be done more efficiently with an XOR, and probably we have an assembler-optimized version somewhere, since it's needed a lot for audio).
Carl Eugen Hoyos July 23, 2017, 10:12 p.m. UTC | #7
2017-07-21 20:16 GMT+02:00 Reimar Döffinger <Reimar.Doeffinger@gmx.de>:
> On 21.07.2017, at 16:26, Paras Chadha <paraschadha18@gmail.com> wrote:
>
>> On Fri, Jul 21, 2017 at 3:27 AM, Carl Eugen Hoyos <ceffmpeg@gmail.com>
>> wrote:
>>
>>> 2017-07-20 21:46 GMT+02:00 Paras Chadha <paraschadha18@gmail.com>:
>>>
>>>> +            case AV_PIX_FMT_RGB24:
>>>> +            case AV_PIX_FMT_RGBA:
>>>> +                for (k = 0; k < naxis3; k++) {
>>>> +                    for (i = 0; i < avctx->height; i++) {
>>>> +                        ptr = p->data[0] + (avctx->height - i - 1) *
>>> p->linesize[0] + k;
>>>> +                        for (j = 0; j < avctx->width; j++) {
>>>> +                            bytestream_put_byte(&bytestream, ptr[0]);
>>>> +                            ptr += naxis3;
>>>
>>> (Sorry if this is nonsense:)
>>> Shouldn't you be using PIX_FMT_GBRP and PIX_FMT_GBRAP?
>>>
>>
>> No, the current pixel formats are working fine. I have tested using GIMP
>> too. It is showing all the RGB images (and others too) correctly.
>
> I think that's not what he meant.
> You are explicitly requesting a packed format that has the data in
> RGBRGBRGB... order, and you then need these loops to split it into
> RRRR... GGGG... BBBB...
> He's suggesting you should simply request the format storing the data like that

Yes, this makes above code both simpler and (on typical hardware)
always faster (it will never be slower, no matter the hardware).

> then all you need to do is the unsigned -> signed conversion

Which is not necessary for the above case.

Carl Eugen
Paras July 27, 2017, 7:09 p.m. UTC | #8
On Fri, Jul 21, 2017 at 7:59 PM, Nicolas George <george@nsup.org> wrote:

> Le tridi 3 thermidor, an CCXXV, Paras Chadha a écrit :
> > > >  AVOutputFormat ff_image2pipe_muxer = {
> > > >      .name           = "image2pipe",
> > > >      .long_name      = NULL_IF_CONFIG_SMALL("piped image2 sequence"),
> > > > +    .extensions     = "fits",
> > > This is probably wrong. Did you intend to add this extension to
> > > ff_image2_muxer instead?
> > No, if there are multiple images, i want them to be appended one after
> > other. I think image2pipe is the correct muxer to use.
>
> But it is not the way image2pipe handles extensions, otherwise you would
> see tga, tiff, sun, etc., already there.
>

Okay, I will create a separate muxer for FITS.


>
> Regards,
>
> --
>   Nicolas George
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
>
Paras July 27, 2017, 7:09 p.m. UTC | #9
On Mon, Jul 24, 2017 at 3:42 AM, Carl Eugen Hoyos <ceffmpeg@gmail.com>
wrote:

> 2017-07-21 20:16 GMT+02:00 Reimar Döffinger <Reimar.Doeffinger@gmx.de>:
> > On 21.07.2017, at 16:26, Paras Chadha <paraschadha18@gmail.com> wrote:
> >
> >> On Fri, Jul 21, 2017 at 3:27 AM, Carl Eugen Hoyos <ceffmpeg@gmail.com>
> >> wrote:
> >>
> >>> 2017-07-20 21:46 GMT+02:00 Paras Chadha <paraschadha18@gmail.com>:
> >>>
> >>>> +            case AV_PIX_FMT_RGB24:
> >>>> +            case AV_PIX_FMT_RGBA:
> >>>> +                for (k = 0; k < naxis3; k++) {
> >>>> +                    for (i = 0; i < avctx->height; i++) {
> >>>> +                        ptr = p->data[0] + (avctx->height - i - 1) *
> >>> p->linesize[0] + k;
> >>>> +                        for (j = 0; j < avctx->width; j++) {
> >>>> +                            bytestream_put_byte(&bytestream,
> ptr[0]);
> >>>> +                            ptr += naxis3;
> >>>
> >>> (Sorry if this is nonsense:)
> >>> Shouldn't you be using PIX_FMT_GBRP and PIX_FMT_GBRAP?
> >>>
> >>
> >> No, the current pixel formats are working fine. I have tested using GIMP
> >> too. It is showing all the RGB images (and others too) correctly.
> >
> > I think that's not what he meant.
> > You are explicitly requesting a packed format that has the data in
> > RGBRGBRGB... order, and you then need these loops to split it into
> > RRRR... GGGG... BBBB...
> > He's suggesting you should simply request the format storing the data
> like that
>
> Yes, this makes above code both simpler and (on typical hardware)
> always faster (it will never be slower, no matter the hardware).
>

Yes, done. It has made the code a lot simple, thanks.


>
> > then all you need to do is the unsigned -> signed conversion
>
> Which is not necessary for the above case.
>
> Carl Eugen
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
diff mbox

Patch

diff --git a/doc/general.texi b/doc/general.texi
index 01402cb..1ea7984 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -592,7 +592,7 @@  following image formats are supported:
     @tab Digital Picture Exchange
 @item EXR          @tab   @tab X
     @tab OpenEXR
-@item FITS         @tab   @tab X
+@item FITS         @tab X @tab X
     @tab Flexible Image Transport System
 @item JPEG         @tab X @tab X
     @tab Progressive JPEG is not supported.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 5348ed9..9b1429f 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -292,6 +292,7 @@  OBJS-$(CONFIG_FFV1_ENCODER)            += ffv1enc.o ffv1.o
 OBJS-$(CONFIG_FFWAVESYNTH_DECODER)     += ffwavesynth.o
 OBJS-$(CONFIG_FIC_DECODER)             += fic.o
 OBJS-$(CONFIG_FITS_DECODER)            += fitsdec.o
+OBJS-$(CONFIG_FITS_ENCODER)            += fitsenc.o
 OBJS-$(CONFIG_FLAC_DECODER)            += flacdec.o flacdata.o flac.o
 OBJS-$(CONFIG_FLAC_ENCODER)            += flacenc.o flacdata.o flac.o vorbis_data.o
 OBJS-$(CONFIG_FLASHSV_DECODER)         += flashsv.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 8678ac2..7fe66f4 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -192,7 +192,7 @@  static void register_all(void)
     REGISTER_ENCDEC (FFV1,              ffv1);
     REGISTER_ENCDEC (FFVHUFF,           ffvhuff);
     REGISTER_DECODER(FIC,               fic);
-    REGISTER_DECODER(FITS,              fits);
+    REGISTER_ENCDEC (FITS,              fits);
     REGISTER_ENCDEC (FLASHSV,           flashsv);
     REGISTER_ENCDEC (FLASHSV2,          flashsv2);
     REGISTER_DECODER(FLIC,              flic);
diff --git a/libavcodec/fitsenc.c b/libavcodec/fitsenc.c
new file mode 100644
index 0000000..cdb662b
--- /dev/null
+++ b/libavcodec/fitsenc.c
@@ -0,0 +1,237 @@ 
+/*
+ * FITS image encoder
+ * Copyright (c) 2017 Paras Chadha
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * FITS image encoder
+ *
+ * Specification: https://fits.gsfc.nasa.gov/fits_standard.html Version 3.0
+ *
+ * RGBA images are encoded as planes in RGBA order. So, NAXIS3 is 3 or 4 for them.
+ * Also CTYPE3 = 'RGB ' is added to the header to distinguish them from 3d images.
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "avcodec.h"
+#include "bytestream.h"
+#include "internal.h"
+
+typedef struct FITSContext {
+    int first_image;
+} FITSContext;
+
+static av_cold int fits_encode_init(AVCodecContext *avctx)
+{
+    FITSContext * fitsctx = avctx->priv_data;
+    fitsctx->first_image = 1;
+    return 0;
+}
+
+static int write_keyword_value(uint8_t **bytestream, const char *keyword, int value)
+{
+    int len, ret;
+    uint8_t *header = *bytestream;
+    len = strlen(keyword);
+
+    memset(header, ' ', 80);
+    memcpy(header, keyword, len);
+    header[8] = '=';
+    header[9] = ' ';
+    header += 10;
+    ret = snprintf(header, 70, "%d", value);
+    header[ret] = ' ';
+
+    *bytestream += 80;
+    return 0;
+}
+
+static int fits_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
+                            const AVFrame *pict, int *got_packet)
+{
+    AVFrame * const p = (AVFrame *)pict;
+    FITSContext *fitsctx = avctx->priv_data;
+    uint8_t *bytestream, *bytestream_start, *ptr;
+    uint64_t header_size = 2880, data_size = 0, padded_data_size = 0;
+    int ret, bitpix, naxis, naxis3 = 1, bzero = 0, i, j, k, t, rgb = 0;
+
+    switch (avctx->pix_fmt) {
+        case AV_PIX_FMT_GRAY8:
+            bitpix = 8;
+            naxis = 2;
+            break;
+        case AV_PIX_FMT_GRAY16BE:
+            bitpix = 16;
+            naxis = 2;
+            bzero = 32768;
+            break;
+        case AV_PIX_FMT_RGB24:
+        case AV_PIX_FMT_RGBA:
+            bitpix = 8;
+            naxis = 3;
+            rgb = 1;
+            if (avctx->pix_fmt == AV_PIX_FMT_RGB24) {
+                naxis3 = 3;
+            } else {
+                naxis3 = 4;
+            }
+            break;
+        case AV_PIX_FMT_RGB48BE:
+        case AV_PIX_FMT_RGBA64BE:
+            bitpix = 16;
+            naxis = 3;
+            bzero = 32768;
+            rgb = 1;
+            if (avctx->pix_fmt == AV_PIX_FMT_RGB48BE) {
+                naxis3 = 3;
+            } else {
+                naxis3 = 4;
+            }
+            break;
+        default:
+            av_log(avctx, AV_LOG_ERROR, "unsupported pixel format\n");
+            return AVERROR(EINVAL);
+    }
+
+    data_size = (bitpix >> 3) * avctx->height * avctx->width * naxis3;
+    padded_data_size = ((data_size + 2879) / 2880 ) * 2880;
+
+    if ((ret = ff_alloc_packet2(avctx, pkt, header_size + padded_data_size, 0)) < 0)
+        return ret;
+
+    bytestream_start =
+    bytestream       = pkt->data;
+
+    if (fitsctx->first_image) {
+        memcpy(bytestream, "SIMPLE  = ", 10);
+        memset(bytestream + 10, ' ', 70);
+        bytestream[29] = 'T';
+    } else {
+        memcpy(bytestream, "XTENSION= 'IMAGE   '", 20);
+        memset(bytestream + 20, ' ', 60);
+    }
+    bytestream += 80;
+
+    write_keyword_value(&bytestream, "BITPIX", bitpix);         // no of bits per pixel
+    write_keyword_value(&bytestream, "NAXIS", naxis);           // no of dimensions of image
+    write_keyword_value(&bytestream, "NAXIS1", avctx->width);   // first dimension i.e. width
+    write_keyword_value(&bytestream, "NAXIS2", avctx->height);  // second dimension i.e. height
+
+    if (rgb)
+        write_keyword_value(&bytestream, "NAXIS3", naxis3);     // third dimension to store RGBA planes
+
+    if (!fitsctx->first_image) {
+        write_keyword_value(&bytestream, "PCOUNT", 0);
+        write_keyword_value(&bytestream, "GCOUNT", 1);
+    } else {
+        fitsctx->first_image = 0;
+    }
+
+    /*
+     * Since FITS does not support unsigned 16 bit integers,
+     * BZERO = 32768 is used to store unsigned 16 bit integers as
+     * signed integers so that it can be read properly.
+     */
+    if (bitpix == 16)
+        write_keyword_value(&bytestream, "BZERO", bzero);
+
+    if (rgb) {
+        memcpy(bytestream, "CTYPE3  = 'RGB     '", 20);
+        memset(bytestream + 20, ' ', 60);
+        bytestream += 80;
+    }
+
+    memcpy(bytestream, "END", 3);
+    memset(bytestream + 3, ' ', 77);
+    bytestream += 80;
+
+    t = header_size - (bytestream - bytestream_start);
+    memset(bytestream, ' ', t);
+    bytestream += t;
+
+    if (rgb) {
+        switch (avctx->pix_fmt) {
+            case AV_PIX_FMT_RGB24:
+            case AV_PIX_FMT_RGBA:
+                for (k = 0; k < naxis3; k++) {
+                    for (i = 0; i < avctx->height; i++) {
+                        ptr = p->data[0] + (avctx->height - i - 1) * p->linesize[0] + k;
+                        for (j = 0; j < avctx->width; j++) {
+                            bytestream_put_byte(&bytestream, ptr[0]);
+                            ptr += naxis3;
+                        }
+                    }
+                }
+                break;
+            case AV_PIX_FMT_RGB48BE:
+            case AV_PIX_FMT_RGBA64BE:
+                for (k = 0; k < naxis3; k++) {
+                    for (i = 0; i < avctx->height; i++) {
+                        ptr = p->data[0] + (avctx->height - i - 1) * p->linesize[0] + k * 2;
+                        for (j = 0; j < avctx->width; j++) {
+                            bytestream_put_be16(&bytestream, AV_RB16(ptr) - bzero);
+                            ptr += naxis3 * 2;
+                        }
+                    }
+                }
+                break;
+        }
+    } else {
+        for (i = 0; i < avctx->height; i++) {
+            ptr = p->data[0] + (avctx->height - i - 1) * p->linesize[0];
+            if (bitpix == 16) {
+                for (j = 0; j < avctx->width; j++) {
+                    bytestream_put_be16(&bytestream, AV_RB16(ptr) - bzero);
+                    ptr += 2;
+                }
+            } else {
+                memcpy(bytestream, ptr, avctx->width);
+                bytestream += avctx->width;
+            }
+        }
+    }
+
+    t = padded_data_size - data_size;
+    memset(bytestream, 0, t);
+    bytestream += t;
+
+    pkt->size   = bytestream - bytestream_start;
+    pkt->flags |= AV_PKT_FLAG_KEY;
+    *got_packet = 1;
+
+    return 0;
+}
+
+AVCodec ff_fits_encoder = {
+    .name           = "fits",
+    .long_name      = NULL_IF_CONFIG_SMALL("Flexible Image Transport System"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_FITS,
+    .priv_data_size = sizeof(FITSContext),
+    .init           = fits_encode_init,
+    .encode2        = fits_encode_frame,
+    .pix_fmts       = (const enum AVPixelFormat[]) { AV_PIX_FMT_RGBA64BE,
+                                                 AV_PIX_FMT_RGB48BE,
+                                                 AV_PIX_FMT_RGBA,
+                                                 AV_PIX_FMT_RGB24,
+                                                 AV_PIX_FMT_GRAY16BE,
+                                                 AV_PIX_FMT_GRAY8,
+                                                 AV_PIX_FMT_NONE },
+};
diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c
index 1297b1a..25283cc 100644
--- a/libavformat/img2enc.c
+++ b/libavformat/img2enc.c
@@ -237,6 +237,7 @@  AVOutputFormat ff_image2_muxer = {
 AVOutputFormat ff_image2pipe_muxer = {
     .name           = "image2pipe",
     .long_name      = NULL_IF_CONFIG_SMALL("piped image2 sequence"),
+    .extensions     = "fits",
     .priv_data_size = sizeof(VideoMuxData),
     .video_codec    = AV_CODEC_ID_MJPEG,
     .write_header   = write_header,