If you want to generate an image to identify multiple objects, here is the right place you come.
This is the identity image without overly.
This is the identity image with overlay.
In a project, I want to generate an image to identify my multiple USB devices in C++ with Qt. Thanks to Qt in which there are a lot of pre-created classes for us to use, for example, QImage, QPixmap, QPainter, QString, QCryptographicHash, etc. which can be used in my project, I can fulfill my task easily.
Rationale
First of all, let me simply explain the rationale of the creation.
- Generate a hash number from the specified string. MD5 is a good choice to do so. Certainly, other methods can also be used, such as SM3, MD4, Sha224, Sha256, Sha384, Sha512, etc. Even you can create a hash function by yourself. I was using MD5, because MD5 is the best function we can get easily.
- Dissolve the generated hash number and reorganize the data we are going to use. 4 bits are used for color, 4 bits for size, 4 bits for x, 4 bits for y. To implement the dissolving the data, structure and union can be used. For detail, see the source code below.
- To calculate the background color, add all bytes of the hash number. Only the least 4 bits of the sum are use to choose the background color. The 16 background colors can be customized in file IDImage.h.
- Draw a solid square with the size and color at the point of (x, y) for each word (2 bytes) of the hash umber. Maybe you want to get some information beneath the square. If so, the solid square must be transparent, you can specify the alpha channel with setAlpha() function. Normally, 128 might be a propriate value.
- Repeat step 4 until all the data are processed.
IDImage.h
#pragma once
#include <qimage.h>
#include <qpixmap.h>
#include <qpainter.h>
#include <qstring.h>
#include <qcryptographichash.h>
#pragma pack(1)
typedef union U {
struct {
unsigned char b0;
unsigned char b1;
} b;
struct {
unsigned char color : 4;
unsigned char size : 4;
unsigned char x : 4;
unsigned char y : 4;
} p;
} UMark;
#pragma pack()
class IDImage : QObject
{
Q_OBJECT
public:
IDImage(int w = 64, int h = 64);
~IDImage();
QPixmap* createImage(QString s);
QPixmap* getImage();
bool saveImage(QString path = NULL);
private:
QColor getBackgroundColor(QByteArray b);
QPixmap* m_image;
int m_w, m_h;
QVector<QColor> g_bcolor{
QColor::fromRgb(0, 0, 139),//darkblue rgb(0, 0, 139)
QColor::fromRgb(0, 139, 139),//darkcyan rgb(0, 139, 139)
QColor::fromRgb(184, 134, 11),//darkgoldenrod rgb(184, 134, 11)
QColor::fromRgb(0, 100, 0),//darkgreen rgb(0, 100, 0)
QColor::fromRgb(189, 183, 107),//darkkhaki rgb(189, 183, 107)
QColor::fromRgb(139, 0, 139),//darkmagenta rgb(139, 0, 139)
QColor::fromRgb(85, 107, 47),//darkolivegreen rgb(85, 107, 47)
QColor::fromRgb(255, 140, 0),//darkorange rgb(255, 140, 0)
QColor::fromRgb(153, 50, 204),//darkorchid rgb(153, 50, 204)
QColor::fromRgb(139, 0, 0),//darkred rgb(139, 0, 0)
QColor::fromRgb(233, 150, 122),//darksalmon rgb(233, 150, 122)
QColor::fromRgb(143, 188, 143),//darkseagreen rgb(143, 188, 143)
QColor::fromRgb(72, 61, 139),//darkslateblue rgb(72, 61, 139)
QColor::fromRgb(0, 206, 209),//darkturquoise rgb(0, 206, 209)
QColor::fromRgb(148, 0, 211),//darkviolet rgb(148, 0, 211)
QColor::fromRgb(34, 139, 34)//forestgreen rgb(34, 139, 34)
};
QVector<QColor> g_fcolor{
QColor::fromRgb(255, 192, 203),//pink rgb(255, 192, 203)
QColor::fromRgb(221, 160, 221),//plum rgb(221, 160, 221)
QColor::fromRgb(176, 224, 230),//powderblue rgb(176, 224, 230)
QColor::fromRgb(128, 0, 128),//purple rgb(128, 0, 128)
QColor::fromRgb(255, 0, 51),//
QColor::fromRgb(188, 143, 143),//rosybrown rgb(188, 143, 143)
QColor::fromRgb(65, 105, 225),//royalblue rgb(65, 105, 225)
QColor::fromRgb(139, 69, 19),//saddlebrown rgb(139, 69, 19)
QColor::fromRgb(250, 128, 114),//salmon rgb(250, 128, 114)
QColor::fromRgb(244, 164, 96),//sandybrown rgb(244, 164, 96)
QColor::fromRgb(46, 139, 87),//seagreen rgb(46, 139, 87)
QColor::fromRgb(255, 245, 238),//seashell rgb(255, 245, 238)
QColor::fromRgb(160, 82, 45),//sienna rgb(160, 82, 45)
QColor::fromRgb(153, 153, 51),//
QColor::fromRgb(135, 206, 235),//skyblue rgb(135, 206, 235)
QColor::fromRgb(106, 90, 205) //slateblue rgb(106, 90, 205)
};
};
IDImage.cpp
#include "stdafx.h"
#include "IDImage.h"
IDImage::IDImage(int w /*= 64*/, int h /*= 64*/)
{
m_image = new QPixmap(w, h);
m_w = w;
m_h = h;
}
IDImage::~IDImage()
{
if (m_image != nullptr) {
delete m_image;
}
}
QPixmap* IDImage::createImage(QString s)
{
QByteArray hash = QCryptographicHash::hash(s.toUtf8(), QCryptographicHash::Md5);
if (m_image != nullptr) {
QPainter paint(m_image);
paint.fillRect(0, 0, m_w, m_h, getBackgroundColor(hash));
for (int i = 0; i < hash.size(); i += 2) {
UMark data;
data.b.b0 = hash.at(i);
data.b.b1 = hash.at(i + 1);
QColor color = g_fcolor.at(data.p.color);
color.setAlpha(128);
int max_size = (m_w < m_h) ? m_w : m_h;
int size = (data.p.size + 1) * max_size / 33;
int x = data.p.x * m_w / 16 - size;
int y = data.p.y * m_h / 16 - size;
paint.fillRect(x, y, 2 * size, 2 * size, color);
}
//If you want to overlay the image with some interesting image,
//please specify the overly and uncomment the lines below.
//QImage hard_key(":/MainFrame/Resources/common/hard_key.png");
//hard_key.scaled(QSize(m_w, m_h));
//paint.drawImage(QPoint(0, 0), hard_key);
}
return m_image;
}
QPixmap * IDImage::getImage()
{
return m_image;
}
bool IDImage::saveImage(QString path)
{
return m_image->save(path, "PNG");
}
QColor IDImage::getBackgroundColor(QByteArray b)
{
int t = 0;
for (int i = 0; i < b.size(); i += 1) {
t += b.at(i);
}
t &= 0xf;
return g_bcolor.at(t);
}
How to use the class IDImage
#include "IDImage.h"
...
IDImage img(64, 64);
img.createImage(_here_is_the_identity_string_);
if (!img.saveImage(_path_to_the_image_)
{
//handle the error
}
Fine tune your own code
- The background colors can be customized in IDImage.h. In my opinion, the darker the better.
- The foreground colors can also be customized in IDImage.h. Again, in my opinion, the brighter the better.
- The principle to choose the background colors and foreground colors is that the bigger the distinguishability the better. Also, you may take the color series into consideration. Anyway, all depends on how you design.
- If you want to put something on top of the generated image, uncomment the lines in the source code, and make sure to provide the image with the application.
- In the source code, the input is a string. In fact, the input can be any data type, because the real data we used is the MD5 of the original input. So, you can create the overloaded functions by yourself.