Neatly Display Images of Different Sizes Sequentially in The Same UI Element

Neatly display images of different sizes sequentially in the same UI element

You were really close with uiOutput() which is necessary because you want the layout of the UI to change. Whereas what you have uncommented only swaps the images in and out but keeps the UI layout constant.

library(shiny)
library(png)

ui <- fluidPage(
tags$head(tags$link(rel="stylesheet", type="text/css", href="stylesheet.css")),
selectInput("size", label="Size:", choices=c("small", "large")),

# don't use imageOutput() here because the UI will not be able to change
#imageOutput("image"),
# instead use uiOutput() which will allow you to update the UI itself
# that is, you will be able to update the height of the image div reactively
uiOutput("imageUI"),
textOutput("info")
)

server <- function(input, output) {
imgFileName <- reactive({
paste0("./www/", input$size, ".png")
})

imgFile <- reactive({
readPNG(imgFileName(), info=TRUE)
})

imgSize <- reactive({
info <- unlist(stringr::str_split(attr(imgFile(), "info")$dim, stringr::fixed(" ")))
info <- paste0(info, "px")
names(info) <- c("width", "height")
info <- as.list(info)
info
})

output$info <- renderText({
paste0("Height: ", imgSize()$height, "; Width: ", imgSize()$width)
})

output$image <- renderImage({
list(
src=imgFileName(),
contentType="image/png",
width=imgSize()$width,
height=imgSize()$height,
alt=paste0("A ", input$size, " image"),
class="myImageClass"
)
})

# update the UI here by changing the height of the image div
output$imageUI <- renderUI({
imageOutput("image", height = imgSize()$height)
})
}

shinyApp(ui, server)

Flexible width and height of pre-rendered image using Shiny and renderImage()

Figured it out myself. As far as I know, there is no way to handle this in R. Instead, use your style sheet (e.g., bootstrap.css) by adding something like this:

   img {
border: 1;
max-width: 100%;
}
element.style {
width: 33.33%;
}

I was also successful in getting this to work with a leaflet map in Shiny. This is what I did in my style sheet:

div#myChart2 {
width: inherit;
}

How to display dynamically fetched image on client end in react?

There's a lot going on in your code. You've some problems with asynchronous calls and also not all functions are bound to this. I would suggest to convert your code to async/await to make it more readable and use named functions, because they are automatically bound to this.

getAllEventsData = async () => {
const response = await axios({
method: 'POST',
url: '/api/Account/GetAllEventsData',
contentType: 'application/json',
data: {},
})
console.log(response);
this.setState({
eventDetail: response.data
});
this.getAllImages()
}

In getAllImages it's neccessary to wait for setState to be finished, because getImageBlobs is using the state value and setState is asynchronous.

getAllImages = async () => {
const response = await axios({
method: 'POST',
url: '/api/Account/GetAllEventImages',
contentType: 'application/json',
data: {},
})
console.log(response);
await this.setState({
images: response.data
});
this.getImageBlobs();
}

In getImageBlobs there is a loop over a asynchronous function. So you need to wait till all calls are finished. Promise.all will help here. Also imageArr is some magically created global variable which is bad practise. Better return the value from fetchEventImage and collect it in a local array. I replaced the for-loop with a map, which basically does the same, but looks cooler and cleaner and returns an array with all the Blob-URLs as Promise. Also destructuring images from this.state helps cleaning up the code.

getImageBlobs = async () => {
const { images } = this.state
console.log("Image Objects:", images);
const imageArr = await Promise.all(
images.map(image => this.fetchEventImage(image.image_path))
this.setState({
imagesInBlob: imageArr
});
console.log("Images in Blob:", this.state.imagesInBlob);
}

Also in fetchEventImage async/await looks cleaner and with try/catch you can do the same for error handling. As stated, this function will now return the created Blob-URL.

fetchEventImage = async (imagePath) => {
var path = {
ImagePath: imagePath
}
try {
const response = await axios({
method: 'POST',
url: '/api/ImageFetch/FetchEventImages',
responseType: 'blob',// important
headers: {
'Content-Type': 'application/json'
},
data: path
})
const url = window.URL.createObjectURL(new Blob([response.data]));
console.log("URL: ", url);
return url
} catch(error) {
console.log("Status:", error.response.status);
console.log("Data:", error.response.data);
}
}

I haven't tested the refactored code. Maybe there are some minor mistakes, but I think in general it should work.

How to show images in a large frequency in JavaFX?

Your code updates the UI from a background thread, which is definitely not allowed. You need to ensure you update from the FX Application Thread. You also want to try to "throttle" the actual UI updates to occur no more than once per JavaFX frame rendering. The easiest way to do this is with an AnimationTimer, whose handle() method is invoked each time a frame is rendered.

Here's a version of your code which does that:

import java.awt.Dimension;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicReference;

import org.tc33.jheatchart.HeatChart;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {
ImageView imageView = new ImageView();
final int scale = 15;

@Override
public void start(Stage primaryStage) {

AtomicReference<BufferedImage> image = new AtomicReference<>();

Thread generator = new Thread(() -> {
int col = 0;
LinkedList<Long> fps = new LinkedList<>();
while (true) {
fps.add(System.currentTimeMillis());
double[][] matrix = new double[48][128];
for (int i = 0; i < 48; i++) {
for (int j = 0; j < 128; j++) {
matrix[i][j] = col == j ? Math.random() : 0;
}
}
col = (col + 1) % 128;

HeatChart heatChart = new HeatChart(matrix, 0, 1);
heatChart.setShowXAxisValues(false);
heatChart.setShowYAxisValues(false);
heatChart.setLowValueColour(java.awt.Color.black);
heatChart.setHighValueColour(java.awt.Color.white);
heatChart.setAxisThickness(0);
heatChart.setChartMargin(0);
heatChart.setCellSize(new Dimension(1, 1));

long currentTime = System.currentTimeMillis();
fps.removeIf(elem -> currentTime - elem > 1000);
System.out.println(fps.size());

image.set((BufferedImage) scale(heatChart.getChartImage(), scale));

}
});

VBox box = new VBox();
box.getChildren().add(imageView);

Scene scene = new Scene(box, 1920, 720);
primaryStage.setScene(scene);
primaryStage.show();

generator.setDaemon(true);
generator.start();

AnimationTimer animation = new AnimationTimer() {

@Override
public void handle(long now) {
BufferedImage img = image.getAndSet(null);
if (img != null) {
imageView.setImage(SwingFXUtils.toFXImage(img, null));
}
}

};

animation.start();
}

public static void main(String[] args) {
launch(args);
}

private static Image scale(Image image, int scale) {
BufferedImage res = new BufferedImage(image.getWidth(null) * scale, image.getHeight(null) * scale,
BufferedImage.TYPE_INT_ARGB);
AffineTransform at = new AffineTransform();
at.scale(scale, scale);
AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);

return scaleOp.filter((BufferedImage) image, res);
}
}

Using the AtomicReference to wrap the buffered image ensures that it is safely shared between the two threads.

On my machine, this generates about 130 images per second; note that not all are displayed, as only the latest one is shown each time the JavaFX graphics framework displays a frame (which is typically throttled at 60fps).

If you want to ensure you show all images that are generated, i.e. you throttle the image generation by the JavaFX framerate, then you can use a BlockingQueue to store the images:

    // AtomicReference<BufferedImage> image = new AtomicReference<>();

// Size of the queue is a trade-off between memory consumption
// and smoothness (essentially works as a buffer size)
BlockingQueue<BufferedImage> image = new ArrayBlockingQueue<>(5);

// ...

// image.set((BufferedImage) scale(heatChart.getChartImage(), scale));
try {
image.put((BufferedImage) scale(heatChart.getChartImage(), scale));
} catch (InterruptedException exc) {
Thread.currentThread.interrupt();
}

and

        @Override
public void handle(long now) {
BufferedImage img = image.poll();
if (img != null) {
imageView.setImage(SwingFXUtils.toFXImage(img, null));
}
}

