Determine distance from coastline in Matlab - matlab

In MATLAB, I have an array of latitude and longitude pairs that represents locations in the United States. I need to determine the distance to the nearest coastline.
I think MATLAB has a built in database of lat/lon points of the United States. How do I access it and use it?
Also any suggestions on how to efficiently determine the distance?
Update: Follow-up question : Determine center of bins when using meshm

Since I don't have access to the Mapping Toolbox, which would be ideal to solve this problem, I came up with a solution that is independent of any toolboxes, including the Image Processing Toolbox.
Steve Eddins has an image processing blog at The MathWorks where last year he had a series of pretty cool posts devoted to using digital elevation maps. Specifically, he pointed out where to get them and how to load and process them. Here are the relevant blog posts:
Locating the US continental divide, part 1 - Introduction: Here, Steve shows where you can get digital elevation map (DEM) tiles and how to load and process them. You can get the DEM tiles (tile E and tile F cover the continental US) from the Global Land One-km Base Elevation Project. The latitude and longitude ranges for each tile can be found here.
Locating the US continental divide, part 4 - Ocean masks: Using the processed DEM data from the above post, Steve shows how to create an ocean mask.
With this DEM data, you can find out the latitude and longitude for where the edges of the oceans are, find the distance from an inland map point to the closest of these coastal points, then make a sweet visualization. I used the function FILTER2 to help find the edges of the oceans via convolution with the ocean mask, and the equations for computing great-circle distances to get the distance between map points along the surface of the Earth.
Using some of the sample code from the above blog posts, here's what I came up with:
%# Load the DEM data:
data_size = [6000 10800 1]; %# The data has 1 band.
precision = 'int16=>int16'; %# Read 16-bit signed integers into a int16 array.
header_bytes = 0;
interleave = 'bsq'; %# Band sequential. Not critical for 1 band.
byte_order = 'ieee-le';
E = multibandread('e10g',data_size,precision,... %# Load tile E
header_bytes,interleave,byte_order);
F = multibandread('f10g',data_size,precision,... %# Load tile F
header_bytes,interleave,byte_order);
dem = [E F]; %# The digital elevation map for tile E and F
clear E F; %# Clear E and F (they are huge!)
%# Crop the DEM data and get the ranges of latitudes and longitudes:
[r,c] = size(dem); %# Size of DEM
rIndex = [1 4000]; %# Row range of DEM to keep
cIndex = [6000 14500]; %# Column range of DEM to keep
dem = dem(rIndex(1):rIndex(2),cIndex(1):cIndex(2)); %# Crop the DEM
latRange = (50/r).*(r-rIndex+0.5); %# Range of pixel center latitudes
longRange = (-180/c).*(c-cIndex+0.5); %# Range of pixel center longitudes
%# Find the edge points of the ocean:
ocean_mask = dem == -500; %# The ocean is labeled as -500 on the DEM
kernel = [0 1 0; 1 1 1; 0 1 0]; %# Convolution kernel
[latIndex,longIndex] = ... %# Find indices of points on ocean edge
find(filter2(kernel,~ocean_mask) & ocean_mask);
coastLat = latRange(1)+diff(latRange).*... %# Convert indices to
(latIndex-1)./diff(rIndex); %# latitude values
coastLong = longRange(1)+diff(longRange).*... %# Convert indices to
(longIndex-1)./diff(cIndex); %# longitude values
%# Find the distance to the nearest coastline for a set of map points:
lat = [39.1407 35 45]; %# Inland latitude points (in degrees)
long = [-84.5012 -100 -110]; %# Inland longitude points (in degrees)
nPoints = numel(lat); %# Number of map points
scale = pi/180; %# Scale to convert degrees to radians
radiusEarth = 3958.76; %# Average radius of Earth, in miles
distanceToCoast = zeros(1,nPoints); %# Preallocate distance measure
coastIndex = zeros(1,nPoints); %# Preallocate a coastal point index
for iPoint = 1:nPoints %# Loop over map points
rho = cos(scale.*lat(iPoint)).*... %# Compute central angles from map
cos(scale.*coastLat).*... %# point to all coastal points
cos(scale.*(coastLong-long(iPoint)))+...
sin(scale.*lat(iPoint)).*...
sin(scale.*coastLat);
d = radiusEarth.*acos(rho); %# Compute great-circle distances
[distanceToCoast(iPoint),coastIndex(iPoint)] = min(d); %# Find minimum
end
%# Visualize the data:
image(longRange,latRange,dem,'CDataMapping','scaled'); %# Display the DEM
set(gca,'DataAspectRatio',[1 1 1],'YDir','normal',... %# Modify some axes
'XLim',longRange,'YLim',fliplr(latRange)); %# properties
colormap([0 0.8 0.8; hot]); %# Add a cyan color to the "hot" colormap
xlabel('Longitude'); %# Label the x axis
ylabel('Latitude'); %# Label the y axis
hold on; %# Add to the plot
plot([long; coastLong(coastIndex).'],... %'# Plot the inland points and
[lat; coastLat(coastIndex).'],... %'# nearest coastal points
'wo-');
str = strcat(num2str(distanceToCoast.',... %'# Make text for the distances
'%0.1f'),{' miles'});
text(long,lat,str,'Color','w','VerticalAlignment','bottom'); %# Plot the text
And here's the resulting figure:
I guess that puts me almost 400 miles from the nearest "ocean" coastline (in actuality, it's probably the Intracoastal Waterway).

load coast;
axesm('mercator');
plotm(lat,long)
There are other datasets in the same directory as coast.mat that might be more useful.
I would then just find the distance to all the points in the dataset, and take the shortest distance. This would assume that coastlines outside of the US are acceptable answers. You will want to use the distance function since Euclidean geometry does not apply here.

Gnovice's answer was nice and useful for the future but I did not need that high of fidelity and didn't want to spend extra timing converting from pixel distance to latitude/longitude distance. Taking MatlabDoug's answer, I wrote the following script:
% Get Data
coast = load('coast.mat');
locations = load('locations.mat');
% Preallocate
coast_indexes = nan(size(locations.lat));
distancefromcoast = nan(size(locations.lat));
% Find distance and corresponding coastal point
for i=1:1:numel(locations.lat)
[dist, az] = distance(locations.lat(i), locations.long(i), coast.lat, coast.long);
[distancefromcoast(i),coast_indexes(i)] = min(dist);
end

Related

MATLAB: pdist, finding pixel locations of minimum pairwise distance in binary image

I have a binary image and have found the minimum distance connecting two nearby regions of interest/connected components using
min(min(pdist2(CCstats(1).PixelList,CCstats(2).PixelList)))
I also need to get the coordinates of these ends of this distance/the most adjacent pixels between these 2 regions of interest
I plan on drawing a line along this distance. I was going to use something like:
x=linspace(coord1(1), coord2(1),1000);
y=linspace(coord1(2), coord2(2),1000);
index=sub2ind(size(image),round(y),round(x));
image(index)=1;
Any suggestions for how to find these coordinates?
You will want the second output of min (combined with ind2sub) to determine the row/column of the pdist2 matrix that corresponds to the minimum distance.
distances = pdist2(CCstats(1).PixelList, CCstats(2).PixelList);
[mindist, minind] = min(distances(:));
%// Now convert this linear index into index1/index2 into your pixel lists
[index1, index2] = ind2sub(size(distances), minind);
%// Now grab the coordinates using these index values
coord1 = CCstats(1).PixelList(index1,:);
coord2 = CCstats(2).PixelList(index2,:);

Centroid calculation for a connected component in 3D volume using Matlab

I am trying to implement brain tumor segmentation on 3D brain MRI(.mha data type).
After preliminary segmentation, I am applying 26-neighbor connected component algorithm(using bwconncomp) to obtain the largest connected component by obtaining the component with the largest volume, following which I need to calculate the centroid of the resultant component.
I am not sure if my method of calculating the largest connected component and the centroid is correct, because the centroid obtained and its nearby voxels all have value 0.
Also I am having confusion with the representation of 3D voxel coordinates. For eg. if centroid=(x,y,z), does it correspond to x=row,y=column and z=2D slice?
Any help would be appreciated. Below is my code with the relevant part.
CC=bwconncomp(Ibin,26); %Input Black & White 3D data of size 240x240x155
Pixelid=regionprops(CC,'PixelIdxList');
[prow pcol]=size(Pixelid);
maxval=numel(Pixelid(1).PixelIdxList);
index=1;
for i=1:prow
number=numel([Pixelid(i).PixelIdxList]);
if (number>maxval) %calculating the component with max number of voxels
maxval=number;
index=i;
end
end
for i=1:prow
if i~=index
Ibin(Pixelid(i).PixelIdxList)=0;
end
end
CC1=bwconncomp(Ibin,26);
Cent=regionprops(CC1,'Centroid');
I changed your code to the following:
CC=bwconncomp(Ibin,26);
PixelIdxList = CC.PixelIdxList;
maxval = numel(PixelIdxList{1});
index = 1;
for ii = 1:length(PixelIdxList)
number = numel(PixelIdxList{ii});
if number > maxval
maxval = number;
index = ii;
end
end
[y,x,z] = ind2sub(size(Ibin),PixelIdxList{index})
centroid = [mean(x), mean(y), mean(z)];
bwconncomp already gives you a PixelIdxList so you don't have to use regionprops. The PixelIdxList lists pixels by their linear indices, so you have to convert them into subscripts to get x, y, and z coordinates. The first dimension in MATLAB matrix represents y coordinates, and second dimension represents x, while the third dimension represents z. Centroid is calculated by taking the mean x, y, and z coordinates of all the pixels contained in the object.

Calculate circular bins around a point + matlab

My question is related to this link stackoverflow ques
In essence repeating the figure drawn there .. I have a central point ( x , y ) in an image around which I have to draw 4 circles of 1-4 unit radius with 8 angles between them.
In this diagram there are 12 angle bins but I have 8. There is a code solution there but it is for plotting the above figure.
I want to calculate the maximum intensity point in each of the 4 regions of each wedge. Is there any inbuilt function in matlab ? I looked at rose but could'nt understand if it would help me....
I would greatly appreciate if someone could help me how to calculate it in matlab....
Thanks
I put some code below that should be the basic skeleton of what you want to do. But I left an important function unimplemented because I think you will be able to do it and it will help you understand this process better.
% I assume that data_points is an M-by-2 array, where each row corresponds
% to an (x,y) coordinate pair, and M is the number of data points.
data_points = ... ;
% I assume this array stores the intensities at each data point.
intensities = ... ;
% I assume that this stores the total number of gridded polar regions you want
% to find the max intensity in (i.e. 4*(number of cells) in your picture above).
total_num_bins = ... ;
% This will store the max intensities. For places that have no nearby
% data points, the max intensity will remain zero.
max_intensities = zeros(total_num_bins);
% I assume these store the values of the center point.
x = ... ; y = ... ;
% The number of different data points.
num_data_points = length(intensities); % also equals size(data_points,1)
% Now, loop through the data points, decide which polar bin they fall in, and
% update the max intensity of that area if needed.
for ii = 1:num_data_points
% Grab the current point coordinates.
cur_x = data_points[ii,1];
cur_y = data_points[ii,2];
% Convert the current data point to polar coordinates,
% keeping in mind that we are treating (x,y) like the center.
cur_radius = sqrt( (cur_x - x)^2 + (cur_y - y)^2 );
cur_angle = atan2(cur_y - y, cur_x - x)
% You have to write this yourself, but it
% will return an index for the bin that this
% data point falls into, i.e. which of the 4 segments
% of one of the radial cells it falls into.
cur_bin = get_bin_number(cur_radius, cur_angle);
% Check if this data point intensity is larger than
% the current max value for its bin.
if ( intensities(ii) >= max_intensities(cur_bin))
max_intensities(cur_bin) = intensities(ii);
end
end
You will now have to make the function get_bin_number() which takes as its input the angle and radius of the data point away from the center point. It should return just an index between 1 and total_num_bins, because you will be keeping the max intensities in a linear array. So, for example, index number 1 might correspond to the first 1/4 piece of the closest radial cell in the upper right quadrant, index 2 might correspond to the next 1/4 of that same cell, moving counter-clockwise, or something like this. You have to devise your own convention for keeping track of the bins.
A late answer, but I believe an even easier solution would just be to convert your data from (x,y) coordinates to (r,theta) by using (r = sqrt(x.^2 + y.^2), theta = atan(y,x)) then use the hist3 function on the (r,theta) data set to get a radial histogram.
Therefore solution is as follows:
% I assume you have some M-by-2 matrix X that's in the form (x,y)
% Convert (x,y) to (r,theta)
xVect = X(:,1);
yVect = X(:,2);
X = [sqrt(xVect.^2 + yVect.^2), ...%formula for r
atan(yVect,xVect)]; %formula for theta
% 5 is the number of wedges along 'r', your radial axis
% 12 is the number of wedges along 'theta', your theta 'axis'
dist = hist3(X,5,12);
Even if you have solved this, I hope this helps anybody else who wants to create a radial/angular histogram!

How to draw a straight across the centroid points of the barcode using best fit points Matlab

This is the processed image and I can't increase the bwareaopen() as it won't work for my other image.
Anyway I'm trying to find the shortest points in the centre points of the barcode, to get the straight line across the centre points in the barcode.
Example:
After doing a centroid command, the points in the barcode are near to each other. Therefore, I just wanted to get the shortest points(which is the barcode) and draw a straight line across.
All the points need not be join, best fit points will do.
Step 1
Step 2
Step 3
If you dont have the x,y elements Andrey uses, you can find them by segmenting the image and using a naive threshold value on the area to avoid including the number below the bar code.
I've hacked out a solution in MATLAB doing the following:
Loading the image and making it binary
Extracting all connected components using bwlabel().
Getting useful information about each of them via regionprops() [.centroid will be a good approximation to the middel point for the lines].
Thresholded out small regions (noise and numbers)
Extracted x,y coordinates
Used Andreys linear fit solution
Code:
set(0,'DefaultFigureWindowStyle','docked');
close all;clear all;clc;
Im = imread('29ekeap.jpg');
Im=rgb2gray(Im);
%%
%Make binary
temp = zeros(size(Im));
temp(Im > mean(Im(:)))=1;
Im = temp;
%Visualize
f1 = figure(1);
imagesc(Im);colormap(gray);
%Find connected components
LabelIm = bwlabel(Im);
RegionInfo = regionprops(LabelIm);
%Remove background region
RegionInfo(1) = [];
%Get average area of regions
AvgArea = mean([RegionInfo(1:end).Area]);
%Vector to keep track of likely "bar elements"
Bar = zeros(length(RegionInfo),1);
%Iterate over regions, plot centroids if area is big enough
for i=1:length(RegionInfo)
if RegionInfo(i).Area > AvgArea
hold on;
plot(RegionInfo(i).Centroid(1),RegionInfo(i).Centroid(2),'r*')
Bar(i) = 1;
end
end
%Extract x,y points for interpolation
X = [RegionInfo(Bar==1).Centroid];
X = reshape(X,2,length(X)/2);
x = X(1,:);
y = X(2,:);
%Plot line according to Andrey
p = polyfit(x,y,1);
xMin = min(x(:));
xMax = max(x(:));
xRange = xMin:0.01:xMax;
yRange = p(1).*xRange + p(2);
plot(xRange,yRange,'LineWidth',2,'Color',[0.9 0.2 0.2]);
The result is a pretty good fitted line. You should be able to extend it to the ends by using the 'p' polynomal and evaluate when you dont encounter any more '1's if needed.
Result:
If you already found the x,y of the centers, you should use polyfit function:
You will then find the polynomial coefficients of the best line. In order to draw a segment, you can take the minimal and maximal x
p = polyfit(x,y,1);
xMin = min(x(:));
xMax = max(x(:));
xRange = xMin:0.01:xMax;
yRange = p(1).*xRange + p(2);
plot(xRange,yRange);
If your ultimate goal is to generate a line perpendicular to the bars in the bar code and passing roughly through the centroids of the bars, then I have another option for you to consider...
A simple solution would be to perform a Hough transform to detect the primary orientation of lines in the bar code. Once you find the angle of the lines in the bar code, all you have to do is rotate that by 90 degrees to get the slope of a perpendicular line. The centroid of the entire bar code can then be used as an intercept for this line. Using the functions HOUGH and HOUGHPEAKS from the Image Processing Toolbox, here's the code starting with a cropped version of your image from step 1:
img = imread('bar_code.jpg'); %# Load the image
img = im2bw(img); %# Convert from RGB to BW
[H, theta, rho] = hough(img); %# Perform the Hough transform
peak = houghpeaks(H); %# Find the peak pt in the Hough transform
barAngle = theta(peak(2)); %# Find the angle of the bars
slope = -tan(pi*(barAngle + 90)/180); %# Compute the perpendicular line slope
[y, x] = find(img); %# Find the coordinates of all the white image points
xMean = mean(x); %# Find the x centroid of the bar code
yMean = mean(y); %# Find the y centroid of the bar code
xLine = 1:size(img,2); %# X points of perpendicular line
yLine = slope.*(xLine - xMean) + yMean; %# Y points of perpendicular line
imshow(img); %# Plot bar code image
hold on; %# Add to the plot
plot(xMean, yMean, 'r*'); %# Plot the bar code centroid
plot(xLine, yLine, 'r'); %# Plot the perpendicular line
And here's the resulting image:

Measuring weighted mean length from an electrophoresis gel image

Background:
My question relates to extracting feature from an electrophoresis gel (see below). In this gel, DNA is loaded from the top and allowed to migrate under a voltage gradient. The gel has sieves so smaller molecules migrate further than longer molecules resulting in the separation of DNA by length. So higher up the molecule, the longer it is.
Question:
In this image there are 9 lanes each with separate source of DNA. I am interested in measuring the mean location (value on the y axis) of each lane.
I am really new to image processing, but I do know MATLAB and I can get by with R with some difficulty. I would really appreciate it if someone can show me how to go about finding the mean of each lane.
Here's my try. It requires that the gels are nice (i.e. straight lanes and the gel should not be rotated), but should otherwise work fairly generically. Note that there are two image-size-dependent parameters that will need to be adjusted to make this work on images of different size.
%# first size-dependent parameter: should be about 1/4th-1/5th
%# of the lane width in pixels.
minFilterWidth = 10;
%# second size-dependent parameter for filtering the
%# lane profiles
gaussWidth = 5;
%# read the image, normalize to 0...1
img = imread('http://img823.imageshack.us/img823/588/gele.png');
img = rgb2gray(img);
img = double(img)/255;
%# Otsu thresholding to (roughly) find lanes
thMsk = img < graythresh(img);
%# count the mask-pixels in each columns. Due to
%# lane separation, there will be fewer pixels
%# between lanes
cts = sum(thMsk,1);
%# widen the local minima, so that we get a nice
%# separation between lanes
ctsEroded = imerode(cts,ones(1,minFilterWidth));
%# use imregionalmin to identify the separation
%# between lanes. Invert to get a positive mask
laneMsk = ~repmat(imregionalmin(ctsEroded),size(img,1),1);
Image with lanes that will be used for analysis
%# for each lane, create an averaged profile
lblMsk = bwlabel(laneMsk);
nLanes = max(lblMsk(:));
profiles = zeros(size(img,1),nLanes);
midLane = zeros(1,nLanes);
for i = 1:nLanes
profiles(:,i) = mean(img.*(lblMsk==i),2);
midLane(:,i) = mean(find(lblMsk(1,:)==i));
end
%# Gauss-filter the profiles (each column is an
%# averaged intensity profile
G = exp(-(-gaussWidth*5:gaussWidth*5).^2/(2*gaussWidth^2));
G=G./sum(G);
profiles = imfilter(profiles,G','replicate'); %'
%# find the minima
[~,idx] = min(profiles,[],1);
%# plot
figure,imshow(img,[])
hold on, plot(midLane,idx,'.r')
Here's my stab at a simple template for an interactive way to do this:
% Load image
img = imread('gel.png');
img = rgb2gray(img);
% Identify lanes
imshow(img)
[x,y] = ginput;
% Invert image
img = max(img(:)) - img;
% Subtract background
[xn,yn] = ginput(1);
noise = img((yn-2):(yn+2), (xn-2):(xn+2));
noise = mean(noise(:));
img = img - noise;
% Calculate means
means = (1:size(img,1)) * double(img(:,round(x))) ./ sum(double(img(:,round(x))), 1);
% Plot
hold on
plot(x, means, 'r.')
The first thing to do to is convert your RGB image to grayscale:
gr = rgb2gray(imread('gelk.png'));
Then, take a look at the image intensity histogram using imhist. Notice anything funny about it? Use imcontrast(imshow(gr)) to pull up the contrast adjustment tool. I found that eliminating the weird stuff after the major intensity peak was beneficial.
The image processing task itself can be divided into several steps.
Separate each lane
Identify ('segment') the band in each lane
Calculate the location of the bands
Step 1 can be done "by hand," if the lane widths are guaranteed. If not, the line detection offered by the Hough transform is probably the way to go. The documentation on the Image Processing Toolbox has a really nice tutorial on this topic. My code recapitulates that tutorial with better parameters for your image. I only spent a few minutes with them, I'm sure you can improve the results by tuning the parameters further.
Step 2 can be done in a few ways. The easiest technique to use is Otsu's method for thresholding grayscale images. This method works by determining a threshold that minimizes the intra-class variance, or, equivalently, maximizes the inter-class variance. Otsu's method is present in MATLAB as the graythresh function. If Otsu's method isn't working well you can try multi-level Otsu or a number of other histogram based threshold determination methods.
Step 3 can be done as you suggest, by calculating the mean y value of the segmented band pixels. This is what my code does, though I've restricted the check to just the center column of each lane, in case the separation was off. I'm worried that the result may not be as good as calculating the band centroid and using its location.
Here is my solution:
function [locations, lanesBW, lanes, cols] = segmentGel(gr)
%%# Detect lane boundaries
unsharp = fspecial('unsharp'); %# Sharpening filter
I = imfilter(gr,unsharp); %# Apply filter
bw = edge(I,'canny',[0.01 0.3],0.5); %# Canny edges with parameters
[H,T,R] = hough(bw); %# Hough transform of edges
P = houghpeaks(H,20,'threshold',ceil(0.5*max(H(:)))); %# Find peaks of Hough transform
lines = houghlines(bw,T,R,P,'FillGap',30,'MinLength',20); %# Use peaks to identify lines
%%# Plot detected lines above image, for quality control
max_len = 0;
imshow(I);
hold on;
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
plot(xy(:,1),xy(:,2),'LineWidth',2,'Color','green');
%# Plot beginnings and ends of lines
plot(xy(1,1),xy(1,2),'x','LineWidth',2,'Color','yellow');
plot(xy(2,1),xy(2,2),'x','LineWidth',2,'Color','red');
%# Determine the endpoints of the longest line segment
len = norm(lines(k).point1 - lines(k).point2);
if ( len > max_len)
max_len = len;
end
end
hold off;
%%# Use first endpoint of each line to separate lanes
cols = zeros(length(lines),1);
for k = 1:length(lines)
cols(k) = lines(k).point1(1);
end
cols = sort(cols); %# The lines are in no particular order
lanes = cell(length(cols)-1,1);
for k = 2:length(cols)
lanes{k-1} = im2double( gr(:,cols(k-1):cols(k)) ); %# im2double for compatibility with greythresh
end
otsu = cellfun(#graythresh,lanes); %# Calculate threshold for each lane
lanesBW = cell(size(lanes));
for k = 1:length(lanes)
lanesBW{k} = lanes{k} < otsu(k); %# Apply thresholds
end
%%# Use segmented bands to determine migration distance
locations = zeros(size(lanesBW));
for k = 1:length(lanesBW)
width = size(lanesBW{k},2);
[y,~] = find(lanesBW{k}(:,round(width/2))); %# Only use center of lane
locations(k) = mean(y);
end
I suggest you carefully examine not only each output value, but the results from each step of the function, before using it for actual research purposes. In order to get really good results, you will have to read a bit about Hough transforms, Canny edge detection and Otsu's method, and then tune the parameters. You may also have to alter how the lanes are split; this code assumes that there will be lines detected on either side of the image.
Let me add another implementation similar in concept to that of #JohnColby's, only without the manual user-interaction:
%# read image
I = rgb2gray(imread('gele.png'));
%# middle position of each lane
%# (assuming lanes are somewhat evenly spread and of similar width)
x = linspace(1,size(I,2),10);
x = round( (x(1:end-1)+x(2:end))./2 );
%# compute the mean value across those columns
m = mean(I(:,x));
%# find the y-indices of the mean values
[~,idx] = min( bsxfun(#minus, double(I(:,x)), m) );
%# show the result
figure(1)
imshow(I, 'InitialMagnification',100, 'Border','tight')
hold on, plot(x, idx, ...
'Color','r', 'LineStyle','none', 'Marker','.', 'MarkerSize',10)
and applied on the smaller image: