抓虾帮你轻松订阅、收藏、分享博客和新闻等。 订阅 关闭
Steve on Image Processing Steve Eddins manages the Image & Geospatial develo...
共有158篇 | 以下是第41-50篇 | 只浏览标题 <   1   2   3   4   5   6   7   8  >  

When I started this blog in early 2006, one of my first series of articles was called "All About Pixel Colors." These articles discussed how MATLAB associates matrix values with specific screen pixel colors. The series talked about the two fundamental image display models (indexed and truecolor); the scaled indexed image variation; using indexed scaling to program brightness/contrast controls; and the grayscale and binary image conventions in the Image Processing Toolbox.

This material might be useful for people who have started reading this blog since then, so I've used our new category archive pages to gather the material together. In the sidebar on the right, under Categories, click on "Pixel colors."

Or, if you prefer, you can look at my MATLAB Digest article that was based on the blog postings.

I am slowly making more use of categories in my blog. If you have category suggestions, please let me know.

 折叠
发给朋友   转到小组   (打标签) 收藏   推荐  

I'd like to welcome back guest blogger Stan Reeves, professor of Electrical and Computer Engineering at Auburn University. Stan will be writing a few blogs here about image deblurring.

In my last blog, I looked at image deblurring using an inverse filter and some variations. The inverse filter does a terrible job due to the fact that it divides in the frequency domain by numbers that are very small, which amplifies any observation noise in the image. In this blog, I'll look at a better approach, based on the Wiener filter.

I will continue to assume a shift-invariant blur model with independent additive noise, which is given by

y(m,n) = h(m,n)*x(m,n) + u(m,n)

where * is 2-D convolution, h(m,n) is the point-spread function (PSF), f(m,n) is the original image, and u(m,n) is noise.

The Wiener filter can be understood better in the frequency domain. Suppose we want to design a frequency-domain filter G(k,l) so that the restored image is given by

We can choose G(k,l) so that we minimize

E[] is the expected value of the expression. We assume that both the noise and the signal are random processes and are independent of one another. The minimizer of this expression is given by

This filter gives the minimum mean-square error estimate of X(k,l). Now that sounds really great until we realize that we must supply the signal and noise power spectra (or equivalently the autocorrelation functions of the signal and noise).

The noise power spectrum is fairly easy. We usually assume white noise, which makes the noise power spectrum a constant. But how do we determine what this constant is? If

then the noise power spectrum is given by

for an MxN image. This noise variance may be known based on knowledge of the image acquisition process or may be estimated from the local variance of a smooth region of the image.

The signal power spectrum is a little more challenging in principle, since it is not flat. However, we have two factors working in our favor: 1) most images have fairly similar power spectra, and 2) the Wiener filter is insensitive to small variations in the signal power spectrum.

Consider two very different images -- the cameraman and house.

cam = im2double(imread('cameraman.tif'));
house_url = 'http://blogs.mathworks.com/images/steve/180/house.tif';
house = im2double(imread(house_url));

subplot(1,2,1)
imshow(cam)
colormap(gray(256))
title('cameraman')
subplot(1,2,2)
imshow(house)
title('house')

We show the actual (log) spectrum for these two images.

cf = abs(fft2(cam)).^2;
hf = abs(fft2(house)).^2;
subplot(1,2,1)
surf([-127:128]/128,[-127:128]/128,log(fftshift(cf)+1e-6))
shading interp, colormap gray
title('cameraman power spectrum')
subplot(1,2,2)
surf([-127:128]/128,[-127:128]/128,log(fftshift(hf)+1e-6))
shading interp, colormap gray
title('house power spectrum')

Let's see what happens if we restore the cameraman with the actual power spectrum.

h = fspecial('disk',4);
cam_blur = imfilter(cam,h,'circular');
% 40 dB PSNR
sigma_u = 10^(-40/20)*abs(1-0);
cam_blur_noise = cam_blur + sigma_u*randn(size(cam_blur));

cam_wnr = deconvwnr(cam_blur_noise,h,numel(cam)*sigma_u^2./cf);
subplot(1,1,1)
imshow(cam_wnr)
colormap(gray(256))
title('restored image with exact spectrum')

For comparison purposes, we restore the cameraman using the power spectrum obtained from the house image.