The code is pretty inefficient, as you generate a new matrix, new HeatChart, etc, on every iteration. This causes many objects to be created on the heap and quickly discarded, which can cause the GC to be run too often, particularly on a small-memory machine. That said, I ran this with the maximum heap size set at 64MB, (-Xmx64m), and it still performed fine. You may be able to optimize the code, but using the AnimationTimer as shown above, generating images more quickly will not cause any additional stress on the JavaFX framework. I would recommend investigating using the mutability of HeatChart (i.e. setZValues()) to avoid creating too many objects, and/or using PixelBuffer to directly write data to the image view (this would need to be done on the FX Application Thread).

Here's a different example, which (almost) completely minimizes object creation, using one off-screen int[] array to compute data, and one on-screen int[] array to display it. There's a little low-level threading details to ensure the on-screen array is only seen in a consistent state. The on-screen array is used to underly a PixelBuffer, which in turn is used for a WritableImage.

This class generates the image data:

import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;

public class ImageGenerator {

private final int width;
private final int height;

// Keep two copies of the data: one which is not exposed
// that we modify on the fly during computation;
// another which we expose publicly.
// The publicly exposed one can be viewed only in a complete
// state if operations on it are synchronized on this object.
private final int[] privateData ;
private final int[] publicData ;

private final long[] frameTimes ;
private int currentFrameIndex ;
private final AtomicLong averageGenerationTime ;

private final ReentrantLock lock ;

private static final double TWO_PI = 2 * Math.PI;
private static final double PI_BY_TWELVE = Math.PI / 12; // 15 degrees

public ImageGenerator(int width, int height) {
super();
this.width = width;
this.height = height;
privateData = new int[width * height];
publicData = new int[width * height];

lock = new ReentrantLock();

this.frameTimes = new long[100];
this.averageGenerationTime = new AtomicLong();
}

public void generateImage(double angle) {

// compute in private data copy:

int minDim = Math.min(width, height);
int minR2 = minDim * minDim / 4;
for (int x = 0; x < width; x++) {
int xOff = x - width / 2;
int xOff2 = xOff * xOff;
for (int y = 0; y < height; y++) {

int index = x + y * width;

int yOff = y - height / 2;
int yOff2 = yOff * yOff;
int r2 = xOff2 + yOff2;
if (r2 > minR2) {
privateData[index] = 0xffffffff; // white
} else {
double theta = Math.atan2(yOff, xOff);
double delta = Math.abs(theta - angle);
if (delta > TWO_PI - PI_BY_TWELVE) {
delta = TWO_PI - delta;
}
if (delta < PI_BY_TWELVE) {
int green = (int) (255 * (1 - delta / PI_BY_TWELVE));
privateData[index] = (0xff << 24) | (green << 8); // green, fading away from center
} else {
privateData[index] = 0xff << 24; // black
}
}
}
}

// copy computed data to public data copy:
lock.lock();
try {
System.arraycopy(privateData, 0, publicData, 0, privateData.length);
} finally {
lock.unlock();
}

frameTimes[currentFrameIndex] = System.nanoTime() ;
int nextIndex = (currentFrameIndex + 1) % frameTimes.length ;
if (frameTimes[nextIndex] > 0) {
averageGenerationTime.set((frameTimes[currentFrameIndex] - frameTimes[nextIndex]) / frameTimes.length);
}
currentFrameIndex = nextIndex ;
}

public void consumeData(Consumer<int[]> consumer) {
lock.lock();
try {
consumer.accept(publicData);
} finally {
lock.unlock();
}
}

public long getAverageGenerationTime() {
return averageGenerationTime.get() ;
}

}

And here's the UI:

