In PostgreSQL (tested with version 9.4) you could unpivot with a VALUES expression in a LATERAL subquery. You'll need dynamic SQL.
This works for any table with any number of columns matching any pattern as long as selected columns are all numeric or all boolean. Only the value 1 (true) is counted.
Create this function once:
CREATE OR REPLACE FUNCTION f_tagcount(_tbl regclass, col_pattern text)
  RETURNS TABLE (tag text, tag_ct bigint)
  LANGUAGE plpgsql AS
$func$
BEGIN
   RETURN QUERY EXECUTE (
   SELECT
     'SELECT l.tag, count(l.val::int = 1 OR NULL)
      FROM ' || _tbl || ', LATERAL (VALUES '
       || string_agg( format('(%1$L, %1$I)', attname), ', ')
       || ') l(tag, val)
      GROUP  BY 1
      ORDER  BY 1'
   FROM   pg_catalog.pg_attribute
   WHERE  attrelid = _tbl
   AND    attname LIKE col_pattern
   AND    attnum > 0
   AND    NOT attisdropped
   );
END
$func$;
Call:
SELECT * FROM f_tagcount('tbl', 'ted%');
Result:
tag  | tag_ct
-----+-------
ted1 | 4
ted2 | 1
ted3 | 2
The 1st argument is a valid table name, possibly schema-qualified. Defense against SQL-injection is built into the data type regclass.
The 2nd argument is a LIKE pattern for the column names. Hence the wildcard %.
db<>fiddle here
Old sqlfiddle
Related: