Backface checking fails
Ended with this. Transformed geometry to world coordinates (with bmesh.ops.transform as suggested by batFINGER) and replaced single camera direction to multiple directions between camera location and faces' centers:

import bpy
import bmesh
import numpy
from mathutils import Vector

scene = bpy.context.scene

obj_mat = bpy.context.active_object.matrix_world
cam_mat = scene.camera.matrix_world
cam_loc = Vector(([e[3] for e in cam_mat][:-1]))

bm = bmesh.new()
bm.from_object(bpy.context.active_object, scene, deform=False)
bmesh.ops.transform(bm, verts=bm.verts, matrix=obj_mat) #convert to world coordinates
id = [f.index for f in bm.faces if numpy.dot(f.normal, cam_loc-f.calc_center_median())&gt;0]

print(id)