Is Google reverse image search sexist?

A quick one and rhetorical question for you today…

I’ve become quite fascinated with https://thispersondoesnotexist.com/, which generates random real-looking faces. Most of the time. Sometimes it generates people with half glasses, messed up ears, or creepy meat bag half formed faces to the side. You just hit refresh and every time you do so, it generates a new face. (If you search for the URL, you will find articles explaining the technology behind it. Quite fascinating.)

But here’s an odd behaviour of Google’s reverse image search, after searching on two randomly generated images…

gentleman

girl

(Edit: I’ve set the images above to link to the image search so you can see for yourselves. They do actual reverse image searches using the images I uploaded to Facebook, so if ever the algorithm changes, the results you’ll see will be different.)

  • A reverse image search of a young man, who does not exist, comes back as gentleman.
  • A reverse image search of a young woman, who does not exist, comes back as girl.

Hey Google, what’s up with that?

My career seems to have gone off the rails

Every year around December or January, I upload my CV to a local careers website. It’s never even my latest CV and I have in the past included a note to say I am not on the market – but they don’t read that shit anyway. Then, normally I get a number of calls from recruiters, and they send me job specifications. This enables me to:

  1. Gauge whether or not my salary is still in the right range for my job experience.
  2. Compare the job specs to the technologies that I use at work, and get a check-point for what I should be using.

This year, for the first time, something is off.

  • Normally I get twenty or thirty calls within two days. This time I got two or three calls weeks later.
  • From the few I did receive, it’s become clear I’m no longer working on the right technologies.
  • I’m being significantly underpaid.

What to do? What to do?

There have been discussions about updating our server and services infrastructure, and the technologies to be used will put that back on track for me. If it happens.

But being underpaid is an issue. My expenses have just gone up, since I now have to have my son at aftercare every day, and have somebody help keep my flat clean. And I was already in arrears with the levy, since the body corporate where I live failed to send me an invoice for an entire year.

Meanwhile, we are short-staffed at work and unless they get more developers soon, it could get worse. In order to be able to approach my employer, I need more than a gut feel to say that I am being underpaid, but I don’t actually want to go to interviews and waste anybody’s time. I’m in a peculiar position because at the moment, I do have a decent position where I am, and I know the systems I’m working on. So I have good knowledge and am respected there. But respect doesn’t pay the bills and it’s difficult not to let my dampened spirits come across as a bad attitude. So it seems I must ask for more money and I am bad at that.

"You are a fool", says the scammer I didn’t give money to, just like the preacher who likes to quote the Bible claim that atheists are fools.

Funny how this works, eh?

I’ve wondered for some time how these Facebook Messenger scams make money for the cons. I mean, apart from the many men who claim to run orphanages in Uganda, all of whom use the same photos… I wonder about the pretty girls (and in this case not really pretty) who send friend requests, only to immediately chat to you if you accept them, and then ramp up to Facebook calling you a few days later.

What are they? Scammers, catfish, bots… Who knows? Maybe they are not all the same – I don’t know. But every now and then, I accept such a request, reply mostly one word “OK” responses to their comments, and string them along hoping to find out just how the scam works. Unfortunately, they normally bore me before ever getting to the point. Once before it reached the point where one of them actually called me, and I immediately blocked “her”. But finally yesterday one of them hit pay dirt, or so she thought.

She asked me for a voucher to upgrade her phone for reasons unclear, right after I did not answer her Facebook call. Like I am going to pay money to have the privilege of chatting to someone I don’t care to chat to, who will most likely disappear right after taking my money anyway. So I shared this image yesterday, with text similar to this paragraph.

30-07-2018 14-13-10

And last night when I checked my phone, I found her reply: “You are a fool.” Yup-yup, in that scammer’s world, someone who isn’t stupid enough to buy her bullshit is a fool. I do find it amusing, because that’s almost exactly what many theists like to attack atheists with in debates… Psalm 14:1 “The fool says in his heart, there is no god”.

