This function will generate an image by temporarily registering an image size, generating the image (if necessary) and the removing the size so new images will not be created in that size.
function lazy_image_size($image_id, $width, $height, $crop) {
// Temporarily create an image size
$size_id = 'lazy_' . $width . 'x' .$height . '_' . ((string) $crop);
add_image_size($size_id, $width, $height, $crop);
// Get the attachment data
$meta = wp_get_attachment_metadata($image_id);
// If the size does not exist
if(!isset($meta['sizes'][$size_id])) {
require_once(ABSPATH . 'wp-admin/includes/image.php');
$file = get_attached_file($image_id);
$new_meta = wp_generate_attachment_metadata($image_id, $file);
// Merge the sizes so we don't lose already generated sizes
$new_meta['sizes'] = array_merge($meta['sizes'], $new_meta['sizes']);
// Update the meta data
wp_update_attachment_metadata($image_id, $new_meta);
}
// Fetch the sized image
$sized = wp_get_attachment_image_src($image_id, $size_id);
// Remove the image size so new images won't be created in this size automatically
remove_image_size($size_id);
return $sized;
}
It’s “lazy” because images are not generated until they are needed.
In your use-case, you would call that lazy_image_size
with the post thumbnail ID to get an image of the desired size. An image would be generated on the first call. Subsequent calls would fetch the existing image.