In CImg, how to draw text on a specific slice of a 3D image? - cimg

Using CImg, I can draw a text string on a 2D image easily. For example, if you create a 2D image:
CImg<unsigned char> img(180, 160, 1, 3);
Then you can draw "Hello" on it using the draw_text function:
img.draw_text(5, 2, "Hello", white, 0, 1, 24);
But if I have a 3D image like this:
CImg<unsigned char> img(180, 160, 10, 3);
I want to draw the text string on the 2nd slice. What should I do?

You should use CImg<>::get_slice() and CImg::draw() to extract the slice, draw text on it,then put the modified slice back in your volumetric image.

Related

Altair: How to make scatter plot aligned with image background created by mark_image?

I'm looking for a working example to have a .png picture as the background of a scatter chart.
Currently, I use mark_image to draw the background image:
source = pd.DataFrame.from_records([
{"x": 0, "y": 0,
"img": "http://localhost:8888/files/BARTStreamlit/assets/BARTtracksmap.png?_xsrf=2%7Ce13c35be%7Ce013c83479b892363239e5b6a77d97dc%7C1652400559"}
])
tracksmap = alt.Chart(source).mark_image(
width=500,
height=500
).encode(
x='x',
y='y',
url='img'
)
tracksmap
Here is the resulted image drown:
and draw the scater chart,
chart = alt.Chart(maptable).mark_circle(size=60).encode(
x= 'x',
y= 'y',
tooltip=['short_name', 'ENTRY']
).interactive()
chart
I have scaled the x, y channel values for the scatter chart to be in the range of [0, 500]. 500 is the width and height of the background image that I guessed.
Here is the resulted scatter plot:
then I combined the two chart with layer mechanism:
geovizvega = alt.layer(tracksmap, chart)
geovizvega
resulting the following:
The two charts do not align. I'd like to have the scatter dots aligning with the tracks on the background image. How can I achieve that?
To have them aligned, I might need to have the background image's top left corner at the coordinates (0, 0), how can I achieve that? (It seems that the x, y channel values for mark_image is the coordinates of the center of the image? With accurate definition of the x, y channel values, it might be possible to calculate the proper value of x, and y for the top left coroner to be at (0, 0)).
I might need to to have precise dimension of the background image. How?
My above approach may not be the right one. Please show me a working example.
Yes, if you change the values of x and y in your image plot to something like y=-200 and x=200, the image should be more centered in the scatter plot.
You can also change the anchor point of the image using align and baseline:
import altair as alt
import pandas as pd
source = pd.DataFrame.from_records([
{"x": 2, "y": 2, "img": "https://vega.github.io/vega-datasets/data/7zip.png"}
])
imgs = alt.Chart(source).mark_image(
width=100,
height=100
).encode(
x='x',
y='y',
url='img'
)
imgs + imgs.mark_circle(size=200, color='red', opacity=1)
imgs = alt.Chart(source).mark_image(
width=100,
height=100,
align='right',
baseline='top'
).encode(
x='x',
y='y',
url='img'
)
imgs + imgs.mark_circle(size=200, color='red', opacity=1)
After this, you would still need to change the dimensions of the chart so that it has the same size as the image. The default is width=400 and height=300. You can get the dimensions of your image in most image editing software or using the file <imagename> command (at least on linux). But even after getting these dimensions, you would have to do some manual adjustments due to axes taking up some of that space in the chart.

CImg : what kind of values are required on this constructor?

