About use tf.image.crop_and_resize - tensorflow

I'm working on the ROI pooling layer which work for fast-rcnn and I am used to use tensorflow. I found tf.image.crop_and_resize can act as the ROI pooling layer.
But I try many times and cannot get the result that I expected.Or did the true result is exactly what I got?
here is my code
import cv2
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
img_path = r'F:\IMG_0016.JPG'
img = cv2.imread(img_path)
img = img.reshape([1,580,580,3])
img = img.astype(np.float32)
#img = np.concatenate([img,img],axis=0)
img_ = tf.Variable(img) # img shape is [580,580,3]
boxes = tf.Variable([[100,100,300,300],[0.5,0.1,0.9,0.5]])
box_ind = tf.Variable([0,0])
crop_size = tf.Variable([100,100])
#b = tf.image.crop_and_resize(img,[[0.5,0.1,0.9,0.5]],[0],[50,50])
c = tf.image.crop_and_resize(img_,boxes,box_ind,crop_size)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
a = c.eval(session=sess)
plt.imshow(a[0])
plt.imshow(a[1])
And I handed in my origin img and result:a0,a1
if I was wrong can anyone teach me how to use this function? thanks.

Actually, there's no problem with Tensorflow here.
From the doc of tf.image.crop_and_resize (emphasis is mine) :
boxes: A Tensor of type float32. A 2-D tensor of shape [num_boxes, 4].
The i-th row of the tensor specifies the coordinates of a box in the
box_ind[i] image and is specified in normalized coordinates [y1, x1,
y2, x2]. A normalized coordinate value of y is mapped to the image
coordinate at y * (image_height - 1), so as the [0, 1] interval of
normalized image height is mapped to [0, image_height - 1] in image
height coordinates. We do allow y1 > y2, in which case the sampled
crop is an up-down flipped version of the original image. The width
dimension is treated similarly. Normalized coordinates outside the [0,
1] range are allowed, in which case we use extrapolation_value to
extrapolate the input image values.
The boxes argument needs normalized coordinates. That's why you get a black box with your first set of coordinates [100,100,300,300] (not normalized, and no extrapolation value provided), and not with your second set [0.5,0.1,0.9,0.5].
However, as that why matplotlib show you gibberish on your second attempt, it's just because you're using the wrong datatype.
Quoting the matplotlib documentation of plt.imshow (emphasis is mine):
All values should be in the range [0 .. 1] for floats or [0 .. 255]
for integers. Out-of-range values will be clipped to these bounds.
As you're using float outside the [0,1] range, matplotlib is bounding your values to 1. That's why you get those colored pixels (either solid red, solid green or solid blue, or a mixing of these). Cast your array to uint_8 to get an image that make sense.
plt.imshow( a[1].astype(np.uint8))
Edit :
As requested, I will dive a bit more into
tf.image.crop_and_resize.
[When providing non normalized coordinates and no extrapolation values], why I just get a blank result?
Quoting the doc :
Normalized coordinates outside the [0, 1] range are allowed, in which
case we use extrapolation_value to extrapolate the input image values.
So, normalized coordinates outside [0,1] are allowed. But they still need to be normalized !
With your example, [100,100,300,300], the coordinates you provide makes the red square. Your original image is the little green dot in the upper left corner! The default value of the argument extrapolation_value is 0, so the values outside the frame of the original image are inferred as [0,0,0] hence the black.
But if your usecase needs another value, you can provide it. The pixels will take a RGB value of extrapolation_value%256 on each channel. This option is useful if the zone you need to crop is not fully included in you original images. (A possible usecase would be sliding windows for example).

It seems that tf.image.crop_and_resize expects pixel values in the range [0,1].
Changing your code to
test = tf.image.crop_and_resize(image=image_np_expanded/255., ...)
solved the problem for me.

Yet another variant is to use tf.central_crop function.

