Как вы сохраняете и изменяете растровое изображение / набор битов, используя MySQL? - PullRequest
4 голосов
/ 12 марта 2020

Я хочу создать столбец таблицы, в котором будет храниться 500-байтовое растровое изображение (500 bytes * 8 bits per byte = 4000 bits) и выполнять операции по изменению битов (установлен в 1 или 0) для определенных индексов в растровом изображении.

Однако страница документации для растровых изображений в основном пуста , поэтому в качестве единственного руководства я использую функции необработанных бит . Как вы создаете, считываете, считываете и изменяете растровое изображение как тип столбца в MySQL?

Используя bin и lpad, вы можете распечатать 64-разрядное число в виде двоичной строки.

LPAD(BIN(34), 64, '0')
0000000000000000000000000000000000000000000000000000000000100010

Однако как вы распечатываете двоичную / блоб / varbinary строку, которая может иметь длину 4000 битов?

(Примечание: не говорим о растровых индексах)

Ответы [ 2 ]

4 голосов
/ 18 марта 2020

Сначала обновите до MySQL 8.0. Это не будет работать в более ранних версиях MySQL.

. Вы должны использовать BINARY, VARBINARY или BLOB, в зависимости от длины битовых полей, которые вы хотите сохранить.

mysql> create table mytable ( bits binary(500) );
ERROR 1074 (42000): Column length too big for column 'bits' (max = 255); use BLOB or TEXT instead

mysql> create table mytable ( bits blob(500) );
Query OK, 0 rows affected (0.02 sec)

Используйте UNHEX () для формирования битовых полей из шестнадцатеричных строк. Слишком сложно работать с двоичными байтами напрямую.

mysql> insert into mytable set bits = unhex(repeat('00', 500));
Query OK, 1 row affected (0.00 sec)

Вы можете использовать побитовые операторы, такие как |, &, ^ и ~ с битовыми полями. Но строки должны быть одинаковой длины!

mysql> update mytable set bits = bits | b'01000';
ERROR 3513 (HY000): Binary operands of bitwise operators must be of equal length

Это неудобно, но вы должны формировать строки правильной длины, используя CONCAT ():

mysql> update mytable set bits = bits | unhex(concat(repeat('00', 498), 'ffff'));
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Чтение битовой строки ( Я сократил его для этого отображения):

mysql> select hex(bits) from mytable\G
*************************** 1. row ***************************
hex(bits): 000...000FFFF

Функция BIT_COUNT () работает:

mysql> select bit_count(bits) from mytable;
+-----------------+
| bit_count(bits) |
+-----------------+
|              16 |
+-----------------+

mysql> select bit_count(~bits) from mytable;
+------------------+
| bit_count(~bits) |
+------------------+
|             3984 |
+------------------+

К этому времени вы должны сесть и подумать, является ли SQL лучший инструмент для этой задачи, даже с функциями MySQL 8.0. Вы найдете его более гибким и мощным для выполнения побитовой работы в большинстве других языков программирования.

2 голосов
/ 21 марта 2020

Используйте BLOB как тип данных и напишите функцию, которая будет:

  • извлекать байт, который необходимо обновить
  • изменить бит в байте
  • вставить измененный байт в большой двоичный объект в исходной позиции

Вот одна реализация:

delimiter //
create function set_bit(b blob, pos int, val int) returns blob reads sql data
comment 'changes the bit at position <pos> (0: right most bit) to <val> in the blob <b>'
begin
    declare len int;      -- byte length of the blob
    declare byte_pos int; -- position of the affected byte (1: left most byte)
    declare bit_pos  int; -- position within the affected byte (0: right most bit)
    declare byte_val int; -- value of the affected byte

    set len = length(b);
    set byte_pos = len - (pos div 8);
    set bit_pos = pos mod 8;
    set byte_val = ord(substring(b, byte_pos, 1)); -- read the byte
    set byte_val = byte_val & (~(1 << bit_pos));   -- set the bit to 0
    set byte_val = byte_val | (val << bit_pos);    -- set the bit to <val>

    return insert(b, byte_pos, 1, char(byte_val)); -- replace the byte and return
end //
delimiter ;

Простой тест:

create table test(id int, b blob);
insert into test(id, b) select 1, 0x000000;
insert into test(id, b) select 2, 0xffffff;

У нас есть два битовые маски BLOB-объектов (по 3 байта в каждой) - один полный нулей и один полный единиц. В обоих случаях мы устанавливаем бит в позиции 10 (11-й бит справа) в 1, а бит в позиции 11 (12-й бит справа) в 0.

update test set b = set_bit(b, 10, 1);
update test set b = set_bit(b, 11, 0);

select id, hex(b), to_base2(b) from test;

Result :

| id  | hex(b) | to_base2(b)                |
| --- | ------ | -------------------------- |
| 1   | 000400 | 00000000 00000100 00000000 |
| 2   | FFF7FF | 11111111 11110111 11111111 |

Просмотр на БД Fiddle

Примечание: to_base2() - это пользовательская функция, которая возвращает строку с битовым представлением BLOB и используется только

Работает как для MySQL 5.x, так и для 8.0.

Возможно встроить его в одном выражении (без необходимости использования функции) - Но это довольно нечитаемо:

update test t
cross join (select 10 as pos, 1 as val) i -- input
set t.b = insert(
  t.b,
  length(t.b) - (i.pos div 8),
  1,
  char(ord(
    substring(t.b, length(t.b) - (i.pos div 8), 1))
    & ~(1 << (i.pos mod 8))
    | (i.val << (i.pos mod 8)
  ))
);

Просмотр на БД Fiddle

В MySQL 8.0 это немного проще, так как нам не нужно извлекать байт и может использовать битовые операции с BLOB-объектами. Но нам нужно убедиться, что опреранды имеют одинаковую длину:

update test t
cross join (select 10 as pos, 1 as val) i -- input
set t.b = t.b
  & (~(concat(repeat(0x00,length(t.b)-1),char(1)) << i.pos))
  | (concat(repeat(0x00,length(t.b)-1),char(i.val)) << i.pos) 

Просмотр на БД Fiddle

Другой способ:

update test t
cross join (select 10 as pos, 1 as val) i -- input
set t.b = 
  case when i.val = 1
    then t.b | concat(repeat(0x00,length(t.b)-1),char(1)) << i.pos
    else t.b & ~(concat(repeat(0x00,length(t.b)-1),char(1)) << i.pos)
  end

Просмотр на БД Fiddle

...