Example #1
0
        public static Metadata GetMetadata(HttpClient client, ILogger log, Uri imageUri, int defaultTileWidth, string requestId)
        {
            Ckdu_codestream codestream = new Ckdu_codestream();

            try
            {
                using (var family_src = new JPEG2000Source(log, requestId))
                    using (var wrapped_src = new Cjpx_source())
                        using (var jp2_source = new Cjp2_source())
                        {
                            family_src.Open(client, imageUri, false);

                            int success = wrapped_src.open(family_src, true);
                            if (success < 1)
                            {
                                family_src.close();
                                wrapped_src.close();
                                throw new IOException("could not be read as JPEG2000");
                            }

                            jp2_source.open(family_src);
                            while (!jp2_source.read_header())
                            {
                                ;
                            }

                            int ref_component = 0;

                            codestream.create(wrapped_src.access_codestream(ref_component).open_stream());

                            Ckdu_dims image_dims = new Ckdu_dims();
                            codestream.get_dims(ref_component, image_dims);
                            Ckdu_coords image_size = image_dims.access_size();

                            int levels    = codestream.get_min_dwt_levels();
                            var imageSize = new SKPoint(image_size.x, image_size.y);

                            Ckdu_dims tile_dims = new Ckdu_dims();
                            codestream.get_tile_dims(new Ckdu_coords(0, 0), ref_component, tile_dims);

                            var   tileSize  = new SKPoint(tile_dims.access_size().x, tile_dims.access_size().y);
                            int[] precincts = new int[levels];
                            // get precinct info
                            using (var param = codestream.access_siz().access_cluster("COD"))
                            {
                                bool usePrecincts = false;
                                param.get("Cuse_precincts", 0, 0, ref usePrecincts);
                                if (usePrecincts)
                                {
                                    for (int i = 0; i < levels; i++)
                                    {
                                        param.get("Cprecincts", i, 0, ref precincts[i]);
                                    }
                                    tileSize = new SKPoint(precincts[0], precincts[0]);
                                }
                                // if no precincts defined, we should fall back on default size if it reports
                                // tile size as being image size
                                else if (tileSize == imageSize)
                                {
                                    tileSize = new SKPoint(defaultTileWidth, defaultTileWidth);
                                }
                            }

                            tile_dims.Dispose();
                            image_size.Dispose();
                            image_dims.Dispose();

                            if (codestream.exists())
                            {
                                codestream.destroy();
                            }
                            family_src.close();

                            return(new Metadata
                            {
                                Width = Convert.ToInt32(imageSize.X),
                                Height = Convert.ToInt32(imageSize.Y),
                                ScalingLevels = levels,
                                TileWidth = Convert.ToInt32(tileSize.X),
                                TileHeight = Convert.ToInt32(tileSize.Y)
                            });
                        }
            }
            finally
            {
                if (codestream.exists())
                {
                    codestream.destroy();
                }
            }
        }
