Fog of Code

Random thoughts about code, software and life

Getting supplementary view at specified index in UICollectionView

leave a comment »

the UICollectionView class is a powerful view implementation that manages an ordered collection of data. Although convenient to use most of the time, there are some odd design decisions made by the library developers that can make it really hard to write trivial code. This is one of them.

The class provides the convenient method cellForItemAtIndexPath: that locates a data cell by its index based location in the collection view. However, we know that in addition to data cells, a collection view also contains supplementary views. The default layout even provides two pre-defined supplementary view types:

  • UICollectionElementKindSectionHeader
  • UICollectionElementKindSectionFooter

We could expect that the class would also provide an appropriate supplementaryViewOfKind:atIndexPath: method, but it doesn’t. So what can we do to get the supplementary view if the only thing we know is its index?

First attempt:

UICollectionViewController provides two callbacks:

  • collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath
  • collectionView:viewForSupplementaryElementOfKind:forElementOfKind:atIndexPath

At least theoretically, one could override these methods and manually keep track of the active supplementary views and their index locations. This should actually work if the collection view data, especially the sections, doesn’t change. But what happens when the collection is dynamic and cells are inserted at runtime and sections are added and removed? well, hell breaks loose. There are two sources to the problem. The first is call ordering. It seems that the framework doesn’t guarantee that a collectionView:didEndDisplaying... call would happen right after the supplementary view is removed. It would be called eventually, that’s all we know. So what happens when data changes force a section (and its supplementary views) to be removed and another section to be added in its place? When we try to find a supplementary view by index any of the two can happen:

  • didEndDisplayingSupplementaryView: was already called – great
  • didEndDisplayingSupplementaryView: wasn’t called yet – we have two views with the same index!!!

The second problem is even more serious. Imagine consecutive calls to collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath with the following index paths (assume none is removed):

  • (0, 0) – view1
  • (0, 0) – view2
  • (1, 1) – view3

At first glance something looks wrong. How can two supplementary views share the same index path? Well, they don’t. What happens is that view2 is being inserted in place of view1 and then view3 is being inserted in place of view1 again. Behind the scenes the library keeps track of existing items and updates their index paths by taking into account insertions and deletions. This part is not exposed to common developers like us though. It becomes even harder to manage when taking into account the unpredictable order of the add/remove supplementary view calls.

Solution

The best solution I found relies on the layout attributes of the supplementary views. The following function does the work by comparing the frames of all supplementary views we keep track of with the frame of the supplementary view at the specified index of the specified kind:

- (SUGalleryHeaderView *)headerForSection:(NSInteger)section
{
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:section];
    NSString *kind = UICollectionElementKindSectionHeader;
    UICollectionViewLayoutAttributes *attr = 
        [self.collectionViewLayout layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath];

    for (SUGalleryHeaderView *header in headersActive) {
        if (CGRectEqualToRect(header.frame, attr.frame)) {
            return header;
        }
    }
    return nil;
}

It’s ugly, but it seems to work.

Written by xyand

September 4, 2014 at 7:41 pm

Posted in iOS, objective-c

Tagged with

Old meteor posts

leave a comment »

It has been a while since I updated any of posts to a new Meteor version. Although I believe most of the tips and solutions I wrote about still work, these may be irrelevant or have better solutions using the latest Meteor release. I encourage anyone who finds any of Meteor my posts helpful, to check when it was originally posted and double check for modern solutions.

Written by xyand

September 3, 2014 at 12:35 pm

Posted in Meteor

Running OpenCV on OSX with OpenCL support

leave a comment »

It all started when I wanted to test how a simple algorithm implemented using OpenCV can be optimized using GPU. I chose the simplest path and decided to replace some of the OpenCV functions I was using with their GPU accelerated counterparts. Lets use matrix multiplication as an example. I couldn’t use the gpu (CUDA) module as it is intended for NVIDIA GPUs, while I use MacBook Pro Retina that runs an Intel GPU. Luckily I found the ocl (OpenCL) module. The only problem was that OpenCV doesn’t fully support OpenCL on OSX.

