[FFmpeg-devel] [PATCH] Add FITS Encoder
James Almer
jamrial at gmail.com
Thu Jul 20 23:15:07 EEST 2017
On 7/20/2017 4:46 PM, Paras Chadha wrote:
> Signed-off-by: Paras Chadha <paraschadha18 at 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
> - at item FITS @tab @tab X
> + at 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 at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
More information about the ffmpeg-devel
mailing list