Below is a concrete implementation of the tf.image.crop_and_resize API. tf version 1.14
import tensorflow as tf
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import numpy as np
tf.enable_eager_execution()
def single_data_2(img_path):
img = tf.read_file(img_path)
img = tf.image.decode_bmp(img,channels=1)
img_4d = tf.expand_dims(img, axis=0)
processed_img = tf.image.crop_and_resize(img_4d,boxes=
[[0.4529,0.72,0.4664,0.7358]],crop_size=[64,64],box_ind=[0])
processed_img_2 = tf.squeeze(processed_img,0)
raw_img_3 = tf.squeeze(img_4d,0)
return raw_img_3, processed_img_2
def plot_two_image(raw,processed):
fig=plt.figure(figsize=(35,35))
raw_ = fig.add_subplot(1,2,1)
raw_.set_title('Raw Image')
raw_.imshow(raw,cmap='gray')
processed_ = fig.add_subplot(1,2,2)
processed_.set_title('Processed Image')
processed_.imshow(processed,cmap='gray')
img_path = 'D:/samples/your_bmp_image.bmp'
raw_img, process_img = single_data_2(img_path)
print(raw_img.dtype,process_img.dtype)
print(raw_img.shape,process_img.shape)
raw_img=tf.squeeze(raw_img,-1)
process_img=tf.squeeze(process_img,-1)
print(raw_img.dtype,process_img.dtype)
print(raw_img.shape,process_img.shape)
plot_two_image(raw_img,process_img)

Below is my working code, also output image is not black, this can be of help to someone
for idx in range(len(bboxes)):
if bscores[idx] >= Threshold:
#Region of Interest
y_min = int(bboxes[idx][0] * im_height)
x_min = int(bboxes[idx][1] * im_width)
y_max = int(bboxes[idx][2] * im_height)
x_max = int(bboxes[idx][3] * im_width)
class_label = category_index[int(bclasses[idx])]['name']
class_labels.append(class_label)
bbox.append([x_min, y_min, x_max, y_max, class_label, float(bscores[idx])])
#Crop Image - Working Code
cropped_image = tf.image.crop_to_bounding_box(image, y_min, x_min, y_max - y_min, x_max - x_min).numpy().astype(np.int32)
# encode_jpeg encodes a tensor of type uint8 to string
output_image = tf.image.encode_jpeg(cropped_image)
# decode_jpeg decodes the string tensor to a tensor of type uint8
#output_image = tf.image.decode_jpeg(output_image)
score = bscores[idx] * 100
file_name = tf.constant(OUTPUT_PATH+image_name[:-4]+'_'+str(idx)+'_'+class_label+'_'+str(round(score))+'%'+'_'+os.path.splitext(image_name)[1])
writefile = tf.io.write_file(file_name, output_image)

Related

implementing tensorflow cvae but couldn't find out the meaning for the resulted image shape [duplicate]

I am using nibabel lib to load data from nii file. I read the document of the lib at http://nipy.org/nibabel/gettingstarted.html, and found that
This information is available without the need to load anything of the main image data into the memory. Of course there is also access to the image data as a NumPy array
This is my code to load the data and it shapes
import nibabel as nib
img = nib.load('example.nii')
data = img.get_data()
data = np.squeeze(data)
data = np.copy(data, order="C")
print data.shape
I got the result
128, 128, 64
What is order of data shape? Is it WidthxHeightxDepth? And my input must arranged as depth, height, width. So I will use input=data.transpose(2,0,1). Is it right? Thanks all
Update: I found that the Numpy will read the image by order Height x Width x Depth as the reference http://www.python-course.eu/images/axis.jpeg
OK, here's my take:
Using scipy.ndimage.imread('img.jpg', mode='RGB'), the resulting array will always have this order: (H, W, D) i.e. (height, width, depth) because of the terminology that numpy uses for ndarrays (axis=0, axis=1, axis=2) or analogously (Y, X, Z) if one would like to visualize in 3 dimensions.
# read image
In [21]: img = scipy.ndimage.imread('suza.jpg', mode='RGB')
# image shape as (H, W, D)
In [22]: img.shape
Out[22]: (634, 1366, 3)
# transpose to shape as (D, H, W)
In [23]: tr_img = img.transpose((-1, 0, 1))
In [23]: tr_img.shape
Out[23]: (3, 634, 1366)
If you consider the img_shape as a tuple,
# index (0, 1, 2)
img_shape = (634, 1366, 3)
# or index (-3, -2, -1)
Choose which one is a convenient way for you to remember.
NOTE: The scipy.ndimage.imread() API has been removed since Scipy 1.2.0. So, it is now recommended to use imageio.imread(), which reads the image and returns Array, a subclass of numpy array, following the same conventions discussed above.
# read image
$ img = imageio.imread('suza.jpg', format='jpg')
# convert the image to a numpy array
$ img_np = np.asarray(img)
PS: It should also be noted that libraries like tensorflow also (almost) follows the same convention as numpy.
tf.image_decode_jpeg() returns:
A Tensor of type uint8. 3-D with shape [height, width, channels]