Step #1: Build AMDBLAS and AMDFFT

OSX Mavericks comes with OpenCL libraries pre-installed so no additional installation are required. When building OpenCV from source using CMake you should notice:

Use OpenCL: YES

This means that OpenCV will be built against OpenCL libraries. There is however another important point to notice:

--   OpenCL:
--     Version:                     static
--     libraries:                   -framework OpenCL
--     Use AMDFFT:                  NO
--     Use AMDBLAS:                 NO

If AMDFFT and/or AMDBLAS are indicated as missing, as should be unless you manually installed them, then the compiled OpenCL enables OpenCV would be incomplete and would issue runtime errors when trying to call linear algebra operations such as ocl::gemm and others. To overcome this limitation we need to install AMDBLAS and AMDFFT from AMD’s clMath website. The only problem is that pre-compiled versions are available only for Windows and Linux. Therefore the clAmdFft and clAmdBlas need to be built from source.

Step #2: Build CMake with AMDBLAS and AMDFFT

After building AMDBLAS and AMDFFT we need to tell CMake to include these libraries when building OpenCV. The easiest way I found to do that is by specifying:

-DCLAMDBLAS_ROOT_DIR=~/Work/temp/clBLAS/src/

.

And of course doing the same for AMDFFT. This time CMake would print:

--   OpenCL:
--     Version:                     static
--     libraries:                   -framework OpenCL
--     Use AMDFFT:                  YES
--     Use AMDBLAS:                 YES

From here on I’ll demonstrate only how to use AMDBLAS. AMDFFT should be handled in a similar manner.

Step #3: Load AMDBLASS dynamic library in runtime

Now we should be ready to start coding, but wait… Where would the runtime find the AMDBLAS dynamic library? Diving into the code reveals in modules/core/src/opencl/runtime/opencl_clamdblas.cpp:

 static void* openclamdblas_check_fn(int ID)
 {
     ...
     void* func = CV_CL_GET_PROC_ADDRESS(e->fnName);
     ...
 }

Calling this line in runtime will try to find the specific AMDBLAS function in the default OpenCL library. Which is in our case (OSX): /System/Library/Frameworks/OpenCL.framework/Versions/Current/OpenCL. But as we already know it doesn’t come with AMDBLAS bundled. To overcome this we need to change the function that looks for the AMDBLAS library:

#include <dlfcn.h>
static void* AppleCLGetProcAddress2(const char* name)
{
    static bool initialized = false;
    static void* handle = NULL;
    if (!handle)
    {
        if(!initialized)
        {
            initialized = true;
            const char* path = "/Users/me/Work/temp/clBLAS/src/library/libclBLAS.dylib";

            handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
            if (handle == NULL)
            {
                fprintf(stderr, "WTF?!");
            }
        }
        if (!handle)
            return NULL;
    }
    return dlsym(handle, name);
}

 static void* openclamdblas_check_fn(int ID)
 {
     ...
     void* func = AppleCLGetProcAddress2(e->fnName);
     ...
 }

This is an ugly hack but it works for test purposes, a better solution needs to be integrated in the official version.

Step #4: Almost there

Trying to run some code now will reveal another error, the AMDBLAS functions are still not found. This is due some name/versioning issues of the AMDBLASS functions:

"clAmdBlasSetup" -> "clblasSetup" 
"clAmdBlasSgemmEx" -> "clblasSgemm" 
"clAmdBlasTeardown" -> "clblasTeardown" 

The quick and dirty fix would be to edit /modules/core/src/opencl/runtime/autogenerated/opencl_clamdblas_impl.hpp for every function used:

-static const struct DynamicFnEntry clAmdBlasSetup_definition = { "clAmdBlasSetup", (void**)&clAmdBlasSetup};
+static const struct DynamicFnEntry clAmdBlasSetup_definition = { "clblasSetup", (void**)&clAmdBlasSetup};

