newbie keeps losing alpha values in canvas - html5-canvas

I'm probably doing something boneheaded here but I'm having difficulties getting alpha values to cooperate in Canvas. I'm trying to sample an opaque color from a spot on the canvas, make it more transparent, and lay it down in another spot -- but the alpha part doesn't seem to be working. Stripped down it goes sort of like this (condensed from functions strewn across the script):
p = ground.ctx.getImageData(loc.x, loc.y, 1, 1).data;
col = {R: p[0], G: p[1], B: p[2], a: p[3]};
col.a = col.a - 0.1;
ground.ctx.fillStyle = 'rgba(' + col.R + ', ' + col.G + ', ' + col.B + ', ' + col.a + ')';
ground.ctx.fillRect(nuLoc.x, nuLoc.y, sqrSize, sqrSize);
It all runs, but when I test the value of fillStyle I get just a standard RGB "#fa674c" or whatever -- no mention of any alpha -- and when I getImageData() from the newly drawn Rect the value is fully opaque again.
Another thing I haven't been able to figure out either empirically or by reading every tutorial (and the spec) is whether alpha wants to be 0-1.0 or 0-255. Most sources talk about 0-1.0 -- but getImageData() returns 0-255... and I can't make it work either way.

Use context.globalAlpha instead of using rgba fill:
p = ground.ctx.getImageData(loc.x, loc.y, 1, 1).data;
col = {R: p[0], G: p[1], B: p[2], a: p[3]};
// note: globalAlpha uses a scale of 0-1
// and getImageData uses a scale of 0-255
ground.ctx.globalAlpha = a/255-.1;
ground.ctx.fillStyle = 'rgb(' + col.R + ', ' + col.G + ', ' + col.B + ')';
ground.ctx.fillRect(nuLoc.x, nuLoc.y, sqrSize, sqrSize);
// reset globalAlpha when you're done
ground.ctx.globalAlpha = 1;

Related

Is the brush selection handle radius configurable?

I've been testing the following example on tablets & phones:
http://dc-js.github.io/dc.js/examples/filtering.html
Everything is great apart from when I do a brush selection on the bar chart then try to modify the range by dragging one of the handles. You have to be precise to catch the handle. If you miss it, you either drag the range or you cancel the current range and start a new range.
So is there a way to expand the selection radius around the brush selection handles?
Looks like there is an option for this in d3v4, so this will get easier when dc.js is upgraded.
For now, we can guess what d3v3 is doing and use a pretransition event handler to modify the brushes before they're rendered. We can also replace the visual representation.
In d3v3, the brush width seems to be hard coded at 6 with an x offset of -3:
I can't explain why this seems to align perfectly with the right brush handle but seems to be a few pixels off for the left brush handle. In experimenting with this, it seems like it the offset should probably be -6 for the left (west) handle and 0 for the right (east) handle, so maybe dc.js could benefit from the techniques shown here.
Anyway, let's double the width. Our pretransition handler will set the width to 12, and set the offset to -12 for the west and 0 for the east handle:
spendHistChart.on('pretransition.bighandle', function(chart) {
chart.selectAll('g.brush .resize.w rect')
.attr('x', -12)
.attr('width', 12);
chart.selectAll('g.brush .resize.e rect')
.attr('x', 0)
.attr('width', 12);
});
Now, for fun and bonus points, we can also make the handles bigger. Here's a previous answer where we modified the brush path.
Similarly, we can override resizeHandlePath and basically double every X coordinate, as well as doubling the height of the arcs that make up the top and bottom of the handles:
dc.override(spendHistChart, 'resizeHandlePath', function (d) {
var e = +(d === 'e'), x = e ? 1 : -1, y = spendHistChart.effectiveHeight() / 3;
return 'M' + (0.5 * x) + ',' + y +
'A12,12 0 0 ' + e + ' ' + (13 * x) + ',' + (y + 12) +
'V' + (2 * y - 12) +
'A12,12 0 0 ' + e + ' ' + (1 * x) + ',' + (2 * y) +
'Z' +
'M' + (5 * x) + ',' + (y + 14) +
'V' + (2 * y - 14) +
'M' + (9 * x) + ',' + (y + 14) +
'V' + (2 * y - 14);
});
And voilĂ ! Big handles with a lot of area to grab onto:

Check for pixel values in a neighborhood