openCv and PyTorch inverser Transform not working

I have a transforms class which only does:
if transform is None:
transform = transforms.Compose([
transforms.Resize((256, 256)),
transforms.ToTensor()
])
root = os.path.join(PROJECT_ROOT_DIR, "data")
super(AttributesDataset, self).__init__()
self.data = torchvision.datasets.CelebA(
root=root,
split=split,
target_type='attr',
download=True,
transform=transform
)
From the documentation, I understand that this implies just a scale-down of values in the range 0,1 ie all pixel values shall lie between [0,1] (I have verified this as well).
I want to visualize some of the outputs coming from the model. As such, I created a simple method which does:-
for img, label in dataloader:
img.squeeze_(0)
# permute the channels. cv2 expects image in format (h, w, c)
unscaled_img = img.permute(1, 2, 0)
# move images to cpu and convert to numpy as required by cv2 library
unscaled_img = torch.round(unscaled_img * 255)
unscaled_img = unscaled_img.to(torch.uint8)
# unscaled_img = np.rint(unscaled_img * 255).astype(np.uint8)
unscaled_img = cv2.cvtColor(unscaled_img, cv2.COLOR_RGB2BGR)
cv2.imshow(unscaled_img.numpy())
However, all the images that are created have an unusually blue shade. For instance,
Can someone please tell me what exactly am I doing wrong here? Your help would be highly appreciated
Solved by #LajosArpad comment. The culprit was
unscaled_img = cv2.cvtColor(unscaled_img, cv2.COLOR_RGB2BGR)
Removing it resulted in correct values.

Color map an image with TensorFlow?

I'm saving grayscale images in TFRecord files. The idea then was to color map them on my GPU (only using TF of course) so they get three channels (They are going to be used on a pre-trained VGG-16 model so they have to have three channels).
Does anyone have any idea how to this properly?
I tried to do it with my homemade TF color mapping script, using for-loops, tf.scatter_nd and a mapping array with shape = (256,3)... but it took forever.
EDIT:
img_rgb = GRAY SCALE IMAGE WITH 3 CHANNELS
cmp = [[255,255,255],
[255,255,253],
[255,254,250],
[255,254,248],
[255,254,245],
...
[4,0,0],
[0,0,0]]
cmp = tf.convert_to_tensor(cmp, tf.int32) # (256, 3)
hot = tf.zeros([224,224,3], tf.int32)
for i in range(img_rgb.shape[2]):
for j in range(img_rgb.shape[1]):
for k in range(img_rgb.shape[0]):
indices = tf.constant([[k,j,i]])
updates = tf.Variable([cmp[img_rgb[k,j,i],i]])
shape = tf.constant([256, 3])
hot = tf.scatter_nd(indices, updates, shape)
This was my attempt, I know it's not optimal in any way, but It was the only solution I could come up with.
Thanks work by jimfleming, https://gist.github.com/jimfleming/c1adfdb0f526465c99409cc143dea97b
import matplotlib
import matplotlib.cm
import tensorflow as tf
def colorize(value, vmin=None, vmax=None, cmap=None):
"""
A utility function for TensorFlow that maps a grayscale image to a matplotlib
colormap for use with TensorBoard image summaries.
Arguments:
- value: 2D Tensor of shape [height, width] or 3D Tensor of shape
[height, width, 1].
- vmin: the minimum value of the range used for normalization.
(Default: value minimum)
- vmax: the maximum value of the range used for normalization.
(Default: value maximum)
- cmap: a valid cmap named for use with matplotlib's `get_cmap`.
(Default: 'gray')
Example usage:
```
output = tf.random_uniform(shape=[256, 256, 1])
output_color = colorize(output, vmin=0.0, vmax=1.0, cmap='plasma')
tf.summary.image('output', output_color)
```
Returns a 3D tensor of shape [height, width, 3].
"""
# normalize
vmin = tf.reduce_min(value) if vmin is None else vmin
vmax = tf.reduce_max(value) if vmax is None else vmax
value = (value - vmin) / (vmax - vmin) # vmin..vmax
# squeeze last dim if it exists
value = tf.squeeze(value)
# quantize
indices = tf.to_int32(tf.round(value * 255))
# gather
cm = matplotlib.cm.get_cmap(cmap if cmap is not None else 'gray')
colors = tf.constant(cm.colors, dtype=tf.float32)
value = tf.gather(colors, indices)
return value
You could also try tf.image.grayscale_to_rgb, although there seems to be only one choice of color map, gray.
We're here to help. If everyone wrote optimal code, there would be no need for Stackoverflow. :)
Here's how I would do it in place of the last 7 lines (untested code):
conv_img = tf.gather( params = cmp,
indices = img_rgb[ :, :, 0 ] )
Basically, no need for the for loops, Tensorflow will do that for you, and much quicker. tf.gather() will collect elements from cmp according to the indices provided, which here would be the 0th channel of img_rgb. Each collected element will have the three channels from cmp so when you put them all together, it will form an image.
I don't have time to test right now, gotta run, sorry. Hope it works.