cam_wnr2 = deconvwnr(cam_blur_noise,h,numel(cam)*sigma_u^2./hf);
imshow(cam_wnr2)
colormap(gray(256))
title('restored image with house spectrum')

Visually, the two are very similar in quality. In terms of mean-square error (MSE), the former is better (lower), as the theory predicts:

format short e
mse1 = mean((cam(:)-cam_wnr(:)).^2)
mse2 = mean((cam(:)-cam_wnr2(:)).^2)
mse1 =

  2.9905e-003


mse2 =

  3.9191e-003

In both cases, the spectrum is concentrated in the low frequencies and falls away at higher frequencies. A smoothed version of the spectra would look even more similar. Rather than using the power spectrum from a specific image, one can either average a large number of images or use a simple model of the power spectrum or autocorrelation function.

A common model for the image autocorrelation function is

We calculate these parameters for the cameraman, averaging over horizontal and vertical shifts to get a single parameter for the correlation coefficient. Then we calculate an autocorrelation function.

sigma2_x = var(cam(:))
mean_x = mean(cam(:))
cam_r = circshift(cam,[1 0]);
cam_c = circshift(cam,[0 1]);
rho_mat = corrcoef([cam(:); cam(:)],[cam_r(:); cam_c(:)])
rho = rho_mat(1,2);
[rr,cc] = ndgrid([-128:127],[-128:127]);
r_x = sigma2_x*rho.^sqrt(rr.^2+cc.^2) + mean_x^2;

surf([-128:127],[-128:127],r_x)
axis tight
shading interp, camlight, colormap jet
title('image autocorrelation model approximation')
sigma2_x =

  5.9769e-002


mean_x =

  4.6559e-001


rho_mat =

  1.0000e+000  9.4492e-001
  9.4492e-001  1.0000e+000

From this we calculate another restored image:

cam_wnr3 = deconvwnr(cam_blur_noise,h,sigma_u^2,r_x);
imshow(cam_wnr3)
colormap(gray(256))
title('restored image using correlation model')
mse3 = mean((cam(:)-cam_wnr3(:)).^2)
mse3 =

  3.5255e-003

The restored image from this method is better than the restored image using the house spectrum but not quite as good as the one using the exact cameraman spectrum. All in all, we can see that the exact noise-to-signal spectrum isn't necessary to yield good results.

- by Stan Reeves, Department of Electrical and Computer Engineering, Auburn University


Get the MATLAB code

Published with MATLAB® 7.5

 折叠
发给朋友   转到小组   (打标签) 收藏   推荐  

In September I wrote about customer-reported speed issues related to TIFF files containing a very large number (thousands or tens of thousands) of images. I've started to call these things MMTs (massively multipage TIFFs).

If you have experienced this kind of performance problem and you have an MMT that you'd be willing to share with us, please let me know. We can synthesize files here, of course, but we prefer to get our hands on "real-world" files whenever possible.

Just send me an e-mail, and then we'll work out a method for transferring the file.

Thanks for your help.

 折叠
发给朋友   转到小组   (打标签) 收藏   推荐  
bwselect 查看全文   2007-10-26 20:00:02

Didja know about the Image Processing Toolbox function bwselect?

For a blog post I wrote earlier this month, I wanted a binary image containing only the central blob indicated in red below:


url = 'http://blogs.mathworks.com/images/steve/163/plateaus.png';
bw = imread(url);
imshow(bw)
annotation(gcf,'ellipse','LineWidth',3,...
    'Position',[0.4931 0.463 0.07575 0.2029],...
    'Color',[0.8471 0.1608 0]);

The Image Processing Toolbox function bwselect is perfect for this task. You can use it interactively or noninteractively.

If you use the interactive syntax:

 bw2 = bwselect(bw)

the function displays bw and waits for you to click on the image with the mouse. You can click on one or multiple points. To finish your selection, double-click on the last point, or just press RETURN. bwselect then returns a binary image containing only the objects ("blobs") that you clicked on.

You can also use the noninteractive syntax, in which you pass in the point locations as input arguments instead of using the mouse.

For example:


pond = bwselect(bw, 183, 170);
clf
imshow(pond)

I hope you find this function useful.


Get the MATLAB code

Published with MATLAB® 7.5

 折叠
发给朋友   转到小组   (打标签) 收藏   推荐  
Upslope area - Summary 查看全文   2007-10-23 20:00:10