I didn’t investigate why the naming conventions are different.

Sample code

This code should work now and show the difference between a naive nearest neighbor and OpenCL accelerated one (C++11). This isn’t the simplest example but that’s what I had:

#include <iostream>

#include <opencv2/ocl.hpp>

using namespace cv;

struct OCL {
  typedef ocl::oclMat MatType;
  
  static void mult(const MatType& A, const MatType&B, MatType& res) {
    //ocl::gemm(A, B.t(), 1.0, MatType(), 0, res, 0);
    
    ocl::BruteForceMatcher_OCL_base bf;
    bf.distType = ocl::BruteForceMatcher_OCL_base::L2Dist;
    std::vector<cv::DMatch> matches;
    bf.match(A, B, matches);
  }
};

struct OCV {
  typedef cv::Mat MatType;
  static void mult(const MatType& A, const MatType& B, MatType& res) {
    res = A * B.t();
  } 
};

template <typename T, typename InterMat = typename T::MatType>
double test(bool isVerbose)
{
  typedef typename T::MatType MatType;
 
   //getTickCount();
  int N = 3000;
  int D = 128;
  cv::Mat mat1(N, D, CV_32FC1); cv::randu(mat1, cv::Scalar(0), cv::Scalar(10));
  cv::Mat mat2(N, D, CV_32FC1); cv::randu(mat2, cv::Scalar(0), cv::Scalar(10));
  MatType res(N, N, CV_32FC1);
 
  auto imat1 = InterMat(mat1);
  auto imat2 = InterMat(mat2);
  
  auto t1 = getTickCount();
  T::mult(MatType(imat1), MatType(imat2), res);
  auto t2 = getTickCount();
  auto d = ((double)t2 - t1)/getTickFrequency(); 
  
  if (isVerbose) {
    std::cout<< d<< "[s]"<< std::endl;
    //std::cout<< T::name<< d << std::endl;
  }

  return d;
}
int main()
{
  ocl::DevicesInfo devices;
  ocl::getOpenCLDevices(devices);
  ocl::setDevice(devices[0]);

  auto isVerbose = true;
  double sum = 0;
  for (auto ii = 0 ; ii < 100 ; ++ii)
  {
    auto d1 = test<OCL, cv::Mat>(isVerbose);
    auto d2 = test<OCV, cv::Mat>(isVerbose);

    double change = d2/d1;
    sum +=  change;
    std::cout<< "Average ["<< ii+1<< "]: "<< round(sum/(ii+1)) << std::endl;
  }

  return 0;
}

Summary

Taking all these steps should help you run some FFT and linear algebra code using OpenCV+OpenCL on OSX. You can find the changes I listed in this post summarized here https://github.com/Xyand/opencv/tree/openclamdblas

Written by xyand

February 23, 2014 at 7:57 am

Posted in OpenCV

Tagged with , , , ,

Staying out of the spam folder

leave a comment »

Recently I noticed that some legitimate emails started finding their way to my Gmail spam folder. I didn’t consider it ¬†an issue until I started noticing that some of the emails I sent were being ignored. Luckily I had other ways to contact the recipients so I asked them to have a look at their spam folder. Surprise surprise, there they were: my emails!

Now, it’s one thing when personal email are being ignored, but a totally different thing when business opportunities get lost.

My Setup

I used Zoho as my company email provider but was sending most of my emails using my personal Gmail account by adding the Zoho account and using “Send mail as“. I was sending 5-10 emails a day, mostly personal but many had similar content . No bulk sending.

Finding #1: “Send mail as” includes the original email address

Return-Path: <personal.email@gmail.com>
Received: from mail-wg0-x22e.google.com 
        (mail-wg0-x22e.google.com [2a00:1450:400c:c00::22e])
        by mx.google.com with ESMTPS id q13si22316841wjr.20.2014.01.06.00.01.39
        for <company.email@domain.com>
        (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128);
        Mon, 06 Jan 2014 00:01:39 -0800 (PST)