matplotlib pyplot imshow tight spacing between images

I have some numpy image arrays, all of the same shape (say (64, 64, 3)). I want to plot them in a grid using pyplot.subplot(), but when I do, I get unwanted spacing between images, even when I use pyplot.subplots_adjust(hspace=0, wspace=0). Below is an example piece of code.
from matplotlib import pyplot
import numpy
def create_dummy_images():
"""
Creates images, each of shape (64, 64, 3) and of dtype 8-bit unsigned integer.
:return: 4 images in a list.
"""
saturated_channel = numpy.ones((64, 64), dtype=numpy.uint8) * 255
zero_channel = numpy.zeros((64, 64), dtype=numpy.uint8)
red = numpy.array([saturated_channel, zero_channel, zero_channel]).transpose(1, 2, 0)
green = numpy.array([zero_channel, saturated_channel, zero_channel]).transpose(1, 2, 0)
blue = numpy.array([zero_channel, zero_channel, saturated_channel]).transpose(1, 2, 0)
random = numpy.random.randint(0, 256, (64, 64, 3))
return [red, green, blue, random]
if __name__ == "__main__":
images = create_dummy_images()
for i, image in enumerate(images):
pyplot.subplot(2, 2, i + 1)
pyplot.axis("off")
pyplot.imshow(image)
pyplot.subplots_adjust(hspace=0, wspace=0)
pyplot.show()
Below is the output.
As you can see, there is unwanted vertical space between those images. One way of circumventing this problem is to carefully hand-pick the right size for the figure, for example I use matplotlib.rcParams['figure.figsize'] = (_, _) in Jupyter Notebook. However, the number of images I usually want to plot varies between each time I plot them, and hand-picking the right figure size each time is extremely inconvenient (especially because I can't work out exactly what the size means in Matplotlib). So, is there a way that Matplotlib can automatically work out what size the figure should be, given my requirement that all my (64 x 64) images need to be flush next to each other? (Or, for that matter, a specified distance next to each other?)
NOTE: correct answer is reported in the update below the original answer.
Create your subplots first, then plot in them.
I did it on one line here for simplicity sake
images = create_dummy_images()
fig, axs = pyplot.subplots(nrows=1, ncols=4, gridspec_kw={'wspace':0, 'hspace':0},
squeeze=True)
for i, image in enumerate(images):
axs[i].axis("off")
axs[i].imshow(image)
UPDATE:
Nevermind, the problem was not with your subplot definition, but with imshow() which distorts your axes after you've set them up correctly.
The solution is to use aspect='auto' in the call to imshow() so that the pictures fills the axes without changing them. If you want to have square axes, you need to create a picture with the appropriate width/height ratio:
pyplot.figure(figsize=(5,5))
images = create_dummy_images()
for i, image in enumerate(images):
pyplot.subplot(2, 2, i + 1)
pyplot.axis("off")
pyplot.imshow(image, aspect='auto')
pyplot.subplots_adjust(hspace=0, wspace=0)
pyplot.show()

Fake-rivalry stimulus

for a binocular rivalry experiment using color blobs (created with GratingStim using a gaussian mask), I need to draw a fake rivalry stimulus. That is, I need a round color blob that has one color for example on the top (25% of the color blob) and another color below (75% of the color blob). Additionally, I would like the twocolored fake rivalry blob to have a gaussian mask as my real rivalry stimuli do. Also it would be good to have a fuzzy color transition in the fake rivalry stimulus. I hope it's clear what I mean.
One solution I thought of was to draw two rectangles with blurred edges and then lay a gaussian alpha mask over them. In order to get the color proportions right, I would only have to move the two rectangles behind the mask. Is there a way to put a alpha-maks over an entire window?
Another solution would be to use ShapeStim as is suggested in this post explaining how to draw a semi circle : https://groups.google.com/forum/#!msg/psychopy-users/L9TYIrf9eJk/m0zIj0N23bMJ I would have to play around with the vertices, but I think it should work. The only thing that worries me here is that ShapeStim has no mask attribute to blur the edges.
Can you think of a way to do it?
Thank you very much!
Lilla
System specifications:
Psychopy v1.83.01 running on iOS 10.11.1
second update, even nicer result:
# Set up stimuli
from psychopy import visual, event
import numpy as np
from scipy.stats import gaussian_kde
win = visual.Window([500,500])
win2 = visual.Window([500,500])
#magic numpy stuff /scipy stuff, adapted from http://docs.scipy.org/doc/scipy-0.15.1/reference/generated/scipy.stats.gaussian_kde.html
mean1 = [0, 0]
#the smaller the value, the bigger the visible blob
cov1 = [[0.03, 0], [0, 0.09]] #in this mask, it should be 50/50
cov2 = [[0.05,0],[0,0.4]] #in this mask, the color with this mask is the smaller one
m1, m2 = np.random.multivariate_normal(mean1, cov1, 2000).T# * np.random.multivariate_normal(mean2, cov2, 5000).T
for i in xrange(len(m2)):
if m2[i] >= 0:
m2[i] = m2[i]* 0.5#np.random.multivariate_normal(mean2, cov2,1).T[0]
values = np.vstack([m1, m2])
kernel = gaussian_kde(values)
xmin = m1.min()
xmax = m1.max()
ymin = m2.min()
ymax = m2.max()
X, Y = np.mgrid[xmin:xmax:128j, ymin:ymax:128j]
positions = np.vstack([X.ravel(), Y.ravel()])
values = np.vstack([m1, m2])
kernel = gaussian_kde(values)
Z = np.reshape(kernel(positions).T, X.shape) #this array will be the mask
Z = Z - 1
for i in xrange(128):
for j in xrange(128): #it will neverbe smaller than -1
if Z[i][j] > 1:
Z[i][j] = 1
# Draw them on top of each other
perc75 = visual.GratingStim(win, sf=0, size=250, color='green',pos=(0.0, 0.0), mask =Z)
perc25 = visual.GratingStim(win, sf=0, size=250, color='red',pos=(0.0, 0.0), mask = 'raisedCos', maskParams={'fringeWidth':0.8})
perc25.setAutoDraw(True)
perc75.setAutoDraw(True)
win.flip()
event.waitKeys()
Would this work? You can then locate the blobs where you want, e.g. for one eye. No need to do whole-window stuff.
# Set up stimuli
from psychopy import visual, event
win = visual.Window([500,500])
blob_large = visual.GratingStim(win, sf=0, mask='gauss', size=1, color='red')
blob_small = visual.GratingStim(win, sf=0, mask='gauss', size=0.5, color='green', maskParams={'sd':6})
# Draw them on top of each other
blob_large.draw()
blob_small.draw()
win.flip()
# Wait for keyboard before quitting.
event.waitKeys()
Update on my question: the following exemplar code solves the problem:
# -*- coding: utf-8 -*-
# adapted from https://groups.google.com/forum/#!msg/psychopy-users/69p-aAWiDGI/e4iT43cHDeEJ
from psychopy import visual, event
win = visual.Window([500,500])
#stimuli
perc25 = visual.GratingStim(win, sf=0, size=1, color='RED',pos=(0.0, 0.0), mask = 'raisedCos', maskParams={'fringeWidth':0.8})
perc75 = visual.GratingStim(win, sf=0, size=0.8, color='green',pos=(0.0, -0.15), mask = 'raisedCos', maskParams={'fringeWidth':0.6})
#prepare for the screenshot
Stimlist = [perc25, perc75]
delta = .5# larger is bigger, slower
dx = delta * win.size[1]/win.size[0]
dy = delta
rect = (-dx, +dy, +dx, -dy)#size of the screenshot
screenshot = visual.BufferImageStim(win, stim=Stimlist,rect = rect, mask = 'gauss', pos=(0.0, 0.0)) # mask can also be 'raisedCos' with a smaller delta, for exmple .2
screenshot.draw()
win.flip()
event.waitKeys()