Just like the theists, she has it backwards: It is wise not to fall for a scam. It is not wise to accept anyone’s word, or any written word, without evidence; even if that word was taught to you by peers/parents brainwashed before you. When those words come from a theist debater, they are paramount to, “Look, this claim that I demand you believe claims what it claims and it also claims that those who don’t believe it are unwise to do so”. Not terribly convincing because (and I shouldn’t have to spell it out), if I don’t believe your claim, then I don’t believe anything it says about people like me who don’t believe either. In fact, there isn’t much difference between that and a scammer calling me a fool because I didn’t give her my money.

Fret not, little scammer, you will find a sucker for your scheme elsewhere. Maybe you should try church groups? (I can’t link to her account since I blocked it, but you might find this particular scammer if you search for “Christina Mayda Mitchel”.)

Anyway, it amazes me that anyone falls for these scammers. In case you didn’t know, real potential friends do not start messaging you the minute you accept their requests, or they might send a general introductory message that tells you about themselves. They don’t bombard you with random personal questions as soon as you accept their requests. And decent people don’t Facebook call you, ever. I wonder if these scammers use tactics designed to find only the most stupid people, or if they are stupid themselves. I’m betting on the latter, and they get the former more by accident than design.

The most annoying kind of error message?

What is the most annoying kind of error message, from a technical perspective? Or from the perspective of an error that was reported from an end user where you are a developer or in technical support?

I’ve changed my mind over the years. It used to be “the operation completed successfully” or “catastrophic failure” but nowadays I rate those as amusing.

There’s always the dreaded NullReferenceException:

3264.4

But as a programmer, I see that more as an embarrassing error. If someone reports one of these to the company I work for, it probably means a sloppy programming error. It also happens to be the easiest error to get, when one is being sloppy. So it can be annoying. But it isn’t the worst.

Actually I have two errors that annoy the shit out of me. I can’t say which is worse, so I’ll describe both…

The “error in someone else’s code incorrectly reported to us” error

Sometimes we receive error reports from a client liaison, someone who deals with clients who integrate with our software.

To give you a hypothetical example:

  1. I wrote a piece of software, a service that takes some info about doodads that people claim to own, and then queries the World Doodad Consortium to check if the doodads are valid and really belong to those people.
  2. People then use my service, by calling it programmatically inside their own software.
  3. Then tech support sends me an email that says the doodad service is not working. The email contains a screenshot of software I have never seen, which is displaying an error message, “the doodad service is offline”.
  4. Actually number three is the paraphrased version of the error message. To see it, one must scroll through thirty pages of email messages, several replies, angry rants demanding that this should be sorted out immediately, and so on, from three weeks ago… all of which only reach me now.

The kicker: My software never, under any circumstances, ever returns an error with the text “the doodad service is offline”. Hence I do not have any way of knowing what the error means.

It might mean:

  • The World Doodad Consortium returned invalid results because there was an issue there. My service reported this as per the software manual that their implementing developer should have read.
  • Everything is working perfectly but the integrator’s code failed to parse the response and their code fell into a catch-all that displayed an irrelevant general error, because their code assumes that if anything goes wrong in this block of code, it means the doodad service must have failed.
  • Their code never called us at all, but put the request into a queue to send later, and then because of some issue, maybe they set up a new client installation and didn’t configure it correctly, the queue never gets emptied.
  • In rare circumstances, perhaps 0.1% of all errors reported, the service genuinely failed, by returning something unexpected, which crashed the integrating client software.

Of course I have to take every error report seriously, and find out at the very least if:

  1. The request actually reached the service.
  2. If it did, whether or not we returned a valid response to the client.

Of course, more than nine times out of ten the request never reached the service, and the error actually means something went wrong in their software. And in the rare cases where the requests can be found on this end, more than nine times out of ten it turns out that we returned a perfectly valid response. Hence these errors are annoying.

Of course, there are some factors to keep in mind: The person who reported the error is a client liaison. His or her job is to keep the customer happy. He or she may not have enough context to determine the error is probably on the client system. Furthermore, the person who reported the error from the client side may be a manager, who also has no context on how or why the error happened. It would be extremely unprofessional not to give all those involved the benefit of doubt and treat their errors as important. Failing to attend to them and take them seriously may involve losing an important client.

The “system is behaving as designed” error

I’ll illustrate this kind of error with a hypothetical email… Note that the subject and message body contradict one another.

Subject: User Jack Ass can not access the High Security Risk page!!!

Message: I created user Jack Ass on the Doodad system, and gave him access to the High Security Risk page.