Received-SPF: pass 
   (google.com: domain of personal.email@gmail.com 
   designates 2a00:1450:400c:c00::22e as permitted sender) 
   client-ip=2a00:1450:400c:c00::22e;
Authentication-Results: mx.google.com;
   spf=pass 
   (google.com: domain of personal.email@gmail.com 
   designates 2a00:1450:400c:c00::22e as permitted sender) 
   smtp.mail=personal.email@gmail.com;
       dkim=pass header.i=@gmail.com

 

Although it seems that this email was successfully authenticated it raises two questions:

  • Why should others know my personal email address?
  • Does this “send for” help spam filters to mis-detect my emails?

Attempt #1: Not-spam

Hoping that it would make the change I asked my friends to mark all my emails in their spam folder as “not spam”. It didn’t help much as emails kept getting marked as spam.

Attempt #2: Avoid using a proxy

I decided not to take any chances and start sending email directly instead of by proxy. I also moved my mail provider to Google Apps and registered my domain with them.

Some emails still found their way to the spam folder.

Attempt #3: SPF + DKIM

When I started looking for standard means that are used to authenticate email origin, I stumbled upon SPF and DKIM. SPF, Sender Policy Framework, is a mechanism system administrators use to define which hosts can send emails for a specific domain. In my case I want to configure SPF to allow Google Apps servers to send emails from my company domain. DKIM, DomainKeys Identified Email, is a cryptographic mechanism used to associate a domain name with an email message, allowing you to take responsibility for the message and making it hard for other to tamper with the message contents.

Here are the official Google Apps SPF + DKIM instructions

Conclusion

I have no idea why my emails were marked as spam in the first place. I guess Google made some changes to its spam filters as many legitimate emails find their way to the spam folder. I did all I could to be marked as “legit” and from now and on my emails seem find their way to the recipients’ inboxes. I don’t know if it was a spam filter policy change, my attempts to be clean, or some divine intervention, but did the trick.

Written by xyand

January 8, 2014 at 7:11 am

Posted in email

Tagged with , , ,

Meteor photo gallery – memory leaks

leave a comment »

First of all, I want to apologize. Twice. The first is for introducing (at least) two memory leaks in the Meteor photo gallery code I published earlier. The second is for not posting the fix as soon as I discovered it.

Ok, so let’s start.

The first memory leaks is simply a bug that aggregates all preloaded images in an array and never clears them:

