Paste directly from clipboard into matlab image array

Alec Jacobson

October 12, 2014

weblog/

I find myself often wanting to experiment with images from papers I'm reading. To load the image into matlab properly I should extract it from the pdf, save it in a file and load the file via matlab. Often I skip the first step and just take a screengrab. But I still need to create a dummy file just to load it into matlab.

Here's a mex function to load images directly from the clipboard. It even maintains all color channels (RGB, CMYK, RGBA, etc.)

Save this in a file paste.h:

#include <cstddef>
// Get image from clipboard as an RGB image
//
// Outputs:
//   IM  h*w*c list of rgb pixel color values as uint8, running over height,
//     then width, then rgb channels
//   h  height
//   w  width
//   c  channels
bool paste(unsigned char *& IM,size_t & h, size_t & w, size_t & c);

and in paste.mm:

#import "paste.h"
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#import <unistd.h>

bool paste(unsigned char *& IM,size_t & h, size_t & w, size_t & c)
{
  NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
  NSArray *classArray = [NSArray arrayWithObject:[NSImage class]];
  NSDictionary *options = [NSDictionary dictionary];
  BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options]; 
  if(!ok)
  {
    //printf("Error: clipboard doesn't seem to contain image...");
    return false;
  }
  NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options];
  NSImage *image = [objectsToPaste objectAtIndex:0];
  NSBitmapImageRep* bitmap = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]];
  // http://stackoverflow.com/a/19649616/148668
  w = [bitmap pixelsWide];
  h = [bitmap pixelsHigh];
  size_t rowBytes = [bitmap bytesPerRow];
  c = rowBytes/w;
  unsigned char* pixels = [bitmap bitmapData];
  IM = new unsigned char[w*h*c];
  for(size_t y = 0; y < h ; y++)
  {
    for(size_t x = 0; x < w; x++)
    {
      for(size_t d = 0;d<c;d++)
      {
        // For some reason colors are invertex
        IM[y+h*(x+d*w)] = pixels[y*rowBytes + x*c + d];
      }
    }
  }
  [image release];
  return true;
}

and in impaste.cpp

#ifdef MEX
#include <mex.h>
#include "paste.h"
#include <iostream>

void mexFunction(
  int nlhs, mxArray *plhs[], 
  int nrhs, const mxArray *prhs[])
{
  unsigned char * IM;
  size_t h,w,c;
  if(!paste(IM,h,w,c))
  {
    mexErrMsgTxt("Clipboard doesn't contain image.");
  }
  switch(nlhs)
  {
    default:
    {
      mexErrMsgTxt("Too many output parameters.");
    }
    case 1:
    {
      size_t dims[] = {h,w,c};
      plhs[0] = mxCreateNumericArray(3,dims,mxUINT8_CLASS,mxREAL);
      unsigned char * IMp = (unsigned char *)mxGetData(plhs[0]);
      std::copy(IM,IM+w*h*c,IMp);
      // Fall through
    }
    case 0: break;
  }
}

#endif

Then compile on Mac OS X using something like:

mex -v -largeArrayDims -DMEX CXX='/usr/bin/clang++' LD='/usr/bin/clang++' LDOPTIMFLAGS='-O ' LDFLAGS='\$LDFLAGS -framework Foundation -framework AppKit' -o impaste impaste.cpp paste.mm

Notice, I'm forcing the mex compiler to use clang and include the relevant frameworks.

Then you can call from matlab using something like:

IM = impaste();

or

imshow(impaste());