But Jack can access the High Security Risk Page! He should not be allowed to access that page! Please investigate and advise.

I don’t think this kind of error needs any further comment.

More good code I wrote when I was high

Again I was surprised to find some good code that I wrote way back when I was high. I might post this one to my programming blog tomorrow as well, slightly repurposed without the mention of drugs, and a title “How to load a cursor from embedded resources in C#”.

Anyway, that’s what it does. Via some code that I found and then modified, my image viewer, when viewing an image that’s larger than the viewable area, allows you to use the mouse to drag around and pan to different parts of the image. When doing this, the program changes the mouse cursor, to one of two cursor images that I designed myself and drew pixel-by-pixel (I was indeed tweaking my ass off), one for a hand that’s up and one for a hand that’s down.

Here’s what it looks like when the hand down cursor is displayed. (Main mouse button is pressed so you can pan the image… in the application – not here because this is just a fucking screenshot.)

Viewer

And here’s the code (that I took ages to figure out) that loads the cursor, which is saved as a file resource, from the applications resources:

using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Windows.Forms;

namespace Romy.Core
{
    public static class CursorResourceLoader
    {
        #region Methods

        public static Cursor LoadEmbeddedCursor(byte[] cursorResource, int imageIndex = 0)
        {
            var resourceHandle = GCHandle.Alloc(cursorResource, GCHandleType.Pinned);
            var iconImage = IntPtr.Zero;
            var cursorHandle = IntPtr.Zero;

            try
            {
                var header = (IconHeader)Marshal.PtrToStructure(resourceHandle.AddrOfPinnedObject(), typeof(IconHeader));

                if (imageIndex >= header.count)
                    throw new ArgumentOutOfRangeException("imageIndex");

                var iconInfoPtr = resourceHandle.AddrOfPinnedObject() + Marshal.SizeOf(typeof(IconHeader)) + imageIndex * Marshal.SizeOf(typeof(IconInfo));
                var info = (IconInfo)Marshal.PtrToStructure(iconInfoPtr, typeof(IconInfo));

                iconImage = Marshal.AllocHGlobal(info.size + 4);
                Marshal.WriteInt16(iconImage + 0, info.hotspot_x);
                Marshal.WriteInt16(iconImage + 2, info.hotspot_y);
                Marshal.Copy(cursorResource, info.offset, iconImage + 4, info.size);

                cursorHandle = NativeMethods.CreateIconFromResource(iconImage, info.size + 4, false, 0x30000);
                return new Cursor(cursorHandle);
            }
            finally
            {
                if (cursorHandle != IntPtr.Zero)
                    NativeMethods.DestroyIcon(cursorHandle);

                if (iconImage != IntPtr.Zero)
                    Marshal.FreeHGlobal(iconImage);

                if (resourceHandle.IsAllocated)
                    resourceHandle.Free();
            }
        }

        #endregion Methods

        #region Native Methods

        [SuppressUnmanagedCodeSecurity]
        static class NativeMethods
        {
            [DllImportAttribute("user32.dll", CharSet = CharSet.Unicode)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool DestroyIcon(IntPtr hIcon);

            [DllImport("user32.dll", SetLastError = true)]
            public static extern IntPtr CreateIconFromResource(IntPtr pbIconBits, int dwResSize, bool fIcon, int dwVer);
        }

        #endregion Native Methods

        #region Native Structures

        [StructLayout(LayoutKind.Explicit, Pack = 1)]
        struct IconHeader
        {
            [FieldOffset(0)]
            public short reserved;

            [FieldOffset(2)]
            public short type;

            [FieldOffset(4)]
            public short count;
        }

        /// <summary>Union structure for icons and cursors.</summary>
        /// <remarks>For icons, field offset 4 is used for planes and field offset 6 for 
        /// bits-per-pixel, while for cursors field offset 4 is used for the x coordinate 
        /// of the hotspot, and field offset 6 is used for the y coordinate.</remarks>
        [StructLayout(LayoutKind.Explicit, Pack = 1)]
        struct IconInfo
        {
            [FieldOffset(0)]
            public byte width;

            [FieldOffset(1)]
            public byte height;

            [FieldOffset(2)]
            public byte colors;