Meteor.setInterval(
  ->
    # Function that marks an image as loaded
    load = (id) -> return -> doing -= 1; Session.set("loaded" + id, true); 

Should be removed once loaded:

  Meteor.setInterval(
    ->
      load = (id) -> return -> doing -= 1; Session.set("loaded" + id, true); delete loading[id]

The second leak is trickier. It all started when I abused session variables to store global data. I mentioned that it is a hack and should be changed, but missed the memory leak it introduced. Can you find the leak?

  # This triggers template rendering
  for id in photosVisible
    Session.set("visible" + id, true)
 
  # This triggers template removal
  for id in updatesFalse
    Session.set("visible" + id, false)

The problem is that the session variables never get garbage collected. Being accessed by name there is no way for the GC to figure out that they won’t be used again. So we need to clear them explicitly.

for id in updatesFalse
  Session.set("visible" + id, undefined)
  Session.set("loaded" + id, undefined)

Much better. Now the gallery barely takes any RAM, compared to other galleries around.

Written by xyand

September 29, 2013 at 8:08 am

Posted in javascript, Meteor

Tagged with , , ,

Lightbox ambient background with CSS3

with one comment

 

Nice? Keep reading. This post is 100% inspired by chromatic.io (sample).

So how is it done?

The recipe is quite simple:
1. Place an image in the center of the page
2. Create a blurred background image and add some noise
3. Place the background image so it covers the entire background (zoom)

I’ll show here a CSS3 implementation (didn’t make it cross browser, sorry) to avoid creating/reading the background image. Let’s start.

I assume that you have two version of the image. One for thumbnails and the other for full screen view. The thumbnail would be a better choice for the background image as it is already loaded.

Base HTML

<div class="lightbox">
    <div class="background"></div>
    <div class="noise"></div>
    <img src="http://i.imgur.com/1rsf6jI.jpg"/>
</div>

As you can see we have two placeholders for the background and the noise. Let’s move on to the CSS:

Background

.lightbox {
    position: fixed;
    height: 100%;
    width: 100%;
    background-color: black;
}

.background {
    top: 0;
    left: 0;
    position: absolute;
    width: 20%;
    height: 20%;

    background: url(http://i.imgur.com/1rsf6jI.jpg) no-repeat;
    background-size: cover;
    
    -webkit-filter: blur(3px) brightness(0.8);
    -webkit-transform-origin: top left;
    -webkit-transform: scale(5);
}

There are two interesting things to notice here. The first is that we use -webkit-filter: blur() to create the blur effect. The second is the width: 20%; height: 20% and -webkit-transform: scale(5);. If we remove these lines, everything should look the same, with a small exception. The browser would apply the blur filter pixel-wise on the full size image. Try that and you’ll notice a considerable degradation in performance (when used inside a gallery). Applying the filter on a small image and then scaling the result has the same effect but works much much faster.

Noise

.noise {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-image: url(data:image/png;base64,iV... See fiddle ...ErkJggg==);
  opacity: 0.5;
}

By overlaying an opaque noise image on top of the background we create the granularity effect.

You can test the final result here fiddle

Written by xyand

August 14, 2013 at 4:38 pm

Posted in css, html, Uncategorized

Tagged with , , , ,

CSS transitions with Meteor

leave a comment »

This is my second attempt to do transitions with Meteor. The first time I was new to Meteor and was going against the stream. Eventually it worked, but the code was so sensitive that any small change broke some existing functionality. Mixing Meteor declarative style with imperative js is not fun at all.
This time I decided to try and do it the Meteor way.

The basics

CSS transitions fit nicely with Meteor as it is declarative by nature. Once you define that a certain element has a transition, it is up to the browser to monitor changes and apply the transition when applicable.

.class {
  -webkit-transition: all 0.5s ease;
  -moz-transition: all 0.5s ease;
  -o-transition: all 0.5s ease;
  -ms-transition: all 0.5s ease;
  transition: all 0.5s ease;
}

That was the CSS side of the story. Now lets get back to Meteor. Lets keep in mind Meteor is simply a javascript code that does DOM manipulations with a certain timing. For the transitions to work these DOM updates need to be transition-able. There are certain DOM changes that transitions don’t apply to:

  • Changing display (display:none)
  • Removing element from DOM

The first one is pretty easy and is not Meteor specific. Simply use other means to hide elements. The second one is a little more complicated. So when does Meteor remove an element from the DOM?

  • When it re-renders the element
  • When an element is conditionally included
Re-renders

To avoid removing and re-adding an element upon re-render, we need to ask Meteor to preserve that element via Template.templateName.preserve(). It is straight forward for single element selectors and a little more complicated when a selector matches more than one element. In that case we need to add a unique identifier to each element. And use the following form:

  Template.selectThumbs.preserve(
    # Needed for transitions
    ".wrapper-compare": (node) -> node.getAttribute("data-id")
  )

That way the element is preserved between re-renders. Keep in mind that any property you added to that element dynamically (jQuery, etc.) won’t be preserved.

Conditional elements

By conditional element I mean something like:

{{#if isHelperTrue}}
   <div id="conditionalDiv">Content...</div>
{{/if}}

If you want these elements to animate on show/hide, simply use another mechanism to hide them. One candidate could be style="position:absolute;width:0;height:0". This can be a problematic if you hide MANY elements and clutter the DOM.

This should be it. I’ll try to post a working example soon. Contact me if you want to see it in action before that.

Written by xyand

July 25, 2013 at 3:43 pm

Posted in css, Meteor

Tagged with , , ,

Follow

Get every new post delivered to your Inbox.