Example #2
0
        public static (ProcessState state, SKImage image) ExpandRegion(HttpClient client, ILogger Log, Uri imageUri, ImageRequest request, bool allowSizeAboveFull, C.ImageQuality quality)
        {
            using (var compositor = new BitmapCompositor())
                using (var env = new Ckdu_thread_env())
                    using (var family_src = new JPEG2000Source(Log, request.RequestId))
                        using (var wrapped_src = new Cjpx_source())
                            using (var imageDimensions = new Ckdu_dims())
                                using (var limiter = new Ckdu_quality_limiter(quality.WeightedRMSE))
                                {
                                    Ckdu_codestream codestream = new Ckdu_codestream();
                                    try
                                    {
                                        int num_threads = Ckdu_global_funcs.kdu_get_num_processors();
                                        env.create();

                                        for (int nt = 1; nt < num_threads; nt++)
                                        {
                                            if (!env.add_thread())
                                            {
                                                num_threads = nt;
                                            }
                                        }
                                        Log.Debug("Created {@NumThreads} threads", num_threads);
                                        family_src.Open(client, imageUri, false);

                                        int success = wrapped_src.open(family_src, true);
                                        if (success < 1)
                                        {
                                            family_src.close();
                                            wrapped_src.close();
                                            throw new IOException("could not be read as JPEG2000");
                                        }

                                        if (wrapped_src != null)
                                        {
                                            compositor.create(wrapped_src);
                                        }

                                        compositor.set_thread_env(env, null);
                                        compositor.get_total_composition_dims(imageDimensions);
                                        Ckdu_coords imageSize     = imageDimensions.access_size();
                                        Ckdu_coords imagePosition = imageDimensions.access_pos();

                                        float imageScale = 1;

                                        int ref_component = 0;

                                        if (wrapped_src == null)
                                        {
                                            throw new IOException("could not be read as JPEG2000");
                                        }
                                        codestream.create(wrapped_src.access_codestream(ref_component).open_stream());
                                        codestream.set_fast();

                                        int originalWidth, originalHeight;
                                        using (Ckdu_dims original_dims = new Ckdu_dims())
                                        {
                                            codestream.get_dims(ref_component, original_dims);
                                            using (Ckdu_coords original_size = original_dims.access_size())
                                            {
                                                originalWidth  = original_size.x;
                                                originalHeight = original_size.y;
                                            }
                                        }

                                        var kt             = codestream.open_tile(new Ckdu_coords(0, 0), null);
                                        var quality_layers = codestream.get_max_tile_layers();
                                        var layers         = quality.MaxQualityLayers;
                                        if (layers < 0)
                                        {
                                            layers = quality_layers;
                                        }
                                        else if (layers == 0)
                                        {
                                            layers = Convert.ToInt32(Math.Ceiling(quality_layers / 2.0));
                                        }

                                        ushort ppi_x = 96, ppi_y = 96;

                                        if (wrapped_src.access_layer(0).exists())
                                        {
                                            var accessLayer = wrapped_src.access_layer(0);
                                            var resolution  = accessLayer.access_resolution();
                                            if (resolution.exists())
                                            {
                                                bool  for_display     = false;
                                                float ypels_per_metre = resolution.get_resolution(for_display);
                                                if (ypels_per_metre <= 0.0F)
                                                {
                                                    for_display     = true;
                                                    ypels_per_metre = resolution.get_resolution(for_display);
                                                    if (ypels_per_metre <= 0.0F)
                                                    {
                                                        ypels_per_metre = 1.0F;
                                                    }
                                                }

                                                float xpels_per_metre = ypels_per_metre * resolution.get_aspect_ratio(for_display);

                                                ppi_x = Convert.ToUInt16(Math.Ceiling(xpels_per_metre * 0.0254));
                                                ppi_y = Convert.ToUInt16(Math.Ceiling(ypels_per_metre * 0.0254));
                                            }
                                        }

                                        var state = ImageRequestInterpreter.GetInterpretedValues(request, originalWidth, originalHeight, allowSizeAboveFull);
                                        state.HorizontalResolution = ppi_x;
                                        state.VerticalResolution   = ppi_y;
                                        Log.Debug("Image request {@Request}", state);
                                        var scale     = state.OutputScale;
                                        var scaleDiff = 0f;
                                        imageScale = state.ImageScale;

                                        // needs to be able to handle regions
                                        imageSize.x = Convert.ToInt32(Math.Round(state.RegionWidth / scale));
                                        imageSize.y = Convert.ToInt32(Math.Round(state.RegionHeight / scale));

                                        imagePosition.x = state.StartX;
                                        imagePosition.y = state.StartY;

                                        Ckdu_dims extracted_dims = new Ckdu_dims();
                                        extracted_dims.assign(imageDimensions);
                                        extracted_dims.access_size().x = Convert.ToInt32(Math.Round(imageSize.x * imageScale));
                                        extracted_dims.access_size().y = Convert.ToInt32(Math.Round(imageSize.y * imageScale));
                                        var viewSize = extracted_dims.access_size();
                                        Log.Debug("add_ilayer extracted dimension: {@AccessPos}, {@AccessSize}, {@IsEmpty}, {@Scale}", extracted_dims.access_pos(), extracted_dims.access_size(), extracted_dims.is_empty(), scale);
                                        compositor.add_ilayer(0, extracted_dims, extracted_dims);

                                        compositor.set_scale(false, false, false, scale);

                                        compositor.get_total_composition_dims(extracted_dims);
                                        Log.Debug("get_total_composition_dims extracted dimension: {@AccessPos}, {@AccessSize}, {@IsEmpty}, {@Scale}", extracted_dims.access_pos(), extracted_dims.access_size(), extracted_dims.is_empty(), scale);
                                        // check if the access size is the expected size as floating point rounding errors
                                        // might occur
                                        const float roundingValue = 0.0001f;
                                        if (((scale - roundingValue) * imageSize.x > 1 && (scale - roundingValue) * imageSize.y > 1) &&
                                            (scale * imageSize.x != viewSize.x ||
                                             scale * imageSize.y != viewSize.y))
                                        {
                                            // attempt to correct by shifting rounding down
                                            compositor.set_scale(false, false, false, 1, scale - roundingValue);

                                            compositor.get_total_composition_dims(extracted_dims);
                                            extracted_dims.access_size().x = Convert.ToInt32(Math.Round(imageSize.x * imageScale));
                                            extracted_dims.access_size().y = Convert.ToInt32(Math.Round(imageSize.y * imageScale));
                                            viewSize = extracted_dims.access_size();
                                        }

                                        var checkScale = compositor.check_invalid_scale_code();
                                        if (0 != checkScale)
                                        {
                                            // we've come up with a scale factor which is (probably) way too small
                                            // ask Kakadu to come up with a valid one that's close
                                            var minScale = Ckdu_global.KDU_COMPOSITOR_SCALE_TOO_SMALL == checkScale ? scale : 0;
                                            var maxScale = Ckdu_global.KDU_COMPOSITOR_SCALE_TOO_LARGE == checkScale ? scale : 1;

                                            var optimal_scale = compositor.find_optimal_scale(extracted_dims, 0, minScale, maxScale);
                                            scaleDiff = Ckdu_global.KDU_COMPOSITOR_SCALE_TOO_SMALL == checkScale ? optimal_scale - scale : scale - optimal_scale;
                                            scale     = optimal_scale;
                                            compositor.set_scale(false, false, false, scale);
                                            compositor.get_total_composition_dims(extracted_dims);
                                        }
                                        viewSize = extracted_dims.access_size();
                                        Log.Debug("Extracted dimension: {@AccessPos}, {@AccessSize}, {@IsEmpty}, {@Scale}", extracted_dims.access_pos(), extracted_dims.access_size(), extracted_dims.is_empty(), scale);
                                        compositor.set_buffer_surface(extracted_dims);
                                        compositor.set_quality_limiting(limiter, quality.OutputDpi, quality.OutputDpi);
                                        Log.Debug("Set quality limiting: {@Limiter}, {@HorizontalPPI}, {@VerticalPPI}", limiter, quality.OutputDpi, quality.OutputDpi);
                                        compositor.set_max_quality_layers(layers);
                                        Log.Debug("Set max quality layers: {@Layers}", layers);
                                        var compositorBuffer = compositor.GetCompositionBitmap(extracted_dims);

                                        using (Ckdu_dims newRegion = new Ckdu_dims())
                                        {
                                            // we're only interested in the final composited image
                                            while (compositor.process(256000, newRegion))
                                            {
                                            }

                                            using (var bmp = compositorBuffer.AcquireBitmap())
                                            {
                                                return(state, SKImage.FromBitmap(bmp));
                                            }
                                        }
                                    }
                                    finally
                                    {
                                        if (codestream.exists())
                                        {
                                            codestream.destroy();
                                        }

                                        if (env.exists())
                                        {
                                            env.destroy();
                                        }

                                        if (family_src != null)
                                        {
                                            family_src.close();
                                        }
                                        else if (wrapped_src != null)
                                        {
                                            wrapped_src.close();
                                        }
                                    }
                                }
        }