I'm trying to write a MATLAB script that does the following:
Given: pixel coordinates(x,y) for a .jpg image
Goal: Check, within a 5 pixel radius of given coordinates, if there is a pixel of a certain value.
For example, let's say I'm given the coordinates (100,100), then I want to check the neighborhood of (100,100) within my image for any pixels that are black (0,0,0). So perhaps, pixel (103, 100) and (104,100) might have the value (0,0,0).
Current code:
x_coord = uint32(coord(:,1));
y_coord = uint32(coord(:,2));
count = 0;
for i = 1:length(x_coord)
%(img(x,y) returns pixel value at that (x,y)
%Note 0 = black. Indicating that, at that position, the image is just
% black
if img(x_coord(i),y_coord(i)) == 0
count = count + 1;
end
end
It currently only checks at an exact location. Not in a local neighborhood. How to could I extend this?
EDIT: Also note, as long as there as at least one pixel in the neighborhood with the value, I increment count. I'm not trying to enumerate how many pixels in the neighborhood have that value, just trying to find evidence of at least one pixel that has that value.
EDIT:
Even though I am unable to identify an error with the code, I am not able to get the exact results I want. Here is the code I am using.
val = 0; %pixel value to check
N = 50; % neighbourhood radius
%2D grid of coordinates surrounding center coordinate
[R, C] = ndgrid(1 : size(img, 1), 1 : size(img, 2));
for kk = 1 : size(coord, 1)
r = coord(kk, 1); c = coord(kk, 2); % Get pixel locations
% mask of valid locations within the neighbourhood (avoid boundary problems)
mask = (R - r).^2 + (C - c).^2 <= N*N;
pix = img(mask); % Get the valid pixels
valid = any(pix(:) ~= val);
% Add either 0 or 1 depending if we have found any matching pixels
if(valid == 1)
img = insertMarker(img, [r c], 'x', 'color', 'red', 'size', 10);
imwrite(img, images(i).name,'tiff');
end
count = count + valid;
end
An easier way to do this would be to use indexing to grab a neighbourhood, then to check to see if any of the pixels in the neighbourhood have the value that you're looking for, use any on a flattened version of this neighbourhood. The trick with grabbing the right neighbourhood is to first generate a 2D grid of coordinates that span the entire dimensions of your image, then simply use the equation of a circle with the centre of it being each coordinate you are looking at and determine those locations that satisfy the following equation:
(x - a)^2 + (y - b)^2 <= N^2
N is the radius of the observation window, (a, b) is a coordinate of interest while (x, y) is a coordinate in the image. Use meshgrid to generate the coordinates.
You would use the above equation to create a logical mask, index into your image to pull the locations that are valid within the mask and check how many pixels match the one you want. Another added benefit with the above approach is that you are not subject to any out of bounds errors. Because you are pre-generating the list of all valid coordinates in your image, generating the mask will confine you within the boundaries of the image so you never have to check for out of boundaries conditions.... even when you specify coordinates to search that are out of bounds.
Specifically, assuming your image is stored in img, you would do:
count = 0; % Remembers total count of pixels matching a value
val = 0; % Value to match
N = 50; % Radius of neighbourhood
% Generate 2D grid of coordinates
[x, y] = meshgrid(1 : size(img, 2), 1 : size(img, 1));
% For each coordinate to check...
for kk = 1 : size(coord, 1)
a = coord(kk, 1); b = coord(kk, 2); % Get the pixel locations
mask = (x - a).^2 + (y - b).^2 <= N*N; % Get a mask of valid locations
% within the neighbourhood
pix = img(mask); % Get the valid pixels
count = count + any(pix(:) == val); % Add either 0 or 1 depending if
% we have found any matching pixels
end
The proposed solution:
fc = repmat(-5:5,11,1);
I = (fc.^2+fc'.^2)<=25;
fc_x = fc(I);
fc_y = fc'; fc_y = fc_y(I);
for i = 1:length(x_coord)
x_toCheck = fc_x + x_coord(i);
y_toCheck = fc_y + y_coord(i);
I = x_toCheck>0 & x_toCheck<=yourImageWidth;
I = I.*(y_toCheck>0 & y_toCheck<=yourImageHeight);
x_toCheck = x_toCheck(logical(I));
y_toCheck = y_toCheck(logical(I));
count = sum(img(x_toCheck(:),y_toCheck(:)) == 0);
end
If your img function can only check one pixel at a time, just add a for loop:
for i = 1:length(x_coord)
x_toCheck = fc_x + x_coord(i);
y_toCheck = fc_y + y_coord(i);
I = x_toCheck>0 & x_toCheck<=yourImageWidth;
I = I.*(y_toCheck>0 & y_toCheck<=yourImageHeight);
x_toCheck = x_toCheck(logical(I));
y_toCheck = y_toCheck(logical(I));
for j = 1:length(x_toCheck)
count = count + (img(x_toCheck(j),y_toCheck(j)) == 0);
end
end
Step-by-step:
You first need to get all the coordinates within 5 pixels range of the given coordinate.
We start by building a square of 11 pixels in length/width.
fc = repmat(-5:5,11,1);
fc_x = fc;
fc_y = fc';
plot(fc_x,fc_y,'.');
We now need to build a filter to get rid of those points outside the 5-pixel radius.
I = (fc.^2+fc'.^2)<=25;
Apply the filter, so we can get a circle of 5-pixel radius.
fc_x = fc_x(I);
fc_y = fc_y(I);
Next translate the centre of the circle to the given coordinate:
x_toCheck = fc_x + x_coord(i);
y_toCheck = fc_y + y_coord(i);
You need to check whether part of the circle is outside the range of your image:
I = x_toCheck>0 & x_toCheck<=yourImageWidth;
I = I.*(y_toCheck>0 & y_toCheck<=yourImageHeight);
x_toCheck = x_toCheck(logical(I));
y_toCheck = y_toCheck(logical(I));
Finally count the pixels:
count = sum(img(x_toCheck,y_toCheck) == 0);

Mid line through a set of dicom images in matlab

I have a set of Dicom images on matlab and i would like to add a midline going through all the images
I am outputting the images via imshow3d function
thanks
Edit: here's what i have, the random points are not in the middle they just run through the image
>> clc;
>>clear;
>>%imports dicom images
>>run DicomImport.m;
>>%random points for shortest distance test
>>a = [1 10 200];
>>b = [500 512 300];
>>ab = b - a;
>>n = max(abs(ab)) + 1;
>>s = repmat(linspace(0, 1, n)', 1, 3);
>>for d = 1:3
>> s(:, d) = s(:, d) * ab(d) + a(d);
>>end
>>s = round(s);
>>Z = 593;
>>N = 512;
>>X = zeros(N, N, Z);
>>X(sub2ind(size(X), s(:, 1), s(:, 2), s(:, 3))) = 1;
>>C = find(X);
>>ans.Img(C) = 5000;
>> %shows image
>>imshow3D(ans.Img);
So it looks like ans.Img contains the 3D matrix consisting of your image stack. It looks like you've got something going, but allow me to do this a bit differently. Basically, you need to generate a set of coordinates where we can access the image stack and draw a vertical line in the middle of the each image in the image stack. Do something like this. First get the dimensions of the stack, then determine the halfway point for the columns. Next, generate a set of coordinates that will draw a line down the middle for one image. After you do this, repeat this for the rest of the slices and get the column major indices for these:
%// Get dimensions
[rows,cols,slices] = size(ans.Img);
%// Get halfway point for columns
col_half = floor(cols/2);
%// Generate coordinates for vertical line for one slice
coords_middle_row = (1:rows).';
coords_middle_col = repmat(col_half, rows, 1);
%// Generate column major indices for the rest of the slices:
ind = sub2ind(size(ans.Img), repmat(coords_middle_row, slices, 1), ...
repmat(coords_middle_col, slices, 1), ...
reshape(kron(1:slices, ones(rows, 1)), [], 1));
%// Set the pixels accordingly
ans.Img(ind) = 5000;
This code is quite similar to the answer I provided to one of your earlier question; i.e. I don't use imshow3D but the framework is similar and simpler to modify in order to suit your need. In this case, upon pressing a pushbutton a line appears at the middle of the stack and you can scroll through it with the slider. I hope this can be of help.
function LineDicom(~)
clc
clear
close all
%// Load demo data
S = load('mri');
%// Get dimensions and number of slices.
ImageHeight = S.siz(1); %// Not used here
ImageWidth = S.siz(2); %// Not used here
NumSlices = S.siz(3);
S.D = squeeze(S.D);
%// Create GUI
hFig = figure('Position',[100 100 400 400],'Units','normalized');
%// create axes with handle
handles.axes1 = axes('Position', [0.2 0.2 0.6 0.6]);
%// create y slider with handle
handles.y_slider = uicontrol('style', 'Slider', 'Min', 1, 'Max', NumSlices, 'Value',1, 'Units','normalized','position', [0.08 0.2 0.08 0.6], 'callback', #(s,e) UpdateY);
handles.SlideryListener = addlistener(handles.y_slider,'Value','PostSet',#(s,e) YListenerCallBack);
%// Create pusbutton to draw line
handles.DrawLineButton= uicontrol('style', 'push','position', [40 40 100 30],'String','Draw line', 'callback', {#DrawLine,handles});
%// Flag to know whether pushbutton has been pushed
handles.LineDrawn = false;
%// Show 1st slice
imshow(S.D(:,:,1))
guidata(hFig,handles);
%// Listeners callbacks followed by sliders callbacks. Used to display each
%// slice smoothly.
function YListenerCallBack
handles = guidata(hFig);
%// Get current slice
CurrentSlice = round(get(handles.y_slider,'value'));
hold on
imshow(S.D(:,:,CurrentSlice));
%// If button was button, draw line
if handles.LineDrawn
line([round(ImageWidth/2) round(ImageWidth/2)],[1 ImageHeight],'Color','r','LineWidth',2);
end
drawnow
guidata(hFig,handles);
end
function UpdateY(~)
handles = guidata(hFig); %// Get handles.
CurrentSlice = round(get(handles.y_slider,'value'));
hold on
imshow(S.D(:,:,CurrentSlice));
if handles.LineDrawn
line([round(ImageWidth/2) round(ImageWidth/2)],[1 ImageHeight],'Color','r','LineWidth',2);
end
drawnow
guidata(hFig,handles);
end
%// Pushbutton callback to draw line.
function DrawLine(~,~,handles)
line([round(ImageWidth/2) round(ImageWidth/2)],[1 ImageHeight],'Color','r','LineWidth',2);
handles.LineDrawn = true;
guidata(hFig,handles);
end
end
Sample output:
and after moving the slider up:
Is this what you meant? If not I'll remove that answer haha and sorry.

Octave imwrite loses grayscale

I am using Octave 3.6.4 to process an image and store it afterwards. The image I read is grayscale, and after calculations the matrix should be of the same type. However, if I open the stored image, there are no gray pixels. There are only black and white ones and the gray ones got lost. They are essentially all white.
Here is the processing code:
function aufgabe13()
[img, map, alpha] = imread("Buche.png");
[imax, jmax] = size(img);
a = 0.7;
M = 255 * ones(imax,jmax + round(imax * a));
for i = 1:imax
begin = round((imax-i)*a);
M(i,begin +1 : begin + jmax) = img(i,:);
end
imwrite(M, 'BucheScherung.png', 'png');
end
So what am I doing wrong?
The reason why is because M is a double matrix so the values are expected to be between [0,1] when representing an image. Because your values in your image are between [0,255] when read in (type uint8), a lot of the values are white because they're beyond the value of 1. What you should do is convert the image so that it is double precision and normalized between [0,1], then proceed as normal. This can be done with the im2double function.
In other words, do this:
function aufgabe13()
[img, map, alpha] = imread("Buche.png");
img = im2double(img); % Edit
[imax, jmax] = size(img);
a = 0.7;
M = ones(imax,jmax + round(imax * a)); % Edit
for i = 1:imax
begin = round((imax-i)*a);
M(i,begin +1 : begin + jmax) = img(i,:);
end
imwrite(M, 'BucheScherung.png', 'png');
end

NVD3.js (d3.js) Scale Break

I want to create a chart with scale break on y axis. I don't want to use a non-linear scale but using a scale break make lower values more visible when having data anomalies.
What is the best way to implement that in nvd3 or in general in d3?
Lacking other options I created a brute force solution, manipulating the vertical <path> of the Y axis.
Using pseudo code, you use it like this:
var domainPath = yAxis
.select('path.domain');
domainPath.attr('d', breakScale(domainPath.attr('d'), 14, 6, 4, 7);
The breakScale function is not very special nor elegant, but here it goes:
function breakScale(pathString, amplitude, wavelength, periods, dist){
var parts = pathString.match(/(.*)(H-\d+)/);
var first = parts[1];
var last = parts[2];
first = first.replace(/(.*?V)(\d+)/, function(match, p1, p2) { return p1 + (p2-dist-(wavelength)*periods) });
var newPath = first;
for(var i=0; i<periods+1; i++){
if(i === 0){
newPath += 'l-' + (amplitude/2) + ',' + (wavelength/2);
}
else if(i == periods){
newPath += 'l' + (i%2?'':'-') + (amplitude/2) + ',' + (wavelength/2);
}
else {
newPath += 'l' + (i%2?'':'-') + amplitude + ',' + wavelength;
}
}
newPath += 'v' + dist + last;
return newPath;
}
dist defines the distance from the start of the axis that the wavy thing should begin. The function probably works best with even numbers for amplitude and wavelength because they are both halved for the first and last part of the wave. periodsshould be at least 3.
Given a typical domain axis path string like M-6,0H0V376H-6, it will spit back something like M-6,0H0V345l-7,3l14,6l-14,6l14,6l-7,3v7H-6 which would look like this:

Resources