Back in March I started writing about an algorithm implementation experiment for computing upslope area. Given an "image" whose pixel values are terrain elevations, the upslope area of a pixel is the area of the uphill terrain that drains through that pixel. I chose a paper that looked promising, and I wrote about my progress as I implemented the paper's techniques in MATLAB. Those implementations are available in an "upslope toolbox" on the MATLAB Central File Exchange.

Some of you have probably been scratching your heads, wondering why I've been writing so much about water flow and terrain analysis in an image processing blog. I want to conclude with some thoughts on that very point.

First, this series illustrates quite a few different image processing algorithm implementation techniques. If you implement image processing algorithms in MATLAB, I think it will be well worth your time to take a look. Here are a few of the useful nuggets you'll find:

  • Using morphological functions to find pixels with certain relationships to their neighbors. For example, using imerode and relational operators to find all pixels that have no downhill neighbors.
  • Using a binary image as a logical mask to index into another image. I use this technique all the time, and you should, too.
  • Using linear indexing to process sets of pixels. (Essential!)
  • Using linear index offsets to find neighbors of a set of pixels ( "neighbor indexing").
  • Identifying connected groups of pixels that touch the image border.
  • Using a variety of Image Processing Toolbox functions, such as imregionalmin, roifill, bwselect, etc., to achieve different effects.
  • Using sparse linear systems to solve for pixel values that are linearly related to neighboring pixel values.
  • Visualizing algorithm output:
    • By superimposing a quiver plot on an image.
    • By superimposing one image transparently on another.

I also believe that terrain analysis techniques like this will continue to find application to other kinds of image processing and image analysis problems, just like the watershed transform did. Much of mathematical morphology applied to image processing is about ordering relationships between pixels and their neighbors, and the upslope area problem fits right into that category. In particular, I would guess that the influence and dependence maps will be useful for a variety of problems.

And hey, it was fun! And if I'm going to keeping writing new content here every week, it's just gotta be fun!

Enjoy.

 折叠
发给朋友   转到小组   (打标签) 收藏   推荐  
Finding bright objects 查看全文   2007-10-19 20:00:58

In a blog comment earlier this summer, someone asked how to find "which labeled regions have a bright spot greater than some threshold?" That's a good question. It can be done efficiently and compactly using the Image Processing Toolbox, but the techniques may not be widely known. (Have you ever used the ismember function to do image processing before?) Also, the techniques apply to other questions of the form "which regions satisfy some criterion?"

Let's work with the rice image.


I = imread('rice.png');
imshow(I)

Threshold it.


bw = im2bw(I, graythresh(I));
imshow(bw)

Clean it up a bit.


bw = bwareaopen(bw, 50);
imshow(bw)

Now label connected components and compute the PixelIdxList property for each labeled region. The PixelIdxList is useful for the extracting pixels from a grayscale image that correspond to each labeled region in the binary image. (See my post "Grayscale pixel values in labeled regions.")


L = bwlabel(bw);
s = regionprops(L,'PixelIdxList');

Compute the maximum pixel value for each labeled region.


% Initialize vector containing max values.
max_value = zeros(numel(s), 1);

% Loop over each labeled object, grabbing the gray scale pixel values using
% PixelIdxList and computing their maximum.
for k = 1:numel(s)
    max_value(k) = max(I(s(k).PixelIdxList));
end

% Show all the maximum values as a bar chart.
bar(max_value)

And we'll finish by using find to determine which objects have a maximum value greater than 200. Then we'll display those objects using ismember.


bright_objects = find(max_value > 200)
bright_objects =

    22
    28
    33
    35
    40
    42
    49
    51
    60
    65


imshow(ismember(L, bright_objects))


Get the MATLAB code

Published with MATLAB® 7.5

 折叠
发给朋友   转到小组   (打标签) 收藏   推荐  

Since I originally posted my upslope toolbox to MATLAB Central back in August, I have heard from some experts about an issue related to NaNs in the DEM data. Specifically, some data sets record elevation only for DEM pixels within a particular catchment basin. Pixels outside the catchment basin typically are NaN. Some of my code doesn't handle NaN values in the DEM.

After some e-mail correspondence, I settled on this approach:

  • In the pixel flow direction computation, replace groups of NaN pixels that touch the border by a high value. (You can read about the technique for find such pixels in this post.) This replacement makes the flow direction on the watershed pixels point inward, toward the catchment basin.
  • When solving the flow matrix for upslope area, use 0 instead 1 on the right-hand side for exterior NaN pixels. This means that such pixels don't contribute any flow to the system.