import java.nio.IntBuffer;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class AnimationApp extends Application {

private final int size = 400 ;
private IntBuffer buffer ;

@Override
public void start(Stage primaryStage) throws Exception {

// background image data generation:

ImageGenerator generator = new ImageGenerator(size, size);

// Generate new image data as fast as possible:
Thread thread = new Thread(() -> {
while( true ) {
long now = System.currentTimeMillis() ;
double angle = 2 * Math.PI * (now % 10000) / 10000 - Math.PI;
generator.generateImage(angle);
}
});
thread.setDaemon(true);
thread.start();

generator.consumeData(data -> buffer = IntBuffer.wrap(data));
PixelFormat<IntBuffer> format = PixelFormat.getIntArgbPreInstance() ;
PixelBuffer<IntBuffer> pixelBuffer = new PixelBuffer<>(size, size, buffer, format);
WritableImage image = new WritableImage(pixelBuffer);

BorderPane root = new BorderPane(new ImageView(image));

Label fps = new Label("FPS: ");
root.setTop(fps);

Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Give me a ping, Vasili. ");
primaryStage.show();

AnimationTimer animation = new AnimationTimer() {

@Override
public void handle(long now) {
// Update image, ensuring we only see the underlying
// data in a consistent state:
generator.consumeData(data -> {
pixelBuffer.updateBuffer(pb -> null);
});
long aveGenTime = generator.getAverageGenerationTime() ;
if (aveGenTime > 0) {
double aveFPS = 1.0 / (aveGenTime / 1_000_000_000.0);
fps.setText(String.format("FPS: %.2f", aveFPS));
}
}

};

animation.start();

}

public static void main(String[] args) {
Application.launch(args);
}
}

For a version that doesn't rely on the JavaFX 13 PixelBuffer, you can just modify this class to use a PixelWriter (AIUI this won't be quite as efficient, but works just as smoothly in this example):

//      generator.consumeData(data -> buffer = IntBuffer.wrap(data));
PixelFormat<IntBuffer> format = PixelFormat.getIntArgbPreInstance() ;
// PixelBuffer<IntBuffer> pixelBuffer = new PixelBuffer<>(size, size, buffer, format);
// WritableImage image = new WritableImage(pixelBuffer);

WritableImage image = new WritableImage(size, size);
PixelWriter pixelWriter = image.getPixelWriter() ;

and

        AnimationTimer animation = new AnimationTimer() {

@Override
public void handle(long now) {
// Update image, ensuring we only see the underlying
// data in a consistent state:
generator.consumeData(data -> {
// pixelBuffer.updateBuffer(pb -> null);
pixelWriter.setPixels(0, 0, size, size, format, data, 0, size);
});
long aveGenTime = generator.getAverageGenerationTime() ;
if (aveGenTime > 0) {
double aveFPS = 1.0 / (aveGenTime / 1_000_000_000.0);
fps.setText(String.format("FPS: %.2f", aveFPS));
}
}

};

Beamer: How to show images as step-by-step images

I found a solution to my problem, by using the visble-command.

EDITED:

\visible<2->{
\textbf{Some text}
\begin{figure}[ht]
\includegraphics[width=5cm]{./path/to/image}
\end{figure}
}

Pending in Chrome When Lazy Loading Images in Dynamically Loaded Content

Unfortunately, the best thing I could find is to navigate to chrome://net-internals/#sockets and hit the "Flush socket pools" button at the top of the page. Once I refresh the page with all the images, they all lazily load as they should.

Obviously expecting clients to tweak their browsers, let alone to know how, isn't an option, but for the time being this works.

Display multiple images in one IPython Notebook cell?

Short answer:

call plt.figure() to create new figures if you want more than one in a cell:

for ima in images:
plt.figure()
plt.imshow(ima)

But to clarify the confusion with Image:

IPython.display.Image is for displaying Image files, not array data. If you want to display numpy arrays with Image, you have to convert them to a file-format first (easiest with PIL):

from io import BytesIO
import PIL
from IPython.display import display, Image

def display_img_array(ima):
im = PIL.Image.fromarray(ima)
bio = BytesIO()
im.save(bio, format='png')
display(Image(bio.getvalue(), format='png'))

for ima in images:
display_img_array(ima)

A notebook illustrating both approaches.



Related Topics



Leave a reply



Submit