private BufferedImage rasterizeConvoLayers(@NonNull List<INDArray> tensors3D, BufferedImage sourceImage) {
        long width = 0;
        long height = 0;

        int border = 1;
        int padding_row = 2;
        int padding_col = 80;

        /*
            We determine height of joint output image. We assume that first position holds maximum dimensionality
         */
        val shape = tensors3D.get(0).shape();
        val numImages = shape[0];
        height = (shape[2]);
        width = (shape[1]);
        //        log.info("Output image dimensions: {height: " + height + ", width: " + width + "}");
        int maxHeight = 0; //(height + (border * 2 ) + padding_row) * numImages;
        int totalWidth = 0;
        int iOffset = 1;

        Orientation orientation = Orientation.LANDSCAPE;
        /*
            for debug purposes we'll use portait only now
         */
        if (tensors3D.size() > 3) {
            orientation = Orientation.PORTRAIT;
        }



        List<BufferedImage> images = new ArrayList<>();
        for (int layer = 0; layer < tensors3D.size(); layer++) {
            INDArray tad = tensors3D.get(layer);
            int zoomed = 0;

            BufferedImage image = null;
            if (orientation == Orientation.LANDSCAPE) {
                maxHeight = (int) ((height + (border * 2) + padding_row) * numImages);
                image = renderMultipleImagesLandscape(tad, maxHeight, (int) width, (int) height);
                totalWidth += image.getWidth() + padding_col;
            } else if (orientation == Orientation.PORTRAIT) {
                totalWidth = (int) ((width + (border * 2) + padding_row) * numImages);
                image = renderMultipleImagesPortrait(tad, totalWidth, (int) width, (int) height);
                maxHeight += image.getHeight() + padding_col;
            }

            images.add(image);
        }

        if (orientation == Orientation.LANDSCAPE) {
            // append some space for arrows
            totalWidth += padding_col * 2;
        } else if (orientation == Orientation.PORTRAIT) {
            maxHeight += padding_col * 2;
            maxHeight += sourceImage.getHeight() + (padding_col * 2);
        }

        BufferedImage output = new BufferedImage(totalWidth, maxHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics2D = output.createGraphics();

        graphics2D.setPaint(bgColor);
        graphics2D.fillRect(0, 0, output.getWidth(), output.getHeight());

        BufferedImage singleArrow = null;
        BufferedImage multipleArrows = null;

        /*
            We try to add nice flow arrow here
         */
        try {

            if (orientation == Orientation.LANDSCAPE) {
                try {
                    ClassPathResource resource = new ClassPathResource("arrow_sing.PNG");
                    ClassPathResource resource2 = new ClassPathResource("arrow_mul.PNG");

                    singleArrow = ImageIO.read(resource.getInputStream());
                    multipleArrows = ImageIO.read(resource2.getInputStream());
                } catch (Exception e) {
                }

                graphics2D.drawImage(sourceImage, (padding_col / 2) - (sourceImage.getWidth() / 2),
                                (maxHeight / 2) - (sourceImage.getHeight() / 2), null);

                graphics2D.setPaint(borderColor);
                graphics2D.drawRect((padding_col / 2) - (sourceImage.getWidth() / 2),
                                (maxHeight / 2) - (sourceImage.getHeight() / 2), sourceImage.getWidth(),
                                sourceImage.getHeight());

                iOffset += sourceImage.getWidth();

                if (singleArrow != null)
                    graphics2D.drawImage(singleArrow, iOffset + (padding_col / 2) - (singleArrow.getWidth() / 2),
                                    (maxHeight / 2) - (singleArrow.getHeight() / 2), null);
            } else {
                try {
                    ClassPathResource resource = new ClassPathResource("arrow_singi.PNG");
                    ClassPathResource resource2 = new ClassPathResource("arrow_muli.PNG");

                    singleArrow = ImageIO.read(resource.getInputStream());
                    multipleArrows = ImageIO.read(resource2.getInputStream());
                } catch (Exception e) {
                }

                graphics2D.drawImage(sourceImage, (totalWidth / 2) - (sourceImage.getWidth() / 2),
                                (padding_col / 2) - (sourceImage.getHeight() / 2), null);

                graphics2D.setPaint(borderColor);
                graphics2D.drawRect((totalWidth / 2) - (sourceImage.getWidth() / 2),
                                (padding_col / 2) - (sourceImage.getHeight() / 2), sourceImage.getWidth(),
                                sourceImage.getHeight());

                iOffset += sourceImage.getHeight();
                if (singleArrow != null)
                    graphics2D.drawImage(singleArrow, (totalWidth / 2) - (singleArrow.getWidth() / 2),
                                    iOffset + (padding_col / 2) - (singleArrow.getHeight() / 2), null);

            }
            iOffset += padding_col;
        } catch (Exception e) {
            // if we can't load images - ignore them
        }



        /*
            now we merge all images into one big image with some offset
        */


        for (int i = 0; i < images.size(); i++) {
            BufferedImage curImage = images.get(i);
            if (orientation == Orientation.LANDSCAPE) {
                // image grows from left to right
                graphics2D.drawImage(curImage, iOffset, 1, null);
                iOffset += curImage.getWidth() + padding_col;

                if (singleArrow != null && multipleArrows != null) {
                    if (i < images.size() - 1) {
                        // draw multiple arrows here
                        if (multipleArrows != null)
                            graphics2D.drawImage(multipleArrows,
                                            iOffset - (padding_col / 2) - (multipleArrows.getWidth() / 2),
                                            (maxHeight / 2) - (multipleArrows.getHeight() / 2), null);
                    } else {
                        // draw single arrow
                        //    graphics2D.drawImage(singleArrow, iOffset - (padding_col / 2) - (singleArrow.getWidth() / 2), (maxHeight / 2) - (singleArrow.getHeight() / 2), null);
                    }
                }
            } else if (orientation == Orientation.PORTRAIT) {
                // image grows from top to bottom
                graphics2D.drawImage(curImage, 1, iOffset, null);
                iOffset += curImage.getHeight() + padding_col;

                if (singleArrow != null && multipleArrows != null) {
                    if (i < images.size() - 1) {
                        // draw multiple arrows here
                        if (multipleArrows != null)
                            graphics2D.drawImage(multipleArrows, (totalWidth / 2) - (multipleArrows.getWidth() / 2),
                                            iOffset - (padding_col / 2) - (multipleArrows.getHeight() / 2), null);
                    } else {
                        // draw single arrow
                        //   graphics2D.drawImage(singleArrow, (totalWidth / 2) - (singleArrow.getWidth() / 2),  iOffset - (padding_col / 2) - (singleArrow.getHeight() / 2) , null);
                    }
                }
            }
        }

        return output;
    }
private BufferedImage rasterizeConvoLayers ( @ NonNull List < INDArray > tensors3D , BufferedImage sourceImage ) { long width = 0 ; long height = 0 ; int border = 1 ; int padding_row = 2 ; int padding_col = 80 ; val shape = tensors3D . get ( 0 ) . shape ( ) ; val numImages = shape [ 0 ] ; height = ( shape [ 2 ] ) ; width = ( shape [ 1 ] ) ; int maxHeight = 0 ; int totalWidth = 0 ; int iOffset = 1 ; Orientation orientation = Orientation . LANDSCAPE ; if ( tensors3D . size ( ) > 3 ) { orientation = Orientation . PORTRAIT ; } List < BufferedImage > images = new ArrayList < > ( ) ; for ( int layer = 0 ; layer < tensors3D . size ( ) ; layer ++ ) { INDArray tad = tensors3D . get ( layer ) ; int zoomed = 0 ; BufferedImage image = null ; if ( orientation == Orientation . LANDSCAPE ) { maxHeight = ( int ) ( ( height + ( border * 2 ) + padding_row ) * numImages ) ; image = renderMultipleImagesLandscape ( tad , maxHeight , ( int ) width , ( int ) height ) ; totalWidth += image . getWidth ( ) + padding_col ; } else if ( orientation == Orientation . PORTRAIT ) { totalWidth = ( int ) ( ( width + ( border * 2 ) + padding_row ) * numImages ) ; image = renderMultipleImagesPortrait ( tad , totalWidth , ( int ) width , ( int ) height ) ; maxHeight += image . getHeight ( ) + padding_col ; } images . add ( image ) ; } if ( orientation == Orientation . LANDSCAPE ) { totalWidth += padding_col * 2 ; } else if ( orientation == Orientation . PORTRAIT ) { maxHeight += padding_col * 2 ; maxHeight += sourceImage . getHeight ( ) + ( padding_col * 2 ) ; } BufferedImage output = new BufferedImage ( totalWidth , maxHeight , BufferedImage . TYPE_INT_RGB ) ; Graphics2D graphics2D = output . createGraphics ( ) ; graphics2D . setPaint ( bgColor ) ; graphics2D . fillRect ( 0 , 0 , output . getWidth ( ) , output . getHeight ( ) ) ; BufferedImage singleArrow = null ; BufferedImage multipleArrows = null ; try { if ( orientation == Orientation . LANDSCAPE ) { try { ClassPathResource resource = new ClassPathResource ( "arrow_sing.PNG" ) ; ClassPathResource resource2 = new ClassPathResource ( "arrow_mul.PNG" ) ; singleArrow = ImageIO . read ( resource . getInputStream ( ) ) ; multipleArrows = ImageIO . read ( resource2 . getInputStream ( ) ) ; } catch ( Exception e ) { } graphics2D . drawImage ( sourceImage , ( padding_col / 2 ) - ( sourceImage . getWidth ( ) / 2 ) , ( maxHeight / 2 ) - ( sourceImage . getHeight ( ) / 2 ) , null ) ; graphics2D . setPaint ( borderColor ) ; graphics2D . drawRect ( ( padding_col / 2 ) - ( sourceImage . getWidth ( ) / 2 ) , ( maxHeight / 2 ) - ( sourceImage . getHeight ( ) / 2 ) , sourceImage . getWidth ( ) , sourceImage . getHeight ( ) ) ; iOffset += sourceImage . getWidth ( ) ; if ( singleArrow != null ) graphics2D . drawImage ( singleArrow , iOffset + ( padding_col / 2 ) - ( singleArrow . getWidth ( ) / 2 ) , ( maxHeight / 2 ) - ( singleArrow . getHeight ( ) / 2 ) , null ) ; } else { try { ClassPathResource resource = new ClassPathResource ( "arrow_singi.PNG" ) ; ClassPathResource resource2 = new ClassPathResource ( "arrow_muli.PNG" ) ; singleArrow = ImageIO . read ( resource . getInputStream ( ) ) ; multipleArrows = ImageIO . read ( resource2 . getInputStream ( ) ) ; } catch ( Exception e ) { } graphics2D . drawImage ( sourceImage , ( totalWidth / 2 ) - ( sourceImage . getWidth ( ) / 2 ) , ( padding_col / 2 ) - ( sourceImage . getHeight ( ) / 2 ) , null ) ; graphics2D . setPaint ( borderColor ) ; graphics2D . drawRect ( ( totalWidth / 2 ) - ( sourceImage . getWidth ( ) / 2 ) , ( padding_col / 2 ) - ( sourceImage . getHeight ( ) / 2 ) , sourceImage . getWidth ( ) , sourceImage . getHeight ( ) ) ; iOffset += sourceImage . getHeight ( ) ; if ( singleArrow != null ) graphics2D . drawImage ( singleArrow , ( totalWidth / 2 ) - ( singleArrow . getWidth ( ) / 2 ) , iOffset + ( padding_col / 2 ) - ( singleArrow . getHeight ( ) / 2 ) , null ) ; } iOffset += padding_col ; } catch ( Exception e ) { } for ( int i = 0 ; i < images . size ( ) ; i ++ ) { BufferedImage curImage = images . get ( i ) ; if ( orientation == Orientation . LANDSCAPE ) { graphics2D . drawImage ( curImage , iOffset , 1 , null ) ; iOffset += curImage . getWidth ( ) + padding_col ; if ( singleArrow != null && multipleArrows != null ) { if ( i < images . size ( ) - 1 ) { if ( multipleArrows != null ) graphics2D . drawImage ( multipleArrows , iOffset - ( padding_col / 2 ) - ( multipleArrows . getWidth ( ) / 2 ) , ( maxHeight / 2 ) - ( multipleArrows . getHeight ( ) / 2 ) , null ) ; } else { } } } else if ( orientation == Orientation . PORTRAIT ) { graphics2D . drawImage ( curImage , 1 , iOffset , null ) ; iOffset += curImage . getHeight ( ) + padding_col ; if ( singleArrow != null && multipleArrows != null ) { if ( i < images . size ( ) - 1 ) { if ( multipleArrows != null ) graphics2D . drawImage ( multipleArrows , ( totalWidth / 2 ) - ( multipleArrows . getWidth ( ) / 2 ) , iOffset - ( padding_col / 2 ) - ( multipleArrows . getHeight ( ) / 2 ) , null ) ; } else { } } } } return output ; }
We visualize set of tensors as vertically aligned set of patches

@param tensors3D list of tensors retrieved from convolution