Here's a simple example:


[x,y] = meshgrid(-3:3);
E = hypot(x,y);
E(abs(x) + abs(y) > 3) = NaN
E =

       NaN       NaN       NaN    3.0000       NaN       NaN       NaN
       NaN       NaN    2.2361    2.0000    2.2361       NaN       NaN
       NaN    2.2361    1.4142    1.0000    1.4142    2.2361       NaN
    3.0000    2.0000    1.0000         0    1.0000    2.0000    3.0000
       NaN    2.2361    1.4142    1.0000    1.4142    2.2361       NaN
       NaN       NaN    2.2361    2.0000    2.2361       NaN       NaN
       NaN       NaN       NaN    3.0000       NaN       NaN       NaN


R = dem_flow(E);
T = flow_matrix(E, R);
A = upslope_area(E, T)
A =

         0         0         0    1.0000         0         0         0
         0         0    1.0000    2.0000    1.0000         0         0
         0    1.0000    1.8112    4.1888    1.8112    1.0000         0
    1.0000    2.0000    4.1888   25.0000    4.1888    2.0000    1.0000
         0    1.0000    1.8112    4.1888    1.8112    1.0000         0
         0         0    1.0000    2.0000    1.0000         0         0
         0         0         0    1.0000         0         0         0

Notice that the upslope area at the lowest pixel is 25. This is the number of non-NaN pixels in the DEM.

I uploaded version 1.2 of the upslope toolbox, with these NaN-related changes, to the MATLAB Central File Exchange in early October. If you have data sets that use NaNs outside the catchment basin of interest, you might want to download the new version.


Get the MATLAB code

Published with MATLAB® 7.5

 折叠
发给朋友   转到小组   (打标签) 收藏   推荐  

We recently changed the view when you click on a category in the sidebar on the right. I haven't used categories much, but the other bloggers (Loren on the Art of MATLAB, Doug's Pick of the Week, and Inside the MATLAB Desktop) have been.

The new view presents a much more concise summary of all the posts in the category. To take advantage of the new view, I've created two new categories: upslope area and spatial transforms. I'll probably add others later on.

There's also an "Archives" link on the sidebar. This view shows all of the blog's post titles, sorted by month.

Try these links on the MATLAB Central blogs. You might find some useful content that you missed.

 折叠
发给朋友   转到小组   (打标签) 收藏   推荐  

At the beginning of the upslope area series, I posted code showing how to compute the direction of maximum slope for a triangular facet, and how to compute the flow direction for a pixel. This purely scalar code ran fine, but more slowly than I wanted, so I decided to vectorize it before submitting the upslope area code onto MATLAB Central.

Here's the facet code:

s1 = (e0 - e1)/d1;             % eqn (1)
s2 = (e1 - e2)/d2;             % eqn (2)

r = atan2(s2, s1);             % eqn (3)
s = hypot(s1, s2);             % eqn (3)

diagonal_angle    = atan2(d2, d1);
diagonal_distance = hypot(d1, d2);

if r < 0
    % eqn (4)
    r = 0;
    s = s1;

elseif r > diagonal_angle
    % eqn (5)
    r = diagonal_angle;
    s = (e0 - e2)/diagonal_distance;
end

The code that needs to change is the part that deals with special cases (r < 0 and r > diagonal_angle). Instead of using logical tests on the scalar value of r, use logical indexing. Here's the new code fragment, from facet_flow.m in the upslope toolbox:

s1 = (e0 - e1) / d1;           % eqn (1)
s2 = (e1 - e2) / d2;           % eqn (2)

r = atan2(s2, s1);             % eqn (3)
s = hypot(s1, s2);             % eqn (3)

% Constrain flow direction to lie within or along the edges of the facet.
too_far_south = r < 0;
r(too_far_south) = 0;
s(too_far_south) = s1(too_far_south);

diagonal_angle    = atan2(d2, d1);
diagonal_distance = hypot(d1, d2);
too_far_north = r > diagonal_angle;
r(too_far_north) = diagonal_angle;
s(too_far_north) = (e0(too_far_north) - e2(too_far_north)) / ...
    diagonal_distance;