In the documentation (http://cimg.eu/reference/structcimg__library_1_1CImg.html#a24f3b43daa4444b94a973c2c3fff82c5), you can read that the N°7 constructor requires an array of values to fill the image :
values = Pointer to the input memory buffer.
You must know that I'm working with a RGB 2D image (so, a "normal"/"common" image).
Thus, I filled a vector (= more or less an array) with Nx3 values. N is the number of pixels, and the number "3" is because I use red, green and blue. I set the first value to 0, the 2nd to 0, the 3rd to 255 and these 3 operations are repeated N times. That's why my vector, which is named w, looks like that : {0, 0, 255 ; 0, 0, 255 ; etc.}
I wrote this constructor : cimg_library::CImg<unsigned char>(&w[0], width, height, 2, 3); to say that there are 3 channels, a depth of 2 (since I use 2D), and to give my values (width, height and pixels).
I should obtain a entirely blue image. But it's yellow. Why ? Did I badly used the vector ?
Unlike most formats which are stored "band interleaved by pixel", i.e. RGBRGBRGB..., the data in a CImg are stored "band-interleaved by plane", i.e. all the red components are first, then all the green components, then all the blue ones, so it looks like RRRGGGBBB. This is described here.
So, your code would need to be like this:
#include <vector>
#include "CImg.h"
using namespace std;
using namespace cimg_library;
int main()
{
const int width=3;
const int height=2;
// 1. row - red, green, blue
// 2. row - cyan, magenta, yellow
// 6 pixels
// Red plane first - red, green, blue, cyan, magenta, yellow
// 255,0,0,0,255,255
// Green plane next - red, green, blue, cyan, magenta, yellow
// 0,255,0,255,0,255
// Blue plane - red, green, blue, cyan, magenta, yellow
// 0,0,255,255,255,0
vector<unsigned char> w{
255,0,0,0,255,255,
0,255,0,255,0,255,
0,0,255,255,255,0
};
CImg<unsigned char> image((unsigned char*)&w[0],width,height,1,3);
image.save_pnm("result.pnm");
}
Or, if you simply want a solid blue image, the easiest way is probably to instantiate a simple 1x1 blue image using an initialiser for the one pixel, then to resize it:
// Instantiate a 1x1 RGB image initialised to blue (last three values)
CImg<unsigned char> blue(1,1,1,3,0,0,255);
// Resize to larger image
blue.resize(width,height);
Another method might be:
// Create RGB image and fill with Blue
CImg<unsigned char> image(width,height,1,3);
image.get_shared_channel(0).fill(0);
image.get_shared_channel(1).fill(0);
image.get_shared_channel(2).fill(255);
Another method might be:
CImg<unsigned char> image(256,256,1,3);
// for all pixels x,y in image
cimg_forXY(image,x,y) {
image(x,y,0,0)=0;
image(x,y,0,1)=0;
image(x,y,0,2)=255;
}

Matlab - remove image border using logical indexing

I have a retinal fundus image which has a white border along the corners. I am trying to remove the borders on all four sides of the image. This is a pre-processing step and my image looks like this:
fundus http://snag.gy/XLGkC.jpg
It is an RGB image, and I took the green channel, and created a mask using logical indexing. I searched for pixels which were all black in the image, and eroded the mask to remove the white edge pixels. However, I am not sure how to retrieve the final image, without the white pixel border using the mask that I have. This is my code, and any help would be appreciated:
maskIdx = rgb(:,:,2) == 0; # rgb is the original image
se = strel('disk',3); # erode 3-pixel using a disk structuring element
im2 = imerode(maskIdx, se);
newrgb = rgb(im2); # gives a vector - not the same size as original im
Solved it myself. This is what I did with some help.
I first computed the mask for all three color channels combined. This is because the mask for each channel is not the same when applied to all the three channels individually, and residual pixels will be left in the final image if I used only the mask from one of the channels in the original image:
mask = (rgb(:,:,1) == 0) & (rgb(:,:,2) == 0) & (rgb(:,:,3) == 0);
Next, I used a disk structuring element with a radius of 9 pixels to dilate my mask:
se = strel('disk', 9);
maskIdx = imdilate(mask,se);
EDIT: A structuring element which is arbitrary can also be used. I used: se = strel(ones(9,9))
Then, with the new mask, I multiplied the original image with the new dilated mask:
newImg(:,:,1) = rgb(:,:,1) .* uint8(maskIdx); # image was of double data-type
newImg(:,:,2) = rgb(:,:,2) .* uint8(maskIdx);
newImg(:,:,3) = rgb(:,:,3) .* uint8(maskIdx);
Finally, I subtracted the computed color-mask from the original image to get my desired border-removed image:
finalImg = rgb - newImg;
Result:
image http://snag.gy/g2X1v.jpg

Matlab overlaying color on an image

I have a grascale image where I want to overlay different colors to different areas that have similar properties (say direction or intensity etc.) I am not referring to a heat map. Rather I have hard coded segmentation code where I have grouped pixels together by their "similarities". Now I want to over lay colors to those pixels.
For example, for a 3x3 pixel pic, say I know that the top row and bottom row are similar group. And the middle row is another group. How can I overlay a red hue with one group and a blue hue with another?
You could make your 3x3x1 grayscale image into a 3x3x3 color image, and then adjust the hue values for the pixels you want.
So say:
GreyImg=[0.2, 0.3, 0.35;...
0.5, 0.6, 0.56;...
0.8, 0.8, 0.85];
%Convert To Color Img
ColorImg(:,:,1)=GreyImg;
ColorImg(:,:,2)=GreyImg;
ColorImg(:,:,3)=GreyImg;
%Add a red hew to top row:
ColorImg(1:1,:,1)=ColorImg(1:1,:,1)+[.2, .2, .2];
%Add a blew hew to top row:
ColorImg(3:3,:,3)=ColorImg(3:3,:,3)+[.2, .2, .2];
imshow(ColorImg);

Converting PIL image to GTK pixmap with alpha

So I need to take an image I made in PIL and convert it to a pixmap to be displayed in a drawable.
How do I convert from PIL to pixmap and keep the images alpha?
Currently I have this code written:
def gfx_draw_tem2(self, r, x, y):
#im = Image.open("TEM/TEM cropped.png")
im = Image.new("RGBA", (r*2,r*2), (255, 255, 255, 255))
draw = ImageDraw.Draw(im)
for i in range(0,r*2):
for j in range(0,r*2):
if(self.in_circle(i,j,r)):
draw.point((i,j), fill=(100,50,75,50)) #alpha at 255 for test2.png
im.save("test.png")
im_data = im.tostring()
pixbuf = gdk.pixbuf_new_from_data(im_data, gdk.COLORSPACE_RGB, True, 8, im.size[0], im.size[1], 4*im.size[0])
pixmap2, mask = pixbuf.render_pixmap_and_mask()
self.pixmap.draw_drawable(self.white_gc, pixmap2, 0,0,x-r,y-r,-1,-1)
Here are the images I created from im.save("test.png"):
http://imgur.com/43spsBG,lqowten#0
Notice the first picture has an alpha of 255 (full) and the seconds has an alpha of 50.
However When I convert the images to a pixmap with my current code I lose the transparent affect.
Thanks for your help,
Ian
EDIT: I have narrowed it down a little bit with more testing. I am losing the alpha of my image when converting the pixbuf to a pixmap.
Okay figured it out.
Trick here is to not convert the pixbuf to a pixmap using pixbuf.render_pixmap_and_mask()
Instead I took my self.pixmap that I draw onto my drawable and called draw_pixbuf() on it.
Here is the new code I used.
def gfx_draw_tem2(self, r, x, y):
im = Image.new("RGBA", (r*2,r*2), (1, 1, 1, 0))
draw = ImageDraw.Draw(im)
for i in range(0,r*2):
for j in range(0,r*2):
if(self.in_circle(i,j,r)):
draw.point((i,j), fill=(100,50,75,140))
im_data = im.tostring()
pixbuf = gdk.pixbuf_new_from_data(im_data, gdk.COLORSPACE_RGB, True, 8, im.size[0], im.size[1], 4*im.size[0])
self.pixmap.draw_pixbuf(self.white_gc, pixbuf, 0, 0, x, y, -1, -1, gdk.RGB_DITHER_NORMAL, 0, 0)
Hope this helps someone.