            [FieldOffset(3)]
            public byte reserved;

            [FieldOffset(4)]
            public short planes;

            [FieldOffset(6)]
            public short bpp;

            [FieldOffset(4)]
            public short hotspot_x;

            [FieldOffset(6)]
            public short hotspot_y;

            [FieldOffset(8)]
            public int size;

            [FieldOffset(12)]
            public int offset;
        }

        #endregion Native Structures
    }
}

The code that uses it, in another class, is just this:

        /// <summary>Loads the two hand cursors from project resources.</summary>
        private static void InitializePanBoxCursors()
        {
            if (handUpCursor == null && handDownCursor == null)
            {
                handUpCursor = CursorResourceLoader.LoadEmbeddedCursor(Properties.Resources.Hand_up);
                handDownCursor = CursorResourceLoader.LoadEmbeddedCursor(Properties.Resources.Hand_down);
            }
        }

It still amazes me that I wrote some more than decent code back when I was tweaking my stupid head off.

This will be the last late night post for a while. My son gets back tomorrow, and since we share a room, I normally go to bed the same time he does. Actually I’m tired and am going to sleep right after posting this. Good night…

I’m still baffled by source code I wrote when I was on meth

Apologies to most people reading this who are not programmers. This post won’t make much sense. But I try to avoid drug references on my programming blog, so this belongs here… And also an apology to anyone who saw this post published by mistake the first time, before I added the image.

I still use this monstrosity of a program I wrote a few years ago. I shouldn’t, because it tries to do too many different things in that it was my scratchpad for all new code I learned for a few years, but it works well for some of them, so I use it even though I know it has its quirks. It’s more or less a file browser, image viewer, video and audio player (via Direct Show which is now obsolete), and can do some minor image editing. But mostly it’s an image viewer. It builds and maintains its own cache of thumbnails, which looks kind of like the large icons view in Windows Explorer. Then double-clicking those thumbnails opens the program’s built in image viewer.

Here’s what it looks like:

RomyView

Anyway, one of its quirks is that there’s a bug somewhere in the custom TaskScheduler (I think) and that can have weird effects like hanging the whole application after deleting a directory. (Sometimes.) So while looking at all the code that references my own TaskSchedulers, I came across this code, written while I was super duper high…

/// <summary>Load a Shell thumbnail image and add it to the cache.</summary>
public static async Task<ThumbnailCacheItem> LoadAndCacheThumbnailAsync(string path, CancellationToken token, bool requiresCurrentDirectory = false)
{
    var item = ThumbnailCacheItem.Invalid;
    var tcs = new TaskCompletionSource<ThumbnailCacheItem>();
    var isSmallImage = false;
    var failed = false;

    try
    {
        using (CancellationTokenSource cts = new CancellationTokenSource(),
            linked = CancellationTokenSource.CreateLinkedTokenSource(token, cts.Token))
        {
            Action checkCancelled = () =>
            {
                if (requiresCurrentDirectory && !Path.GetDirectoryName(path).EqualsIgnoreCultureAndCase(IO.CurrentDirectory))
                    linked.Cancel();

                linked.Token.ThrowIfCancellationRequested();
            };

            await Tasks.Run(() =>
            {
                checkCancelled();
                var fileSystemInfo = IO.Exists(path);

                if (!fileSystemInfo.Item1)
                {
                    /* Neither a file nor a directory exists. This is possible if it was deleted after this 
                        * method was called but before we got here, and just means we have nothing to do here. */
                    linked.Cancel();
                    linked.Token.ThrowIfCancellationRequested();
                }

                var isDirectory = fileSystemInfo.Item2;
                var isFile = fileSystemInfo.Item3;

                var fileSystemType = isDirectory ? FileSystemType.Directory : FileSystemType.File;

                var isMedia = isFile && IO.IsMediaFile(path);
                var isImage = isFile && IO.IsImageFile(path);
                var isPng = isImage && IO.IsPng(path);
                var isZip = isFile && IO.IsZip(path);
                var isPsd = isFile && IO.IsPsdFile(path);
                var isTextFile = IO.IsTextFile(path);

                Bitmap cacheBitmap = null;
                Bitmap fullsizeImage = null;
                try
                {
                    // For simplicity, we define a small bitmap as one with file size less than 1MB.
                    var isBitmapGlyph = IO.IsBmp(path) && new FileInfo(path).Length < 1024L * 1024L;

                    /* Don't use the shell for icon images. It will
                        * display them with shadows and looks like shit. */
                    if (isFile && IO.IsIcon(path))
                    {
                        // Load the largest icon.
                        using (var iconImage = (new IconFileReader { Filename = path }.FirstIcon).ToBitmap())
                        {
                            if (iconImage == null)
                                throw new FileReadException(string.Format("Error reading '{0}'", path));

                            fullsizeImage = iconImage.Clone() as Bitmap;
                            isSmallImage = true;
                        }
                    }
                    else if (isPng || isBitmapGlyph || isPsd)
                    {
                        /* Also don't use the shell for png images, or gifs, for similar reasons as for icons. */
                        fullsizeImage = ImageFile.OpenBitmap(path);

                        if (isBitmapGlyph)
                            isSmallImage = true;

                        if (fullsizeImage == null)
                            throw new FileReadException(string.Format("Error reading '{0}'", path));
                    }
                    checkCancelled();

                    isSmallImage |= IO.IsGif(path);

                    if (fullsizeImage != null && (isSmallImage || isPng || isPsd))
                    {
                        /* If this is a small image, or if it is likely that conversion to jpeg will
                            * produce a thumbnail of inferior quality, we store the actual image in the
                            * cache, instead of a thumbnail.
                            *
                            * Also, the method to get the thumbnail from the shell below will lose the
                            * transparency for small png images, whereas this way, we will display them
                            * with transparency, even in the thumbnails. */
                        cacheBitmap = new Bitmap(fullsizeImage);

                        if (cacheBitmap.Width > 256 || cacheBitmap.Height > 256)
                            cacheBitmap = cacheBitmap.Resize(256, 256) as Bitmap;
                    }
                    else
                    {
                        cacheBitmap = !isZip ? ShellItemImageFactory.GetImage(path) : Icon.ExtractAssociatedIcon(path).ToBitmap();

                        if (cacheBitmap == null)
                            throw new FileReadException(string.Format("Error reading '{0}'", path));
                    }

                    checkCancelled();

                    if (cacheBitmap != null)
                        item = ThumbnailCache.AddNewCacheItem(path, cacheBitmap, fileSystemType, isSmallImage);
                }
                catch (Exception ex)
                {
                    /* An exception here is not necesarily an error. e.g. It will throw 
                        * if you deliberately delete images as they're being cached. */
                    ex.Log();
                    failed = tcs.TrySetException(ex);
                }
                finally
                {
                    if (fullsizeImage != null)
                        fullsizeImage.Dispose();
                }
                return Task.FromResult(item);
            }, token, TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness).ConfigureAwait(false);

            /* The TaskCreationOptions are critically important here.
                * We do not want to be running on the main thread. */

            if (!failed)
                tcs.TrySetResult(item);
        }
    }
    catch (OperationCanceledException) { tcs.TrySetCanceled(); }

    // Prevent the UI from becoming unresponsive when adding many thumbnails.
    await tcs.Task.YieldTo(uiScheduler);

    return await tcs.Task;
}

That’s the code that adds a new thumbnail object to the thumbnail cache, which is a special kind of object cache where the objects are reused once the configurable size is exceeded, and is also persisted to disk, in my own bastardized binary format.

This code showed up in my search results because I was looking for all references to Tasks.Run, which is not standard code, but that calls my own TaskScheduler instead of the built in one.

And yet, this is good code. It’s not verbose, which is surprising. The code is easy to read and it calls out to loads of extension methods and other complex code. So depending on the file type that it finds, it might decide to cache the actual image directly for small files, use my custom icon reader that reads all icons from an ICO file directly, use my custom image reader that can read all kinds of image files, including PSD (Photoshop files), or invoke the Windows shell interfaces, in code that I also managed to figure out myself (because that shit is not implemented in the dot net Framework), which then uses different interfaces depending on the version of Windows. All in all, this is really good code… better than what I write at work nowadays.

I’m baffled.


Also, I found that the bug has nothing to do with my custom TaskScheduler, because it still happens after all references to it are removed. I’ve given up looking for the bug for now.