Now the code works when e0, e1, and e2 are matrices.

Here's the original pixel flow code:

% Table 1, page 311
e0 = E(i, j);
e1 = [E(i,j+1) E(i-1,j) E(i-1,j) E(i,j-1) ...
      E(i,j-1) E(i+1,j) E(i+1,j) E(i,j+1)];

e2 = [E(i-1,j+1) E(i-1,j+1) E(i-1,j-1) E(i-1,j-1) ...
      E(i+1,j-1) E(i+1,j-1) E(i+1,j+1) E(i+1,j+1)];

ac = [0  1  1  2  2  3  3  4];
af = [1 -1  1 -1  1 -1  1 -1];

rp = zeros(1, 8);
sp = zeros(1, 8);

for k = 1:8
    [rp(k), sp(k)] = facetFlow(e0, e1(k), e2(k), d1, d2);
end

% Find the facet with the maximum down-slope.
[s, k_max] = max(sp);

if s > 0
    % Maximum down-slope is positive.
    r = (af(k_max) * rp(k_max)) + (ac(k_max) * pi / 2);
else
    % The pixel is in a flat area or is a pit.  Flow direction angle is
    % unresolved.
    r = NaN;
end

This code is a little harder to vectorize. For each pixel, we have to look at each of eight triangular faces. I imagine one could write a version with no loops, but it would require lots of memory and be impossible to understand. So I decided to write a version that looped over the eight face directions. With only eight such directions, loop overhead is insignificant.

The way to compute a particular triangular face for every pixel simultaneously is to use linear indexing to access pixel neighbors. I described this technique, which I called neighbor indexing, in a post last March. The new function pixel_flow uses neighbor indexing to compute triangular facet vertices, E0, E1, and E2, for all pixels at the same time.

Start by computing a table of linear offsets for E1 and E2, for each of the facets:

% Compute linear indices at desired locations.
e0_idx = (j - 1)*M + i;

% Table 1, page 311
% Row and column offsets corresponding to e1 and e2 for each
% table entry:
e1_row_offsets = [0 -1 -1  0  0  1  1  0];
e1_col_offsets = [1  0  0 -1 -1  0  0  1];

e2_row_offsets = [-1 -1 -1 -1  1  1  1  1];
e2_col_offsets = [ 1  1 -1 -1 -1 -1  1  1];


% Linear e1 and e2 offsets.
e1_linear_offsets = e1_col_offsets*M + e1_row_offsets;
e2_linear_offsets = e2_col_offsets*M + e2_row_offsets;

Next, initialize E0, E1, and E2 for the first facet and call the vectorized facet_flow function for that facet:

E0 = E(e0_idx);
E1 = E(e0_idx + e1_linear_offsets(1));
E2 = E(e0_idx + e2_linear_offsets(1));

[R, S] = facet_flow(E0, E1, E2, d1, d2);

Finally, loop over the remaining seven facets. For each new facet, check to see if the downward slope is greater than the one previously recorded. If so, update the facet flow direction.

for k = 2:8
    % Compute Rk and Sk corresponding to the k-th facet. Where Sk is positive
    % and greater than S, replace S and recompute R based on Rk.
    E1 = E(e0_idx + e1_linear_offsets(k));
    E2 = E(e0_idx + e2_linear_offsets(k));
    [Rk, Sk] = facet_flow(E0, E1, E2, d1, d2);
    
    new_R = (Sk > S) & (Sk > 0);
    S = max(S, Sk);
    
    R(new_R) = (af(k) * Rk(new_R)) + (ac(k) * pi / 2);
end

I've skipped over some of the code details. See pixel_flow.m for the full story.

I have one more algorithm post planned in this series, about handling NaNs outside catchments, and then I'll post a final recap of the techniques used and lessons learned.

 折叠
发给朋友   转到小组   (打标签) 收藏   推荐  

In photography and color science, MathWorks developer Jeff Mather's personal interests intersect with his work. He guest-blogged here last year with a post about the CIE Standard Observer. Earlier this month, he posted on his personal blog about high dynamic range (HDR) imaging and associated tone mapping algorithms. If you want to know more about these topics, and the related new features in the Image Processing Toolbox, I encourage you to take a look:

 折叠
发给朋友   转到小组   (打标签) 收藏   推荐  
<   1   2   3   4   5   6   7   8  >